tcp: Initial support for RFC5925 auth option
Subsystems (6)
v3 cover tcp: Initial support for RFC5925 auth option
This is similar to TCP MD5 in functionality but it's sufficiently different that wire formats are incompatible. Compared to TCP-MD5 more algorithms are supported and multiple keys can be used on the same connection but there is still no negotiation mechanism. Expected use-case is protecting long-duration BGP/LDP connections between routers using pre-shared keys.
This version is mostly functional, it incorporates ABI feedback from previous versions and adds tests to kselftests. More discussion and testing is required and obvious optimizations were skipped in favor of adding functionality. Here are several flaws:
* RST and TIMEWAIT are mostly unhandled
* Locking is lockdep-clean but need to be revised
* Sequence Number Extension not implemented
* User is responsible for ensuring keys do not overlap
* Traffic key is not cached (reducing performance)
Not all ABI suggestions were incorporated, they can be discussed further. However I very much want to avoid supporting algorithms beyond RFC5926.
Test suite was added to tools/selftests/tcp_authopt. Tests are written in python using pytest and scapy and check the API in some detail and validate packet captures. Python code is already in linux and in kselftests but virtualenvs not very much. This test suite uses tox to create a private virtualenv and hide dependencies. Let me know if this is OK or how it can be improved.
Limited testing support is also included in nettest and fcnal-test.sh, those tests are slow and cover much less.
Changes for frr: https://github.com/FRRouting/frr/pull/9442
That PR was made early for ABI feedback, it has many issues.
Changes for yabgp: https://github.com/cdleonard/yabgp/commits/tcp_authopt
The patched version of yabgp can establish a BGP session protected by TCP Authentication Option with a Cisco IOS-XR router. It old now.
Changes since RFCv2:
* Removed local_id from ABI and match on send_id/recv_id/addr
* Add all relevant out-of-tree tests to tools/testing/selftests
* Return an error instead of ignoring unknown flags, hopefully this makes it easier to extend.
* Check sk_family before __tcp_authopt_info_get_or_create in tcp_set_authopt_key
* Use sock_owned_by_me instead of WARN_ON(!lockdep_sock_is_held(sk))
* Fix some intermediate build failures reported by kbuild robot
* Improve documentation
Changes since RFC:
* Split into per-topic commits for ease of review. The intermediate commits compile with a few "unused function" warnings and don't do anything useful by themselves.
* Add ABI documention including kernel-doc on uapi
* Fix lockdep warnings from crypto by creating pools with one shash for each cpu
* Accept short options to setsockopt by padding with zeros; this approach allows increasing the size of the structs in the future.
* Support for aes-128-cmac-96
* Support for binding addresses to keys in a way similar to old tcp_md5
* Add support for retrieving received keyid/rnextkeyid and controling the keyid/rnextkeyid being sent.
Leonard Crestez (15):
tcp: authopt: Initial support and key management
docs: Add user documentation for tcp_authopt
selftests: Initial tcp_authopt test module
selftests: tcp_authopt: Initial sockopt manipulation
tcp: authopt: Add crypto initialization
tcp: authopt: Compute packet signatures
tcp: authopt: Hook into tcp core
tcp: authopt: Add snmp counters
selftests: tcp_authopt: Test key address binding
selftests: tcp_authopt: Capture and verify packets
selftests: Initial tcp_authopt support for nettest
selftests: Initial tcp_authopt support for fcnal-test
selftests: Add -t tcp_authopt option for fcnal-test.sh
tcp: authopt: Add key selection controls
selftests: tcp_authopt: Add tests for rollover
Documentation/networking/index.rst | 1 + Documentation/networking/tcp_authopt.rst | 69 + include/linux/tcp.h | 6 + include/net/tcp.h | 1 + include/net/tcp_authopt.h | 134 ++ include/uapi/linux/snmp.h | 1 + include/uapi/linux/tcp.h | 110 ++ net/ipv4/Kconfig | 14 + net/ipv4/Makefile | 1 + net/ipv4/proc.c | 1 + net/ipv4/tcp.c | 27 + net/ipv4/tcp_authopt.c | 1168 +++++++++++++++++ net/ipv4/tcp_input.c | 17 + net/ipv4/tcp_ipv4.c | 5 + net/ipv4/tcp_minisocks.c | 2 + net/ipv4/tcp_output.c | 74 +- net/ipv6/tcp_ipv6.c | 4 + tools/testing/selftests/net/fcnal-test.sh | 34 + tools/testing/selftests/net/nettest.c | 34 +- tools/testing/selftests/tcp_authopt/Makefile | 5 + .../testing/selftests/tcp_authopt/README.rst | 15 + tools/testing/selftests/tcp_authopt/config | 6 + tools/testing/selftests/tcp_authopt/run.sh | 11 + tools/testing/selftests/tcp_authopt/setup.cfg | 17 + tools/testing/selftests/tcp_authopt/setup.py | 5 + .../tcp_authopt/tcp_authopt_test/__init__.py | 0 .../tcp_authopt/tcp_authopt_test/conftest.py | 21 + .../full_tcp_sniff_session.py | 53 + .../tcp_authopt_test/linux_tcp_authopt.py | 198 +++ .../tcp_authopt_test/netns_fixture.py | 63 + .../tcp_authopt/tcp_authopt_test/server.py | 82 ++ .../tcp_authopt/tcp_authopt_test/sockaddr.py | 101 ++ .../tcp_authopt_test/tcp_authopt_alg.py | 276 ++++ .../tcp_authopt/tcp_authopt_test/test_bind.py | 143 ++ .../tcp_authopt_test/test_rollover.py | 181 +++ .../tcp_authopt_test/test_sockopt.py | 74 ++ .../tcp_authopt_test/test_vectors.py | 359 +++++ .../tcp_authopt_test/test_verify_capture.py | 123 ++ .../tcp_authopt/tcp_authopt_test/utils.py | 154 +++ .../tcp_authopt/tcp_authopt_test/validator.py | 158 +++ 40 files changed, 3746 insertions(+), 2 deletions(-) create mode 100644 Documentation/networking/tcp_authopt.rst create mode 100644 include/net/tcp_authopt.h create mode 100644 net/ipv4/tcp_authopt.c create mode 100644 tools/testing/selftests/tcp_authopt/Makefile create mode 100644 tools/testing/selftests/tcp_authopt/README.rst create mode 100644 tools/testing/selftests/tcp_authopt/config create mode 100755 tools/testing/selftests/tcp_authopt/run.sh create mode 100644 tools/testing/selftests/tcp_authopt/setup.cfg create mode 100644 tools/testing/selftests/tcp_authopt/setup.py create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/__init__.py create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/conftest.py create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/full_tcp_sniff_session.py create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/linux_tcp_authopt.py create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/netns_fixture.py create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/server.py create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/sockaddr.py create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/tcp_authopt_alg.py create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/test_bind.py create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/test_rollover.py create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/test_sockopt.py create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/test_vectors.py create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/test_verify_capture.py create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/utils.py create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/validator.py
base-commit: 3a62c333497b164868fdcd241842a1dd4e331825
Signature · 1 line
2.25.1
RFC v3 1/15 tcp: authopt: Initial support and key management
This commit adds support to add and remove keys but does not use them further.
Similar to tcp md5 a single pointer to a struct tcp_authopt_info* struct is added to struct tcp_sock, this avoids increasing memory usage. The data structures related to tcp_authopt are initialized on setsockopt and only freed on socket close.
--- include/linux/tcp.h | 6 + include/net/tcp.h | 1 + include/net/tcp_authopt.h | 65 +++++++++++ include/uapi/linux/tcp.h | 79 ++++++++++++++ net/ipv4/Kconfig | 14 +++ net/ipv4/Makefile | 1 + net/ipv4/tcp.c | 27 +++++ net/ipv4/tcp_authopt.c | 223 ++++++++++++++++++++++++++++++++++++++ net/ipv4/tcp_ipv4.c | 2 + 9 files changed, 418 insertions(+) create mode 100644 include/net/tcp_authopt.h create mode 100644 net/ipv4/tcp_authopt.c
include/linux/tcp.h +6 -0
@@ -140,10 +140,12 @@ struct tcp_request_sock {static inline struct tcp_request_sock *tcp_rsk (const struct request_sock *req) {return (struct tcp_request_sock *)req; } +struct tcp_authopt_info ; +struct tcp_sock {/* inet_connection_sock has to be the first member of tcp_sock */ struct inet_connection_sock inet_conn;u16 tcp_header_len;/* Bytes of tcp header to send */ u16 gso_segs;/* Max number of segs per GSO packet */
@@ -403,10 +405,14 @@ struct tcp_sock {/* TCP MD5 Signature Option information */ struct tcp_md5sig_info __rcu *md5sig_info;#endif +#ifdef CONFIG_TCP_AUTHOPT +struct tcp_authopt_info __rcu *authopt_info; +#endif +/* TCP fastopen related information */ struct tcp_fastopen_request *fastopen_req;/* fastopen_rsk points to request_sock that resulted in this big * socket. Used to retransmit SYNACKs etc. */
include/net/tcp.h +1 -0
@@ -182,10 +182,11 @@ void tcp_time_wait(struct sock *sk, int state, int timeo);#define TCPOPT_WINDOW 3/* Window scaling */ #define TCPOPT_SACK_PERM 4/* SACK Permitted */ #define TCPOPT_SACK 5/* SACK Block */ #define TCPOPT_TIMESTAMP 8/* Better RTT estimations/PAWS */ #define TCPOPT_MD5SIG 19/* MD5 Signature (RFC2385) */ +#define TCPOPT_AUTHOPT 29/* Auth Option (RFC5925) */ #define TCPOPT_MPTCP 30/* Multipath TCP (RFC6824) */ #define TCPOPT_FASTOPEN 34/* Fast open (RFC7413) */ #define TCPOPT_EXP 254/* Experimental */ /* Magic number to be after the option value for sharing TCP * experimental options. See draft-ietf-tcpm-experimental-options-00.txt
include/net/tcp_authopt.h new file +65 -0
65 lines not on this page · raw message
include/uapi/linux/tcp.h +79 -0
99 lines not on this page · raw message
net/ipv4/Kconfig +14 -0
@@ -740,5 +740,19 @@ config TCP_MD5SIG RFC2385 specifies a method of giving MD5 protection to TCP sessions. Its main (only?) use is to protect BGP sessions between core routers on the Internet. If unsure, say N. + +config TCP_AUTHOPT + bool "TCP: Authentication Option support (RFC5925)" + select CRYPTO + select CRYPTO_SHA1 + select CRYPTO_HMAC + select CRYPTO_AES + select CRYPTO_CMAC + help + RFC5925 specifies a new method of giving protection to TCP sessions. + Its intended use is to protect BGP sessions between core routers + on the Internet. It obsoletes TCP MD5 (RFC2385) but is incompatible. + + If unsure, say N.
net/ipv4/Makefile +1 -0
@@ -59,10 +59,11 @@ obj-$(CONFIG_TCP_CONG_NV) += tcp_nv.o obj-$(CONFIG_TCP_CONG_VENO) += tcp_veno.o obj-$(CONFIG_TCP_CONG_SCALABLE) += tcp_scalable.o obj-$(CONFIG_TCP_CONG_LP) += tcp_lp.o obj-$(CONFIG_TCP_CONG_YEAH) += tcp_yeah.o obj-$(CONFIG_TCP_CONG_ILLINOIS) += tcp_illinois.o +obj-$(CONFIG_TCP_AUTHOPT) += tcp_authopt.o obj-$(CONFIG_NET_SOCK_MSG) += tcp_bpf.o obj-$(CONFIG_BPF_SYSCALL) += udp_bpf.o obj-$(CONFIG_NETLABEL) += cipso_ipv4.o obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \
net/ipv4/tcp.c +27 -0
@@ -271,10 +271,11 @@#include <net/icmp.h> #include <net/inet_common.h> #include <net/tcp.h> #include <net/mptcp.h> +#include <net/tcp_authopt.h> #include <net/xfrm.h> #include <net/ip.h> #include <net/sock.h> #include <linux/uaccess.h>
@@ -3573,10 +3574,16 @@ static int do_tcp_setsockopt(struct sock *sk, int level, int optname,case TCP_MD5SIG :case TCP_MD5SIG_EXT : err = tp->af_specific->md5_parse (sk, optname, optval, optlen);break ;#endif +case TCP_AUTHOPT : + err =tcp_set_authopt (sk, optval, optlen); +break ; +case TCP_AUTHOPT_KEY : + err =tcp_set_authopt_key (sk, optval, optlen); +break ;case TCP_USER_TIMEOUT :/* Cap the max time in ms TCP will retry or probe the window * before giving up and aborting (ETIMEDOUT) a connection. */ if (val <0 )
@@ -4219,10 +4226,30 @@ static int do_tcp_getsockopt(struct sock *sk, int level,if (!err &©_to_user (optval, &zc, len)) err = -EFAULT ;return err; }#endif +#ifdef CONFIG_TCP_AUTHOPT +case TCP_AUTHOPT : { +struct tcp_authopt info; + +if (get_user (len, optlen)) +return -EFAULT ; + +lock_sock (sk); +tcp_get_authopt_val (sk, &info); +release_sock (sk); + + len = min_t(unsigned int, len, sizeof(info)); +if (put_user (len, optlen)) +return -EFAULT ; +if (copy_to_user (optval, &info, len)) +return -EFAULT ; +return 0 ; + } +#endif +default :return -ENOPROTOOPT ; }if (put_user (len, optlen))
net/ipv4/tcp_authopt.c new file +223 -0
223 lines not on this page · raw message
net/ipv4/tcp_ipv4.c +2 -0
@@ -60,10 +60,11 @@#include <net/net_namespace.h> #include <net/icmp.h> #include <net/inet_hashtables.h> #include <net/tcp.h> +#include <net/tcp_authopt.h> #include <net/transp_v6.h> #include <net/ipv6.h> #include <net/inet_common.h> #include <net/timewait_sock.h> #include <net/xfrm.h>
@@ -2256,10 +2257,11 @@ void tcp_v4_destroy_sock(struct sock *sk)tcp_clear_md5_list (sk);kfree_rcu (rcu_dereference_protected (tp->md5sig_info,1 ), rcu); tp->md5sig_info = NULL; }#endif +tcp_authopt_clear (sk);/* Clean up a referenced TCP bind bucket. */ if (inet_csk (sk)->icsk_bind_hash)inet_put_port (sk);
Signature · 1 line
2.25.1
Re: [RFCv3 01/15] tcp: authopt: Initial support and key management
Hi Leonard,
On 8/24/21 10:34 PM, Leonard Crestez wrote:
[..]
Quoted 10 lines
include/net/tcp_authopt.h new file +7
-0@@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _LINUX_TCP_AUTHOPT_H +#define _LINUX_TCP_AUTHOPT_H + +#include <uapi/linux/tcp.h> + +/**
include/net/tcp_authopt.h line 17
Quoted 10 lines
include/net/tcp_authopt.h new file +10
-0+* struct tcp_authopt_key_info - Representation of a Master Key Tuple as per RFC5925 +* +* Key structure lifetime is only protected by RCU so readers needs to hold a +* single rcu_read_lock until they're done with the key. +*/ +struct tcp_authopt_key_info { +struct hlist_node node; +struct rcu_head rcu; +/* Local identifier */ +u32 local_id;
It's unused now, can be removed.
[..]
include/net/tcp_authopt.h line 22
Quoted 5 lines
include/net/tcp_authopt.h new file +5
-0+ +/** +* enum tcp_authopt_key_flag - flags for `tcp_authopt.flags` +* +* @TCP_AUTHOPT_KEY_DEL: Delete the key by local_id and ignore all other fields.
^
By send_id and recv_id.
Also, tcp_authopt_key_match_exact() seems to check TCP_AUTHOPT_KEY_ADDR_BIND. I wounder if that makes sense to relax it in case of TCP_AUTHOPT_KEY_DEL to match only send_id/recv_id if addr isn't specified (no hard feelings about it, though).
[..]
Quoted 7 lines
include/net/tcp_authopt.h new file +7
-0+#ifdef CONFIG_TCP_AUTHOPT +case TCP_AUTHOPT : { +struct tcp_authopt info; + +if (get_user (len, optlen)) +return -EFAULT ; +
include/net/tcp_authopt.h line 39
Quoted 10 lines
include/net/tcp_authopt.h new file +10
-0+lock_sock (sk); +tcp_get_authopt_val (sk, &info); +release_sock (sk); + + len = min_t(unsigned int, len, sizeof(info)); +if (put_user (len, optlen)) +return -EFAULT ; +if (copy_to_user (optval, &info, len)) +return -EFAULT ; +return 0 ;
Failed tcp_get_authopt_val() lookup in:
: if (!info)
: return -EINVAL;
will leak uninitialized kernel memory from stack.
ASLR guys defeated.
[..]
include/net/tcp_authopt.h line 52
Quoted 13 lines
include/net/tcp_authopt.h new file +13
-0+#define TCP_AUTHOPT_KNOWN_FLAGS ( \ + TCP_AUTHOPT_FLAG_REJECT_UNEXPECTED) + +int tcp_set_authopt (struct sock *sk,sockptr_t optval,unsigned int optlen) +{ +struct tcp_authopt opt; +struct tcp_authopt_info *info; + +sock_owned_by_me (sk); + +/* If userspace optlen is too short fill the rest with zeros */ +if (optlen >sizeof (opt)) +return -EINVAL ;
More like
: if (unlikely(len > sizeof(opt))) {
: err = check_zeroed_user(optval + sizeof(opt),
: len - sizeof(opt));
: if (err < 1)
: return err == 0 ? -EINVAL : err;
: len = sizeof(opt);
: if (put_user(len, optlen))
: return -EFAULT;
: }
Quoted 15 lines
include/net/tcp_authopt.h new file +15
-0+memset (&opt,0 ,sizeof (opt)); +if (copy_from_sockptr (&opt, optval, optlen)) +return -EFAULT ; + +if (opt.flags & ~TCP_AUTHOPT_KNOWN_FLAGS ) +return -EINVAL ; + + info =__tcp_authopt_info_get_or_create (sk); +if (IS_ERR (info)) +return PTR_ERR (info); + + info->flags = opt.flags &TCP_AUTHOPT_KNOWN_FLAGS ; + +return 0 ; +}
[..]
include/net/tcp_authopt.h
Quoted 11 lines
include/net/tcp_authopt.h new file +11
-0+int tcp_set_authopt_key (struct sock *sk,sockptr_t optval,unsigned int optlen) +{ +struct tcp_authopt_key opt; +struct tcp_authopt_info *info; +struct tcp_authopt_key_info *key_info; + +sock_owned_by_me (sk); + +/* If userspace optlen is too short fill the rest with zeros */ +if (optlen >sizeof (opt)) +return -EINVAL ;
Ditto
Quoted 9 lines
include/net/tcp_authopt.h new file +9
-0+memset (&opt,0 ,sizeof (opt)); +if (copy_from_sockptr (&opt, optval, optlen)) +return -EFAULT ; + +if (opt.flags & ~TCP_AUTHOPT_KEY_KNOWN_FLAGS ) +return -EINVAL ; + +if (opt.keylen >TCP_AUTHOPT_MAXKEYLEN ) +return -EINVAL ;
include/net/tcp_authopt.h
Quoted 10 lines
include/net/tcp_authopt.h new file +10
-0+ +/* Delete is a special case: */ +if (opt.flags &TCP_AUTHOPT_KEY_DEL ) { + info =rcu_dereference_check (tcp_sk (sk)->authopt_info,lockdep_sock_is_held (sk)); +if (!info) +return -ENOENT ; + key_info =tcp_authopt_key_lookup_exact (sk, info, &opt); +if (!key_info) +return -ENOENT ; +tcp_authopt_key_del (sk, info, key_info);
Doesn't seem to be safe together with tcp_authopt_select_key(). A key can be in use at this moment - you have to add checks for it.
Quoted 13 lines
include/net/tcp_authopt.h new file +13
-0+return 0 ; + } + +/* check key family */ +if (opt.flags &TCP_AUTHOPT_KEY_ADDR_BIND ) { +if (sk->sk_family != opt.addr.ss_family) +return -EINVAL ; + } + +/* Initialize tcp_authopt_info if not already set */ + info =__tcp_authopt_info_get_or_create (sk); +if (IS_ERR (info)) +return PTR_ERR (info);
include/net/tcp_authopt.h
Quoted 10 lines
include/net/tcp_authopt.h new file +10
-0+ +/* If an old key exists with exact ID then remove and replace. +* RCU-protected readers might observe both and pick any. +*/ + key_info =tcp_authopt_key_lookup_exact (sk, info, &opt); +if (key_info) +tcp_authopt_key_del (sk, info, key_info); + key_info =sock_kmalloc (sk,sizeof (*key_info),GFP_KERNEL | __GFP_ZERO); +if (!key_info) +return -ENOMEM ;
So, you may end up without any key.
Also, replacing a key is not at all safe: you may receive old segments which you in turn will discard and reset the connection.
I think the limitation RFC puts on removing keys in use and replacing existing keys are actually reasonable. Probably, it'd be better to enforce "key in use => desired key is different (or key_outdated flag) => key not in use => key may be removed" life-cycle of MKT.
Thanks,
Dmitry
Re: [RFCv3 01/15] tcp: authopt: Initial support and key management
Quoted 15 lines · Dmitry Safonov
Hi Leonard,
include/net/tcp_authopt.h line 17
Quoted 11 lines · Leonard Crestez
include/net/tcp_authopt.h new file +11
-0+/** +* struct tcp_authopt_key_info - Representation of a Master Key Tuple as per RFC5925 +* +* Key structure lifetime is only protected by RCU so readers needs to hold a +* single rcu_read_lock until they're done with the key. +*/ +struct tcp_authopt_key_info { +struct hlist_node node; +struct rcu_head rcu; +/* Local identifier */ +u32 local_id;It's unused now, can be removed.
Yes
Quoted 6 lines
Quoted 4 lines
+/**
+ * enumtcp_authopt_key_flag- flags fortcp_authopt.flags
+ *
+ * @TCP_AUTHOPT_KEY_DEL: Delete the key bylocal_idand ignore all other fields.^By
send_idandrecv_id.
Yes. The identifying fields are documented on struct tcp_authopt_key so I will abbreviate this.
Quoted 4 lines
Also,
tcp_authopt_key_match_exact()seems to checkTCP_AUTHOPT_KEY_ADDR_BIND. I wounder if that makes sense to relax it in case ofTCP_AUTHOPT_KEY_DELto match only send_id/recv_id if addr isn't specified (no hard feelings about it, though).
Same send_id/recv_id can overlap between different remote peers.
Quoted 23 lines
[..]
Quoted 7 lines
net/ipv4/tcp.c +7
-0+#ifdef CONFIG_TCP_AUTHOPT +case TCP_AUTHOPT : { +struct tcp_authopt info; + +if (get_user (len, optlen)) +return -EFAULT ; +net/ipv4/tcp.c static int do_tcp_getsockopt(struct sock *sk, int level, · line 4247
Quoted 10 lines
net/ipv4/tcp.c +10
-0+lock_sock (sk); +tcp_get_authopt_val (sk, &info); +release_sock (sk); + + len = min_t(unsigned int, len, sizeof(info)); +if (put_user (len, optlen)) +return -EFAULT ; +if (copy_to_user (optval, &info, len)) +return -EFAULT ; +return 0 ;Failed
tcp_get_authopt_val()lookup in:
: if (!info)
: return-EINVAL;
Quoted 2 lines
will leak uninitialized kernel memory from stack.
ASLR guys defeated.
tcp_get_authopt_val clears *info before all checks so this will return zeros to userspace.
I do need to propagate the return value from tcp_get_authopt_val.
Quoted 16 lines
net/ipv4/tcp_authopt.c line 89
Quoted 13 lines
net/ipv4/tcp_authopt.c new file +13
-0+#define TCP_AUTHOPT_KNOWN_FLAGS ( \ + TCP_AUTHOPT_FLAG_REJECT_UNEXPECTED) + +int tcp_set_authopt (struct sock *sk,sockptr_t optval,unsigned int optlen) +{ +struct tcp_authopt opt; +struct tcp_authopt_info *info; + +sock_owned_by_me (sk); + +/* If userspace optlen is too short fill the rest with zeros */ +if (optlen >sizeof (opt)) +return -EINVAL ;More like
: if (unlikely(len > sizeof(opt))) {
Quoted 8 lines
: err = check_zeroed_user(optval + sizeof(opt),
: len - sizeof(opt));
: if (err < 1)
: return err == 0 ?-EINVAL: err;
: len = sizeof(opt);
: if (put_user(len, optlen))
: return-EFAULT;
: }
If (optlen > sizeof(opt)) means userspace is attempting to use newer ABI. Current behavior is to return an error which seems very reasonable.
You seem to be suggesting that we check that the rest of option is zeroes and if it is to continue. That seems potentially dangerous but it could work if we forever ensure that zeroes always mean "no effect".
This would make it easier for new apps to run on old kernels: unless they specifically use new features they don't need to do anything.
Also, setsockopt can't report a new length back and there's no getsockopt for keys.
Quoted 6 lines
net/ipv4/tcp_authopt.c line 95
Quoted 6 lines
net/ipv4/tcp_authopt.c new file +6
-0+memset (&opt,0 ,sizeof (opt)); +if (copy_from_sockptr (&opt, optval, optlen)) +return -EFAULT ; + +if (opt.flags & ~TCP_AUTHOPT_KNOWN_FLAGS ) +return -EINVAL ;
Here if the user requests unrecognized flags an error is reported. My intention is that new fields will be accompanied by new flags.
Quoted 38 lines
Quoted 8 lines
net/ipv4/tcp_authopt.c new file +8
-0+ info =__tcp_authopt_info_get_or_create (sk); +if (IS_ERR (info)) +return PTR_ERR (info); + + info->flags = opt.flags &TCP_AUTHOPT_KNOWN_FLAGS ; + +return 0 ; +}[..]
net/ipv4/tcp_authopt.c line 170
Quoted 11 lines
net/ipv4/tcp_authopt.c new file +11
-0+int tcp_set_authopt_key (struct sock *sk,sockptr_t optval,unsigned int optlen) +{ +struct tcp_authopt_key opt; +struct tcp_authopt_info *info; +struct tcp_authopt_key_info *key_info; + +sock_owned_by_me (sk); + +/* If userspace optlen is too short fill the rest with zeros */ +if (optlen >sizeof (opt)) +return -EINVAL ;Ditto
Quoted 14 lines
net/ipv4/tcp_authopt.c new file +14
-0+memset (&opt,0 ,sizeof (opt)); +if (copy_from_sockptr (&opt, optval, optlen)) +return -EFAULT ; + +if (opt.flags & ~TCP_AUTHOPT_KEY_KNOWN_FLAGS ) +return -EINVAL ; + +if (opt.keylen >TCP_AUTHOPT_MAXKEYLEN ) +return -EINVAL ; + +/* Delete is a special case: */ +if (opt.flags &TCP_AUTHOPT_KEY_DEL ) { + info =rcu_dereference_check (tcp_sk (sk)->authopt_info,lockdep_sock_is_held (sk)); +if (!info)
Quoted 8 lines
net/ipv4/tcp_authopt.c line 189
Quoted 5 lines
net/ipv4/tcp_authopt.c new file +5
-0+return -ENOENT ; + key_info =tcp_authopt_key_lookup_exact (sk, info, &opt); +if (!key_info) +return -ENOENT ; +tcp_authopt_key_del (sk, info, key_info);Doesn't seem to be safe together with
tcp_authopt_select_key(). A key can be in use at this moment - you have to add checks for it.
tcp_authopt_key_del does kfree_rcu. As far as I understand this means that if select_key can see the key it is guaranteed to live until the next grace period, which shouldn't be until after the packet is signed.
I will attempt to document this restriction on tcp_authopt_select_key: you can't do anything with the key except give it to tcp_authopt_hash before an RCU grace period.
I'm not confident this is correct in all cases. It's inspired by what MD5 does but apparently those key lists are protected by a combination of sk_lock and rcu?
Quoted 17 lines
Quoted 17 lines
net/ipv4/tcp_authopt.c new file +17
-0+return 0 ; + } + +/* check key family */ +if (opt.flags &TCP_AUTHOPT_KEY_ADDR_BIND ) { +if (sk->sk_family != opt.addr.ss_family) +return -EINVAL ; + } + +/* Initialize tcp_authopt_info if not already set */ + info =__tcp_authopt_info_get_or_create (sk); +if (IS_ERR (info)) +return PTR_ERR (info); + +/* If an old key exists with exact ID then remove and replace. +* RCU-protected readers might observe both and pick any. +*/
Quoted 8 lines
net/ipv4/tcp_authopt.c line 212
Quoted 6 lines
net/ipv4/tcp_authopt.c new file +6
-0+ key_info =tcp_authopt_key_lookup_exact (sk, info, &opt); +if (key_info) +tcp_authopt_key_del (sk, info, key_info); + key_info =sock_kmalloc (sk,sizeof (*key_info),GFP_KERNEL | __GFP_ZERO); +if (!key_info) +return -ENOMEM ;So, you may end up without any key.
Moving the sock_kmalloc higher should fix this, there would be no effect on alloc failure.
Quoted 6 lines
Also, replacing a key is not at all safe: you may receive old segments which you in turn will discard and reset the connection. > I think the limitation RFC puts on removing keys in use and replacing existing keys are actually reasonable. Probably, it'd be better to enforce "key in use => desired key is different (or
key_outdatedflag) => key not in use => key may be removed" life-cycle of MKT.
Userspace breaking its own connections seems fine, it can already do this in many ways.
If the current key is removed the kernel will just switch to another valid key. If no valid keys exist then I expect it will switch to unsigned packets which is possibly quite dangerous.
Maybe it should be possible to insert a "marker" key which just says "don't do any unsigned traffic with this peer"?
--
Regards,
Leonard
RFC v3 2/15 docs: Add user documentation for tcp_authopt
In reply to Leonard CrestezThe .rst documentation contains a brief description of the user interface and includes kernel-doc generated from uapi header.
--- Documentation/networking/index.rst | 1 + Documentation/networking/tcp_authopt.rst | 44 ++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 Documentation/networking/tcp_authopt.rst
Documentation/networking/index.rst +1 -0
@@ -100,10 +100,11 @@ Contents: strparser switchdev sysfs-tagging tc-actions-env-rules tcp-thin + tcp_authopt team timestamping tipc tproxy tuntap
Documentation/networking/tcp_authopt.rst new file +44 -0
44 lines not on this page · raw message
Signature · 1 line
2.25.1
RFC v3 3/15 selftests: Initial tcp_authopt test module
In reply to Leonard CrestezThis test suite is written as a standalone python3 package using dependencies such as scapy.
The run.sh script wrapper called from kselftest infrastructure uses "tox" to generate an isolated virtual environment just for running these tests. The run.sh wrapper can be called from anywhere and does not rely on kselftest infrastructure.
The python3 and tox packages be installed manually but not any other dependencies
--- tools/testing/selftests/tcp_authopt/Makefile | 5 +++++ tools/testing/selftests/tcp_authopt/README.rst | 15 +++++++++++++++ tools/testing/selftests/tcp_authopt/config | 6 ++++++ tools/testing/selftests/tcp_authopt/run.sh | 11 +++++++++++ tools/testing/selftests/tcp_authopt/setup.cfg | 17 +++++++++++++++++ tools/testing/selftests/tcp_authopt/setup.py | 5 +++++ .../tcp_authopt/tcp_authopt_test/__init__.py | 0 7 files changed, 59 insertions(+) create mode 100644 tools/testing/selftests/tcp_authopt/Makefile create mode 100644 tools/testing/selftests/tcp_authopt/README.rst create mode 100644 tools/testing/selftests/tcp_authopt/config create mode 100755 tools/testing/selftests/tcp_authopt/run.sh create mode 100644 tools/testing/selftests/tcp_authopt/setup.cfg create mode 100644 tools/testing/selftests/tcp_authopt/setup.py create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/__init__.py
tools/testing/selftests/tcp_authopt/Makefile new file +5 -0
@@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 +include ../lib.mk + +TEST_PROGS += ./run.sh +TEST_FILES := setup.py setup.cfg tcp_authopt_test
tools/testing/selftests/tcp_authopt/README.rst new file +15 -0
@@ -0,0 +1,15 @@ +========================================= +Tests for linux TCP Authentication Option +========================================= + +Test suite is written in python3 using pytest and scapy. The test suite is +mostly self-contained as a python package. + +The recommended way to run this is the included `run.sh` script as root, this +will automatically create a virtual environment with the correct dependencies +using `tox`. + +An old separate version can be found here: https://github.com/cdleonard/tcp-authopt-test + +Integration with kselftest infrastructure is minimal: when in doubt just run +this separately.
tools/testing/selftests/tcp_authopt/config new file +6 -0
@@ -0,0 +1,6 @@ +# RFC5925 TCP Authentication Option and all algorithms +CONFIG_TCP_AUTHOPT=y +CONFIG_CRYPTO_SHA1=M +CONFIG_CRYPTO_HMAC=M +CONFIG_CRYPTO_AES=M +CONFIG_CRYPTO_CMAC=M
tools/testing/selftests/tcp_authopt/run.sh new file +11 -0
@@ -0,0 +1,11 @@ +#! /bin/bash + +if ! command -v tox >/dev/null; then + echo >&2 "error: please install the python tox package" + exit 1 +fi +if [[ $(id -u) -ne 0 ]]; then + echo >&2 "warning: running as non-root user is unlikely to work" +fi +cd "$(dirname "${BASH_SOURCE[0]}")" +exec tox "$@"
tools/testing/selftests/tcp_authopt/setup.cfg new file +17 -0
@@ -0,0 +1,17 @@ +[options] +install_requires= + cryptography + nsenter + pre-commit + pytest + scapy + +[tox:tox] +envlist = py3 + +[testenv] +commands = pytest {posargs} + +[metadata] +name = tcp-authopt-test +version = 0.1
tools/testing/selftests/tcp_authopt/setup.py new file +5 -0
@@ -0,0 +1,5 @@ +#! /usr/bin/env python3 + +from setuptools import setup + +setup()
tools/testing/selftests/tcp_authopt/tcp_authopt_test/__init__.py new file +0 -0
Signature · 1 line
2.25.1
RFC v3 4/15 selftests: tcp_authopt: Initial sockopt manipulation
In reply to Leonard Crestez--- .../tcp_authopt/tcp_authopt_test/conftest.py | 21 ++ .../tcp_authopt_test/linux_tcp_authopt.py | 188 ++++++++++++++++++ .../tcp_authopt/tcp_authopt_test/sockaddr.py | 101 ++++++++++ .../tcp_authopt_test/test_sockopt.py | 74 +++++++ 4 files changed, 384 insertions(+) create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/conftest.py create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/linux_tcp_authopt.py create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/sockaddr.py create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/test_sockopt.py
tools/testing/selftests/tcp_authopt/tcp_authopt_test/conftest.py new file +21 -0
@@ -0,0 +1,21 @@ +# SPDX-License-Identifier: GPL-2.0 +from tcp_authopt_test.linux_tcp_authopt import has_tcp_authopt +import pytest +import logging +from contextlib import ExitStack + +logger = logging.getLogger(__name__) + +skipif_missing_tcp_authopt = pytest.mark.skipif( + not has_tcp_authopt(), reason="Need CONFIG_TCP_AUTHOPT" +) + + [email protected] +def exit_stack(): + """Return a contextlib.ExitStack as a pytest fixture + + This reduces indentation making code more readable + """ + with ExitStack() as exit_stack: + yield exit_stack
tools/testing/selftests/tcp_authopt/tcp_authopt_test/linux_tcp_authopt.py new file +188 -0
188 lines not on this page · raw message
tools/testing/selftests/tcp_authopt/tcp_authopt_test/sockaddr.py new file +101 -0
101 lines not on this page · raw message
tools/testing/selftests/tcp_authopt/tcp_authopt_test/test_sockopt.py new file +74 -0
74 lines not on this page · raw message
Signature · 1 line
2.25.1
RFC v3 5/15 tcp: authopt: Add crypto initialization
In reply to Leonard CrestezThe crypto_shash API is used in order to compute packet signatures. The API comes with several unfortunate limitations:
1) Allocating a crypto_shash can sleep and must be done in user context.
2) Packet signatures must be computed in softirq context
3) Packet signatures use dynamic "traffic keys" which require exclusive access to crypto_shash for crypto_setkey.
The solution is to allocate one crypto_shash for each possible cpu for each algorithm at setsockopt time. The per-cpu tfm is then borrowed from softirq context, signatures are computed and the tfm is returned.
The pool for each algorithm is reference counted, initialized at setsockopt time and released in tcp_authopt_key_info's rcu callback
--- include/net/tcp_authopt.h | 3 + net/ipv4/tcp_authopt.c | 177 +++++++++++++++++++++++++++++++++++++- 2 files changed, 178 insertions(+), 2 deletions(-)
include/net/tcp_authopt.h +3 -0
@@ -2,10 +2,12 @@#ifndef _LINUX_TCP_AUTHOPT_H#define _LINUX_TCP_AUTHOPT_H#include <uapi/linux/tcp.h> +struct tcp_authopt_alg_imp ; +/** * struct tcp_authopt_key_info - Representation of a Master Key Tuple as per RFC5925 * * Key structure lifetime is only protected by RCU so readers needs to hold a * single rcu_read_lock until they're done with the key.
@@ -20,10 +22,11 @@ struct tcp_authopt_key_info {u8 send_id, recv_id;u8 alg_id;u8 keylen;u8 key[TCP_AUTHOPT_MAXKEYLEN ];struct sockaddr_storage addr; +struct tcp_authopt_alg_imp *alg; };/** * struct tcp_authopt_info - Per-socket information regarding tcp_authopt *
net/ipv4/tcp_authopt.c +175 -2
235 lines not on this page · raw message
Signature · 1 line
2.25.1
Re: [RFCv3 05/15] tcp: authopt: Add crypto initialization
Quoted 8 lines · Leonard Crestez
The
crypto_shashAPI is used in order to compute packet signatures. The API comes with several unfortunate limitations:1) Allocating a
crypto_shashcan sleep and must be done in user context.
2) Packet signatures must be computed in softirq context
3) Packet signatures use dynamic "traffic keys" which require exclusive access tocrypto_shashforcrypto_setkey.
Quoted 8 lines
The solution is to allocate one
crypto_shashfor each possible cpu for each algorithm at setsockopt time. The per-cpu tfm is then borrowed from softirq context, signatures are computed and the tfm is returned.The pool for each algorithm is reference counted, initialized at setsockopt time and released in tcp_authopt_key_info's rcu callback
I don't know, why should we really care and try so hard to release the tfm per cpu ?
I would simply allocate them at boot time.
This would avoid the expensive refcounting (potential false sharing)
Re: [RFCv3 05/15] tcp: authopt: Add crypto initialization
In reply to Leonard CrestezQuoted 12 lines · Leonard Crestez
The
crypto_shashAPI is used in order to compute packet signatures. The API comes with several unfortunate limitations:1) Allocating a
crypto_shashcan sleep and must be done in user context.
2) Packet signatures must be computed in softirq context
3) Packet signatures use dynamic "traffic keys" which require exclusive access tocrypto_shashforcrypto_setkey.The solution is to allocate one
crypto_shashfor each possible cpu for each algorithm at setsockopt time. The per-cpu tfm is then borrowed from softirq context, signatures are computed and the tfm is returned.
I could not see the per-cpu stuff that you mention in the changelog.
Herbert Xu
maintainerRe: [RFCv3 05/15] tcp: authopt: Add crypto initialization
Quoted 10 lines · Eric Dumazet
Quoted 8 lines · Leonard Crestez
The
crypto_shashAPI is used in order to compute packet signatures. The API comes with several unfortunate limitations:1) Allocating a
crypto_shashcan sleep and must be done in user context.
2) Packet signatures must be computed in softirq context
3) Packet signatures use dynamic "traffic keys" which require exclusive access tocrypto_shashforcrypto_setkey.
Quoted 6 lines
Quoted 4 lines
The solution is to allocate one
crypto_shashfor each possible cpu for each algorithm at setsockopt time. The per-cpu tfm is then borrowed from softirq context, signatures are computed and the tfm is returned.I could not see the per-cpu stuff that you mention in the changelog.
Perhaps it's time we moved the key information from the tfm into the request structure for hashes? Or at least provide a way for the key to be in the request structure in addition to the tfm as the tfm model still works for IPsec. Ard/Eric, what do you think about that?
Thanks,
Signature · 3 lines
Email: Herbert Xu <[email protected]> Home Page: http://gondor.apana.org.au/~herbert/ PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
Re: [RFCv3 05/15] tcp: authopt: Add crypto initialization
Quoted 17 lines · Herbert Xu
Quoted 15 lines · Eric Dumazet
Quoted 12 lines · Leonard Crestez
The
crypto_shashAPI is used in order to compute packet signatures. The API comes with several unfortunate limitations:1) Allocating a
crypto_shashcan sleep and must be done in user context.
2) Packet signatures must be computed in softirq context
3) Packet signatures use dynamic "traffic keys" which require exclusive access tocrypto_shashforcrypto_setkey.The solution is to allocate one
crypto_shashfor each possible cpu for each algorithm at setsockopt time. The per-cpu tfm is then borrowed from softirq context, signatures are computed and the tfm is returned.
Quoted 7 lines
Quoted 1 line
I could not see the per-cpu stuff that you mention in the changelog.
Perhaps it's time we moved the key information from the tfm into the request structure for hashes? Or at least provide a way for the key to be in the request structure in addition to the tfm as the tfm model still works for IPsec. Ard/Eric, what do you think about that?
What is the typical size of a ' tfm' and associated data ?
per-cpu tfm might still make sense, if we had proper NUMA affinities. AFAIK, currently we can not provide a numa node to crypto allocations.
So using construct like this ends up allocating all data on one single NUMA node
for_each_possible_cpu(cpu) {
tfm = crypto_alloc_shash(algo->name, 0, 0);
if (IS_ERR(tfm))
return PTR_ERR(tfm);
p_tfm = per_cpu_ptr(algo->tfms, cpu);
*p_tfm = tfm;
}Re: [RFCv3 05/15] tcp: authopt: Add crypto initialization
In reply to Herbert XuQuoted 17 lines · Herbert Xu
Quoted 15 lines · Eric Dumazet
Quoted 12 lines · Leonard Crestez
The
crypto_shashAPI is used in order to compute packet signatures. The API comes with several unfortunate limitations:1) Allocating a
crypto_shashcan sleep and must be done in user context.
2) Packet signatures must be computed in softirq context
3) Packet signatures use dynamic "traffic keys" which require exclusive access tocrypto_shashforcrypto_setkey.The solution is to allocate one
crypto_shashfor each possible cpu for each algorithm at setsockopt time. The per-cpu tfm is then borrowed from softirq context, signatures are computed and the tfm is returned.
Quoted 8 lines
Quoted 1 line
I could not see the per-cpu stuff that you mention in the changelog.
Perhaps it's time we moved the key information from the tfm into the request structure for hashes? Or at least provide a way for the key to be in the request structure in addition to the tfm as the tfm model still works for IPsec. Ard/Eric, what do you think about that?
I think it makes sense for a shash desc to have the ability to carry a key, which will be used instead of the TFM key, but this seems like quite a lot of work, given that all implementations will need to be updated. Also, setkey() can currently sleep, so we need to check whether the existing key manipulation code can actually execute during init/update/final if sleeping is not permitted.
Re: [RFCv3 05/15] tcp: authopt: Add crypto initialization
Quoted 27 lines · Ard Biesheuvel
Quoted 17 lines · Herbert Xu
Quoted 15 lines · Eric Dumazet
Quoted 12 lines · Leonard Crestez
The
crypto_shashAPI is used in order to compute packet signatures. The API comes with several unfortunate limitations:1) Allocating a
crypto_shashcan sleep and must be done in user context.
2) Packet signatures must be computed in softirq context
3) Packet signatures use dynamic "traffic keys" which require exclusive access tocrypto_shashforcrypto_setkey.The solution is to allocate one
crypto_shashfor each possible cpu for each algorithm at setsockopt time. The per-cpu tfm is then borrowed from softirq context, signatures are computed and the tfm is returned.Quoted 8 lines
Quoted 1 line
I could not see the per-cpu stuff that you mention in the changelog.
Perhaps it's time we moved the key information from the tfm into the request structure for hashes? Or at least provide a way for the key to be in the request structure in addition to the tfm as the tfm model still works for IPsec. Ard/Eric, what do you think about that?
Quoted 6 lines
I think it makes sense for a shash desc to have the ability to carry a key, which will be used instead of the TFM key, but this seems like quite a lot of work, given that all implementations will need to be updated. Also,
setkey()can currently sleep, so we need to check whether the existing key manipulation code can actually execute duringinit/update/finalif sleeping is not permitted.
Are you sure that setkey can sleep? The documentation is not clear, maybe it only applies to certain hardware implementations?
The TCP Authentication Option needs dynamic keys for SYN and SYNACK packets, all of which happens in BH context.
--
Regards,
Leonard
Re: [RFCv3 05/15] tcp: authopt: Add crypto initialization
In reply to Eric DumazetQuoted 9 lines · Eric Dumazet
Quoted 8 lines · Leonard Crestez
The
crypto_shashAPI is used in order to compute packet signatures. The API comes with several unfortunate limitations:1) Allocating a
crypto_shashcan sleep and must be done in user context.
2) Packet signatures must be computed in softirq context
3) Packet signatures use dynamic "traffic keys" which require exclusive access tocrypto_shashforcrypto_setkey.
Quoted 6 lines
Quoted 4 lines
The solution is to allocate one
crypto_shashfor each possible cpu for each algorithm at setsockopt time. The per-cpu tfm is then borrowed from softirq context, signatures are computed and the tfm is returned.I could not see the per-cpu stuff that you mention in the changelog.
That's a little embarrasing, I forgot to implement the actual per-cpu stuff. tcp_authopt_alg_imp.tfm is meant to be an array up to NR_CPUS and tcp_authopt_alg_get_tfm needs no locking other than preempt_disable (which should already be the case).
The reference counting would still only happen from very few places: setsockopt, close and openreq. This would only impact request/response traffic and relatively little.
Performance was not a major focus so far. Preventing impact on non-AO connections is important but typical AO usecases are long-lived low-traffic connections.
--
Regards,
Leonard
Re: [RFCv3 05/15] tcp: authopt: Add crypto initialization
Quoted 16 lines · Leonard Crestez
Quoted 14 lines · Eric Dumazet
Quoted 12 lines · Leonard Crestez
The
crypto_shashAPI is used in order to compute packet signatures. The API comes with several unfortunate limitations:1) Allocating a
crypto_shashcan sleep and must be done in user context.
2) Packet signatures must be computed in softirq context
3) Packet signatures use dynamic "traffic keys" which require exclusive access tocrypto_shashforcrypto_setkey.The solution is to allocate one
crypto_shashfor each possible cpu for each algorithm at setsockopt time. The per-cpu tfm is then borrowed from softirq context, signatures are computed and the tfm is returned.
Quoted 6 lines
Quoted 1 line
I could not see the per-cpu stuff that you mention in the changelog.
That's a little embarrasing, I forgot to implement the actual per-cpu stuff. tcp_authopt_alg_imp.tfm is meant to be an array up to
NR_CPUSandtcp_authopt_alg_get_tfmneeds no locking other thanpreempt_disable(which should already be the case).
Well, do not use arrays of NR_CPUS and instead use normal per_cpu accessors (as in __tcp_alloc_md5sig_pool)
Quoted 4 lines
The reference counting would still only happen from very few places:
setsockopt, close and openreq. This would only impact request/response traffic and relatively little.
What I meant is that __tcp_alloc_md5sig_pool() allocates stuff one time, we do not care about tcp_md5sig_pool_populated going back to false.
Otherwise, a single user application constantly allocating a socket, enabling MD5 (or authopt), then closing the socket would incur a big cost on hosts with a lot of cpus.
Quoted 8 lines
Performance was not a major focus so far. Preventing impact on non-AO connections is important but typical AO usecases are long-lived low-traffic connections.
--
Regards,
Leonard
Re: [RFCv3 05/15] tcp: authopt: Add crypto initialization
Quoted 32 lines · Eric Dumazet
Quoted 16 lines · Leonard Crestez
Quoted 14 lines · Eric Dumazet
Quoted 12 lines · Leonard Crestez
The
crypto_shashAPI is used in order to compute packet signatures. The API comes with several unfortunate limitations:1) Allocating a
crypto_shashcan sleep and must be done in user context.
2) Packet signatures must be computed in softirq context
3) Packet signatures use dynamic "traffic keys" which require exclusive access tocrypto_shashforcrypto_setkey.The solution is to allocate one
crypto_shashfor each possible cpu for each algorithm at setsockopt time. The per-cpu tfm is then borrowed from softirq context, signatures are computed and the tfm is returned.Quoted 6 lines
Quoted 1 line
I could not see the per-cpu stuff that you mention in the changelog.
That's a little embarrasing, I forgot to implement the actual per-cpu stuff. tcp_authopt_alg_imp.tfm is meant to be an array up to
NR_CPUSandtcp_authopt_alg_get_tfmneeds no locking other thanpreempt_disable(which should already be the case).Well, do not use arrays of
NR_CPUSand instead use normalper_cpuaccessors (as in__tcp_alloc_md5sig_pool)Quoted 4 lines
The reference counting would still only happen from very few places:
setsockopt, close and openreq. This would only impact request/response traffic and relatively little.
Quoted 6 lines
What I meant is that
__tcp_alloc_md5sig_pool()allocates stuff one time, we do not care abouttcp_md5sig_pool_populatedgoing back to false.Otherwise, a single user application constantly allocating a socket, enabling MD5 (or authopt), then closing the socket would incur a big cost on hosts with a lot of cpus.
Allocating only once would definitely simply things.
I don't know if this might end up tying hardware resources forever if some accelerators are in play but for this feature software-only crypto is perfectly fine.
--
Regards,
Leonard
RFC v3 6/15 tcp: authopt: Compute packet signatures
In reply to Leonard CrestezComputing tcp authopt packet signatures is a two step process:
* traffic key is computed based on tcp 4-tuple, initial sequence numbers and the secret key.
* packet mac is computed based on traffic key and content of individual packets.
The traffic key could be cached for established sockets but it is not.
A single code path exists for ipv4/ipv6 and input/output. This keeps the code short but slightly slower due to lots of conditionals.
On output we read remote IP address from socket members on output, we can't use skb network header because it's computed after TCP options.
On input we read remote IP address from skb network headers, we can't use socket binding members because those are not available for SYN.
--- net/ipv4/tcp_authopt.c | 467 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 467 insertions(+)
net/ipv4/tcp_authopt.c +467 -0
@@ -392,5 +392,472 @@ int tcp_set_authopt_key(struct sock *sk, sockptr_t optval, unsigned int optlen)memcpy (&key_info->addr, &opt.addr,sizeof (key_info->addr));hlist_add_head_rcu (&key_info->node, &info->head);return 0 ; } + +/* feed traffic key into shash */ +static int tcp_authopt_shash_traffic_key (struct shash_desc *desc, +struct sock *sk, +struct sk_buff *skb, +bool input, +bool ipv6) +{ +struct tcphdr *th =tcp_hdr (skb); +int err; +__be32 sisn, disn; +__be16 digestbits =htons (crypto_shash_digestsize (desc->tfm) *8 ); + +// RFC5926 section 3.1.1.1 + err =crypto_shash_update (desc,"\x01TCP-AO" ,7 ); +if (err) +return err; + +/* Addresses from packet on input and from socket on output +* This is because on output MAC is computed before prepending IP header +*/ +if (input) { +if (ipv6) + err =crypto_shash_update (desc, (u8 *)&ipv6_hdr (skb)->saddr,32 ); +else + err =crypto_shash_update (desc, (u8 *)&ip_hdr (skb)->saddr,8 ); +if (err) +return err; + }else { +if (ipv6) { +struct in6_addr *saddr; +struct in6_addr *daddr; + + saddr = &sk->sk_v6_rcv_saddr; + daddr = &sk->sk_v6_daddr; + err =crypto_shash_update (desc, (u8 *)&sk->sk_v6_rcv_saddr,16 ); +if (err) +return err; + err =crypto_shash_update (desc, (u8 *)&sk->sk_v6_daddr,16 ); +if (err) +return err; + }else { + err =crypto_shash_update (desc, (u8 *)&sk->sk_rcv_saddr,4 ); +if (err) +return err; + err =crypto_shash_update (desc, (u8 *)&sk->sk_daddr,4 ); +if (err) +return err; + } + } + +/* TCP ports from header */ + err =crypto_shash_update (desc, (u8 *)&th->source,4 ); +if (err) +return err; + +/* special cases for SYN and SYN/ACK */ +if (th->syn && !th->ack) { + sisn = th->seq; + disn =0 ; + }else if (th->syn && th->ack) { + sisn = th->seq; + disn =htonl (ntohl (th->ack_seq) -1 ); + }else { +struct tcp_authopt_info *authopt_info; + +/* Fetching authopt_info like this means it's possible that authopt_info +* was deleted while we were hashing. If that happens we drop the packet +* which should be fine. +* +* A better solution might be to always pass info as a parameter, or +* compute traffic_key for established sockets separately. +*/ +rcu_read_lock (); + authopt_info =rcu_dereference (tcp_sk (sk)->authopt_info); +if (!authopt_info) { +rcu_read_unlock (); +return -EINVAL ; + } +/* Initial sequence numbers for ESTABLISHED connections from info */ +if (input) { + sisn =htonl (authopt_info->dst_isn); + disn =htonl (authopt_info->src_isn); + }else { + sisn =htonl (authopt_info->src_isn); + disn =htonl (authopt_info->dst_isn); + } +rcu_read_unlock (); + } + + err =crypto_shash_update (desc, (u8 *)&sisn,4 ); +if (err) +return err; + err =crypto_shash_update (desc, (u8 *)&disn,4 ); +if (err) +return err; + + err =crypto_shash_update (desc, (u8 *)&digestbits,2 ); +if (err) +return err; + +return 0 ; +} + +/* Convert a variable-length key to a 16-byte fixed-length key for AES-CMAC +* This is described in RFC5926 section 3.1.1.2 +*/ +static int aes_setkey_derived (struct crypto_shash *tfm,u8 *key,size_t keylen) +{ +static const u8 zeros[16 ] = {0 }; +u8 derived_key[16 ]; +int err; + +if (WARN_ON (crypto_shash_digestsize (tfm) !=16 )) +return -EINVAL ; + err =crypto_shash_setkey (tfm, zeros,sizeof (zeros)); +if (err) +return err; + err =crypto_shash_tfm_digest (tfm, key, keylen, derived_key); +if (err) +return err; +return crypto_shash_setkey (tfm, derived_key,sizeof (derived_key)); +} + +static int tcp_authopt_get_traffic_key (struct sock *sk, +struct sk_buff *skb, +struct tcp_authopt_key_info *key, +bool input, +bool ipv6, +u8 *traffic_key) +{ +SHASH_DESC_ON_STACK (desc, kdf_tfm); +struct crypto_shash *kdf_tfm; +int err; + + kdf_tfm =tcp_authopt_get_kdf_shash (key); +if (IS_ERR (kdf_tfm)) +return PTR_ERR (kdf_tfm); +if (WARN_ON (crypto_shash_digestsize (kdf_tfm) != key->alg->traffic_key_len)) { + err = -EINVAL ; + goto out; + } + +if (key->alg_id ==TCP_AUTHOPT_ALG_AES_128_CMAC_96 && key->keylen !=16 ) { + err =aes_setkey_derived (kdf_tfm, key->key, key->keylen); +if (err) + goto out; + }else { + err =crypto_shash_setkey (kdf_tfm, key->key, key->keylen); +if (err) + goto out; + } + + desc->tfm = kdf_tfm; + err =crypto_shash_init (desc); +if (err) + goto out; + + err =tcp_authopt_shash_traffic_key (desc, sk, skb, input, ipv6); +if (err) + goto out; + + err =crypto_shash_final (desc, traffic_key); +if (err) + goto out; +//printk("traffic_key: %*phN\n", 20, traffic_key); + +out: +tcp_authopt_put_kdf_shash (key, kdf_tfm); +return err; +} + +static int crypto_shash_update_zero (struct shash_desc *desc,int len) +{ +u8 zero =0 ; +int i, err; + +for (i =0 ; i < len; ++i) { + err =crypto_shash_update (desc, &zero,1 ); +if (err) +return err; + } + +return 0 ; +} + +static int tcp_authopt_hash_tcp4_pseudoheader (struct shash_desc *desc, +__be32 saddr, +__be32 daddr, +int nbytes) +{ +struct tcp4_pseudohdr phdr = { + .saddr = saddr, + .daddr = daddr, + .pad =0 , + .protocol =IPPROTO_TCP , + .len =htons (nbytes) + }; +return crypto_shash_update (desc, (u8 *)&phdr,sizeof (phdr)); +} + +static int tcp_authopt_hash_tcp6_pseudoheader (struct shash_desc *desc, +struct in6_addr *saddr, +struct in6_addr *daddr, +u32 plen) +{ +int err; +u32 buf[2 ]; + + buf[0 ] =htonl (plen); + buf[1 ] =htonl (IPPROTO_TCP ); + + err =crypto_shash_update (desc, (u8 *)saddr,sizeof (*saddr)); +if (err) +return err; + err =crypto_shash_update (desc, (u8 *)daddr,sizeof (*daddr)); +if (err) +return err; +return crypto_shash_update (desc, (u8 *)&buf,sizeof (buf)); +} + +/* TCP authopt as found in header */ +struct tcphdr_authopt { +u8 num; +u8 len; +u8 keyid; +u8 rnextkeyid; +u8 mac[0 ]; +}; + +/* Find TCP_AUTHOPT in header. +* +* Returns pointer to TCP_AUTHOPT or NULL if not found. +*/ +static u8 *tcp_authopt_find_option (struct tcphdr *th) +{ +int length = (th->doff <<2 ) -sizeof (*th); +u8 *ptr = (u8 *)(th +1 ); + +while (length >=2 ) { +int opcode = *ptr++; +int opsize; + +switch (opcode) { +case TCPOPT_EOL : +return NULL ; +case TCPOPT_NOP : + length--; +continue ; +default : +if (length <2 ) +return NULL ; + opsize = *ptr++; +if (opsize <2 ) +return NULL ; +if (opsize > length) +return NULL ; +if (opcode ==TCPOPT_AUTHOPT ) +return ptr -2 ; + } + ptr += opsize -2 ; + length -= opsize; + } +return NULL ; +} + +/** Hash tcphdr options. +* If include_options is false then only the TCPOPT_AUTHOPT option itself is hashed +* Maybe we could skip option parsing by assuming the AUTHOPT header is at hash_location-4? +*/ +static int tcp_authopt_hash_opts (struct shash_desc *desc, +struct tcphdr *th, +bool include_options) +{ +int err; +/* start of options */ +u8 *tcp_opts = (u8 *)(th +1 ); +/* end of options */ +u8 *tcp_data = ((u8 *)th) + th->doff *4 ; +/* pointer to TCPOPT_AUTHOPT */ +u8 *authopt_ptr =tcp_authopt_find_option (th); +u8 authopt_len; + +if (!authopt_ptr) +return -EINVAL ; + authopt_len = *(authopt_ptr +1 ); + +if (include_options) { + err =crypto_shash_update (desc, tcp_opts, authopt_ptr - tcp_opts +4 ); +if (err) +return err; + err =crypto_shash_update_zero (desc, authopt_len -4 ); +if (err) +return err; + err =crypto_shash_update (desc, + authopt_ptr + authopt_len, + tcp_data - (authopt_ptr + authopt_len)); +if (err) +return err; + }else { + err =crypto_shash_update (desc, authopt_ptr,4 ); +if (err) +return err; + err =crypto_shash_update_zero (desc, authopt_len -4 ); +if (err) +return err; + } + +return 0 ; +} + +static int skb_shash_frags (struct shash_desc *desc, +struct sk_buff *skb) +{ +struct sk_buff *frag_iter; +int err, i; + +for (i =0 ; i <skb_shinfo (skb)->nr_frags; i++) { +skb_frag_t *f = &skb_shinfo (skb)->frags[i]; +u32 p_off, p_len, copied; +struct page *p; +u8 *vaddr; + +skb_frag_foreach_page (f,skb_frag_off (f),skb_frag_size (f), + p, p_off, p_len, copied) { + vaddr =kmap_atomic (p); + err =crypto_shash_update (desc, vaddr + p_off, p_len); +kunmap_atomic (vaddr); +if (err) +return err; + } + } + + skb_walk_frags(skb, frag_iter) { + err =skb_shash_frags (desc, frag_iter); +if (err) +return err; + } + +return 0 ; +} + +static int tcp_authopt_hash_packet (struct crypto_shash *tfm, +struct sock *sk, +struct sk_buff *skb, +bool input, +bool ipv6, +bool include_options, +u8 *macbuf) +{ +struct tcphdr *th =tcp_hdr (skb); +SHASH_DESC_ON_STACK (desc, tfm); +int err; + +/* NOTE: SNE unimplemented */ +__be32 sne =0 ; + + desc->tfm = tfm; + err =crypto_shash_init (desc); +if (err) +return err; + + err =crypto_shash_update (desc, (u8 *)&sne,4 ); +if (err) +return err; + +if (ipv6) { +struct in6_addr *saddr; +struct in6_addr *daddr; + +if (input) { + saddr = &ipv6_hdr (skb)->saddr; + daddr = &ipv6_hdr (skb)->daddr; + }else { + saddr = &sk->sk_v6_rcv_saddr; + daddr = &sk->sk_v6_daddr; + } + err =tcp_authopt_hash_tcp6_pseudoheader (desc, saddr, daddr, skb->len); +if (err) +return err; + }else { +__be32 saddr; +__be32 daddr; + +if (input) { + saddr =ip_hdr (skb)->saddr; + daddr =ip_hdr (skb)->daddr; + }else { + saddr = sk->sk_rcv_saddr; + daddr = sk->sk_daddr; + } + err =tcp_authopt_hash_tcp4_pseudoheader (desc, saddr, daddr, skb->len); +if (err) +return err; + } + +// TCP header with checksum set to zero + { +struct tcphdr hashed_th = *th; + + hashed_th.check =0 ; + err =crypto_shash_update (desc, (u8 *)&hashed_th,sizeof (hashed_th)); +if (err) +return err; + } + +// TCP options + err =tcp_authopt_hash_opts (desc, th, include_options); +if (err) +return err; + +// Rest of SKB->data + err =crypto_shash_update (desc, (u8 *)th + th->doff *4 ,skb_headlen (skb) - th->doff *4 ); +if (err) +return err; + + err =skb_shash_frags (desc, skb); +if (err) +return err; + +return crypto_shash_final (desc, macbuf); +} + +int __tcp_authopt_calc_mac (struct sock *sk, +struct sk_buff *skb, +struct tcp_authopt_key_info *key, +bool input, +char *macbuf) +{ +struct crypto_shash *mac_tfm; +u8 traffic_key[TCP_AUTHOPT_MAX_TRAFFIC_KEY_LEN ]; +int err; +bool ipv6 = (sk->sk_family !=AF_INET ); + +if (sk->sk_family !=AF_INET && sk->sk_family !=AF_INET6 ) +return -EINVAL ; +if (WARN_ON (key->alg->traffic_key_len >sizeof (traffic_key))) +return -ENOBUFS ; + + err =tcp_authopt_get_traffic_key (sk, skb, key, input, ipv6, traffic_key); +if (err) +return err; + + mac_tfm =tcp_authopt_get_mac_shash (key); +if (IS_ERR (mac_tfm)) +return PTR_ERR (mac_tfm); +if (crypto_shash_digestsize (mac_tfm) >TCP_AUTHOPT_MAXMACBUF ) { + err = -EINVAL ; + goto out; + } + err =crypto_shash_setkey (mac_tfm, traffic_key, key->alg->traffic_key_len); +if (err) + goto out; + + err =tcp_authopt_hash_packet (mac_tfm, + sk, + skb, + input, + ipv6, + !(key->flags &TCP_AUTHOPT_KEY_EXCLUDE_OPTS ), + macbuf); +//printk("mac: %*phN\n", key->maclen, macbuf); + +out: +tcp_authopt_put_mac_shash (key, mac_tfm); +return err; +}
Signature · 1 line
2.25.1
kernel test robot
build botRe: [RFCv3 06/15] tcp: authopt: Compute packet signatures
reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross #…
Hi Leonard,
[FYI, it's a private test report for your RFC patch.] [auto build test WARNING on 3a62c333497b164868fdcd241842a1dd4e331825]
url: https://github.com/0day-ci/linux/commits/Leonard-Crestez/tcp-Initial-support-for-RFC5925-auth-option/20210825-053714 base: 3a62c333497b164868fdcd241842a1dd4e331825 config: arc-allyesconfig (attached as .config) compiler: arceb-elf-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross
# https://github.com/0day-ci/linux/commit/8666d8497484cd6cb4b94bca9097506d886e9c46
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Leonard-Crestez/tcp-Initial-support-for-RFC5925-auth-option/20210825-053714 git checkout 8666d8497484cd6cb4b94bca9097506d886e9c46
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross ARCH=arc
If you fix the issue, kindly add following tag as appropriate
All warnings (new ones prefixed by >>):
net/ipv4/tcp_authopt.c: In function 'tcp_authopt_shash_traffic_key':
>> net/ipv4/tcp_authopt.c:428:42: warning: variable 'daddr' set but not used [-Wunused-but-set-variable]
428 | struct in6_addr *daddr;
| ^~~~~
>> net/ipv4/tcp_authopt.c:427:42: warning: variable 'saddr' set but not used [-Wunused-but-set-variable]
427 | struct in6_addr *saddr;
| ^~~~~
net/ipv4/tcp_authopt.c: At top level:
>> net/ipv4/tcp_authopt.c:820:5: warning: no previous prototype for '__tcp_authopt_calc_mac' [-Wmissing-prototypes] 820 | int __tcp_authopt_calc_mac(struct sock *sk,
| ^~~~~~~~~~~~~~~~~~~~~~
net/ipv4/tcp_authopt.c:117:13: warning: 'tcp_authopt_alg_incref' defined but not used [-Wunused-function]
117 | static void tcp_authopt_alg_incref(struct tcp_authopt_alg_imp *alg)
| ^~~~~~~~~~~~~~~~~~~~~~
vim +/daddr +428 net/ipv4/tcp_authopt.c
397
398 /* feed traffic key into shash */
399 static int tcp_authopt_shash_traffic_key(struct shash_desc *desc,
400 struct sock *sk,
401 struct sk_buff *skb,
402 bool input,
403 bool ipv6)
404 {
405 struct tcphdr *th = tcp_hdr(skb);
406 int err;
407 __be32 sisn, disn;
408 __be16 digestbits = htons(crypto_shash_digestsize(desc->tfm) * 8);
409
410 // RFC5926 section 3.1.1.1
411 err = crypto_shash_update(desc, "\x01TCP-AO", 7);
412 if (err)
413 return err;
414
415 /* Addresses from packet on input and from socket on output
416 * This is because on output MAC is computed before prepending IP header
417 */
418 if (input) {
419 if (ipv6)
420 err = crypto_shash_update(desc, (u8 *)&ipv6_hdr(skb)->saddr, 32);
421 else
422 err = crypto_shash_update(desc, (u8 *)&ip_hdr(skb)->saddr, 8);
423 if (err)
424 return err;
425 } else {
426 if (ipv6) {
> 427 struct in6_addr *saddr; > 428 struct in6_addr *daddr;
429
430 saddr = &sk->sk_v6_rcv_saddr;
431 daddr = &sk->sk_v6_daddr;
432 err = crypto_shash_update(desc, (u8 *)&sk->sk_v6_rcv_saddr, 16);
433 if (err)
434 return err;
435 err = crypto_shash_update(desc, (u8 *)&sk->sk_v6_daddr, 16);
436 if (err)
437 return err;
438 } else {
439 err = crypto_shash_update(desc, (u8 *)&sk->sk_rcv_saddr, 4);
440 if (err)
441 return err;
442 err = crypto_shash_update(desc, (u8 *)&sk->sk_daddr, 4);
443 if (err)
444 return err;
445 }
446 }
447
448 /* TCP ports from header */
449 err = crypto_shash_update(desc, (u8 *)&th->source, 4);
450 if (err)
451 return err;
452
453 /* special cases for SYN and SYN/ACK */
454 if (th->syn && !th->ack) {
455 sisn = th->seq;
456 disn = 0;
457 } else if (th->syn && th->ack) {
458 sisn = th->seq;
459 disn = htonl(ntohl(th->ack_seq) - 1);
460 } else {
461 struct tcp_authopt_info *authopt_info;
462
463 /* Fetching authopt_info like this means it's possible that authopt_info
464 * was deleted while we were hashing. If that happens we drop the packet
465 * which should be fine.
466 *
467 * A better solution might be to always pass info as a parameter, or
468 * compute traffic_key for established sockets separately.
469 */
470 rcu_read_lock();
471 authopt_info = rcu_dereference(tcp_sk(sk)->authopt_info);
472 if (!authopt_info) {
473 rcu_read_unlock();
474 return -EINVAL;
475 }
476 /* Initial sequence numbers for ESTABLISHED connections from info */
477 if (input) {
478 sisn = htonl(authopt_info->dst_isn);
479 disn = htonl(authopt_info->src_isn);
480 } else {
481 sisn = htonl(authopt_info->src_isn);
482 disn = htonl(authopt_info->dst_isn);
483 }
484 rcu_read_unlock();
485 }
486
487 err = crypto_shash_update(desc, (u8 *)&sisn, 4);
488 if (err)
489 return err;
490 err = crypto_shash_update(desc, (u8 *)&disn, 4);
491 if (err)
492 return err;
493
494 err = crypto_shash_update(desc, (u8 *)&digestbits, 2);
495 if (err)
496 return err;
497
498 return 0;
499 }
500
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org
kernel test robot
build botRe: [RFCv3 06/15] tcp: authopt: Compute packet signatures
In reply to Leonard Crestezreproduce:
Hi Leonard,
[FYI, it's a private test report for your RFC patch.] [auto build test WARNING on 3a62c333497b164868fdcd241842a1dd4e331825]
url: https://github.com/0day-ci/linux/commits/Leonard-Crestez/tcp-Initial-support-for-RFC5925-auth-option/20210825-053714 base: 3a62c333497b164868fdcd241842a1dd4e331825 config: x86_64-rhel-8.3-kselftests (attached as .config) compiler: gcc-9 (Debian 9.3.0-22) 9.3.0 reproduce: # apt-get install sparse # sparse version: v0.6.3-348-gf0e6938b-dirty # https://github.com/0day-ci/linux/commit/8666d8497484cd6cb4b94bca9097506d886e9c46 git remote add linux-review https://github.com/0day-ci/linux git fetch --no-tags linux-review Leonard-Crestez/tcp-Initial-support-for-RFC5925-auth-option/20210825-053714 git checkout 8666d8497484cd6cb4b94bca9097506d886e9c46 # save the attached .config to linux build tree make W=1 C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' ARCH=x86_64
If you fix the issue, kindly add following tag as appropriate
sparse warnings: (new ones prefixed by >>)
>> net/ipv4/tcp_authopt.c:606:16: sparse: sparse: incorrect type in assignment (different base types) @@ expected unsigned int @@ got restricted __be32 [usertype] @@ net/ipv4/tcp_authopt.c:606:16: sparse: expected unsigned int net/ipv4/tcp_authopt.c:606:16: sparse: got restricted __be32 [usertype] net/ipv4/tcp_authopt.c:607:16: sparse: sparse: incorrect type in assignment (different base types) @@ expected unsigned int @@ got restricted __be32 [usertype] @@ net/ipv4/tcp_authopt.c:607:16: sparse: expected unsigned int net/ipv4/tcp_authopt.c:607:16: sparse: got restricted __be32 [usertype] >> net/ipv4/tcp_authopt.c:820:5: sparse: sparse: symbol '__tcp_authopt_calc_mac' was not declared. Should it be static?
net/ipv4/tcp_authopt.c:128:9: sparse: sparse: context imbalance in 'tcp_authopt_alg_get_tfm' - wrong count at exit net/ipv4/tcp_authopt.c:134:9: sparse: sparse: context imbalance in 'tcp_authopt_alg_put_tfm' - unexpected unlock
Please review and possibly fold the followup patch.
vim +606 net/ipv4/tcp_authopt.c
597
598 static int tcp_authopt_hash_tcp6_pseudoheader(struct shash_desc *desc,
599 struct in6_addr *saddr,
600 struct in6_addr *daddr,
601 u32 plen)
602 {
603 int err;
604 u32 buf[2];
605
> 606 buf[0] = htonl(plen); 607 buf[1] = htonl(IPPROTO_TCP);
608
609 err = crypto_shash_update(desc, (u8 *)saddr, sizeof(*saddr));
610 if (err)
611 return err;
612 err = crypto_shash_update(desc, (u8 *)daddr, sizeof(*daddr));
613 if (err)
614 return err;
615 return crypto_shash_update(desc, (u8 *)&buf, sizeof(buf));
616 }
617
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org
kernel test robot
build bot[RFC PATCH] tcp: authopt: __tcp_authopt_calc_mac() can be static
In reply to Leonard Cresteznet/ipv4/tcp_authopt.c:820:5: warning: symbol '__tcp_authopt_calc_mac' was not declared. Should it be static?
net/ipv4/tcp_authopt.c:820:5: warning: symbol '__tcp_authopt_calc_mac' was not declared. Should it be static?
--- tcp_authopt.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
net/ipv4/tcp_authopt.c +5 -5
@@ -817,11 +817,11 @@ static int tcp_authopt_hash_packet(struct crypto_shash *tfm,return crypto_shash_final (desc, macbuf); } -int __tcp_authopt_calc_mac (struct sock *sk, -struct sk_buff *skb, -struct tcp_authopt_key_info *key, -bool input, -char *macbuf) +static int __tcp_authopt_calc_mac (struct sock *sk, +struct sk_buff *skb, +struct tcp_authopt_key_info *key, +bool input, +char *macbuf) {struct crypto_shash *mac_tfm;u8 traffic_key[TCP_AUTHOPT_MAX_TRAFFIC_KEY_LEN ];
kernel test robot
build botRe: [RFCv3 06/15] tcp: authopt: Compute packet signatures
In reply to Leonard Crestezreproduce:
Hi Leonard,
[FYI, it's a private test report for your RFC patch.] [auto build test WARNING on 3a62c333497b164868fdcd241842a1dd4e331825]
url: https://github.com/0day-ci/linux/commits/Leonard-Crestez/tcp-Initial-support-for-RFC5925-auth-option/20210825-053714 base: 3a62c333497b164868fdcd241842a1dd4e331825 config: x86_64-rhel-8.3-kselftests (attached as .config) compiler: gcc-9 (Debian 9.3.0-22) 9.3.0 reproduce: # apt-get install sparse # sparse version: v0.6.3-348-gf0e6938b-dirty # https://github.com/0day-ci/linux/commit/8666d8497484cd6cb4b94bca9097506d886e9c46 git remote add linux-review https://github.com/0day-ci/linux git fetch --no-tags linux-review Leonard-Crestez/tcp-Initial-support-for-RFC5925-auth-option/20210825-053714 git checkout 8666d8497484cd6cb4b94bca9097506d886e9c46 # save the attached .config to linux build tree make W=1 C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' ARCH=x86_64
If you fix the issue, kindly add following tag as appropriate
sparse warnings: (new ones prefixed by >>)
>> net/ipv4/tcp_authopt.c:606:16: sparse: sparse: incorrect type in assignment (different base types) @@ expected unsigned int @@ got restricted __be32 [usertype] @@ net/ipv4/tcp_authopt.c:606:16: sparse: expected unsigned int net/ipv4/tcp_authopt.c:606:16: sparse: got restricted __be32 [usertype] net/ipv4/tcp_authopt.c:607:16: sparse: sparse: incorrect type in assignment (different base types) @@ expected unsigned int @@ got restricted __be32 [usertype] @@ net/ipv4/tcp_authopt.c:607:16: sparse: expected unsigned int net/ipv4/tcp_authopt.c:607:16: sparse: got restricted __be32 [usertype] >> net/ipv4/tcp_authopt.c:820:5: sparse: sparse: symbol '__tcp_authopt_calc_mac' was not declared. Should it be static?
net/ipv4/tcp_authopt.c:128:9: sparse: sparse: context imbalance in 'tcp_authopt_alg_get_tfm' - wrong count at exit net/ipv4/tcp_authopt.c:134:9: sparse: sparse: context imbalance in 'tcp_authopt_alg_put_tfm' - unexpected unlock
vim +606 net/ipv4/tcp_authopt.c
597
598 static int tcp_authopt_hash_tcp6_pseudoheader(struct shash_desc *desc,
599 struct in6_addr *saddr,
600 struct in6_addr *daddr,
601 u32 plen)
602 {
603 int err;
604 u32 buf[2];
605
> 606 buf[0] = htonl(plen); 607 buf[1] = htonl(IPPROTO_TCP);
608
609 err = crypto_shash_update(desc, (u8 *)saddr, sizeof(*saddr));
610 if (err)
611 return err;
612 err = crypto_shash_update(desc, (u8 *)daddr, sizeof(*daddr));
613 if (err)
614 return err;
615 return crypto_shash_update(desc, (u8 *)&buf, sizeof(buf));
616 }
617
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org
RFC v3 7/15 tcp: authopt: Hook into tcp core
In reply to Leonard CrestezThe 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.
Add calls into tcp authopt from send, receive and accept code.
--- include/net/tcp_authopt.h | 56 +++++++++ net/ipv4/tcp_authopt.c | 246 ++++++++++++++++++++++++++++++++++++++ net/ipv4/tcp_input.c | 17 +++ net/ipv4/tcp_ipv4.c | 3 + net/ipv4/tcp_minisocks.c | 2 + net/ipv4/tcp_output.c | 74 +++++++++++- net/ipv6/tcp_ipv6.c | 4 + 7 files changed, 401 insertions(+), 1 deletion(-)
include/net/tcp_authopt.h +56 -0
@@ -21,10 +21,11 @@ struct tcp_authopt_key_info {/* Wire identifiers */ u8 send_id, recv_id;u8 alg_id;u8 keylen;u8 key[TCP_AUTHOPT_MAXKEYLEN ]; +u8 maclen;struct sockaddr_storage addr;struct tcp_authopt_alg_imp *alg; };/**
@@ -41,15 +42,53 @@ struct tcp_authopt_info {u32 src_isn;u32 dst_isn; };#ifdef CONFIG_TCP_AUTHOPT +struct tcp_authopt_key_info *tcp_authopt_select_key (const struct sock *sk, +const struct sock *addr_sk, +u8 *rnextkeyid);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); +int tcp_authopt_hash ( +char *hash_location, +struct tcp_authopt_key_info *key, +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); +} +int __tcp_authopt_inbound_check ( +struct sock *sk, +struct sk_buff *skb, +struct tcp_authopt_info *info); +static inline int tcp_authopt_inbound_check (struct sock *sk,struct sk_buff *skb) +{ +struct tcp_authopt_info *info =rcu_dereference (tcp_sk (sk)->authopt_info); + +if (info) +return __tcp_authopt_inbound_check (sk, skb, info); +else +return 0 ; +}#else +static inline struct tcp_authopt_key_info *tcp_authopt_select_key ( +const struct sock *sk, +const struct sock *addr_sk, +u8 *rnextkeyid) +{ +return NULL ; +}static inline int tcp_set_authopt (struct sock *sk,sockptr_t optval,unsigned int optlen) {return -ENOPROTOOPT ; }static inline int tcp_get_authopt_val (struct sock *sk,struct tcp_authopt *key)
@@ -61,8 +100,25 @@ 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 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 int tcp_authopt_inbound_check (struct sock *sk,struct sk_buff *skb) +{ +return 0 ; +}#endif #endif /* _LINUX_TCP_AUTHOPT_H */
net/ipv4/tcp_authopt.c +246 -0
287 lines not on this page · raw message
net/ipv4/tcp_input.c +17 -0
@@ -70,10 +70,11 @@#include <linux/sysctl.h> #include <linux/kernel.h> #include <linux/prefetch.h> #include <net/dst.h> #include <net/tcp.h> +#include <net/tcp_authopt.h> #include <net/inet_common.h> #include <linux/ipsec.h> #include <asm/unaligned.h> #include <linux/errqueue.h> #include <trace/events/tcp.h>
@@ -5967,18 +5968,34 @@ void tcp_init_transfer(struct sock *sk, int bpf_op, struct sk_buff *skb)if (!icsk->icsk_ca_initialized)tcp_init_congestion_control (sk);tcp_init_buffer_space (sk); } +static void tcp_authopt_finish_connect (struct sock *sk,struct sk_buff *skb) +{ +#ifdef CONFIG_TCP_AUTHOPT +struct tcp_authopt_info *info; + + info =rcu_dereference_protected (tcp_sk (sk)->authopt_info,lockdep_sock_is_held (sk)); +if (!info) +return ; + + info->src_isn =ntohl (tcp_hdr (skb)->ack_seq) -1 ; + info->dst_isn =ntohl (tcp_hdr (skb)->seq); +#endif +} +void tcp_finish_connect (struct sock *sk,struct sk_buff *skb) {struct tcp_sock *tp =tcp_sk (sk);struct inet_connection_sock *icsk =inet_csk (sk);tcp_set_state (sk,TCP_ESTABLISHED ); icsk->icsk_ack.lrcvtime = tcp_jiffies32; +tcp_authopt_finish_connect (sk, skb); +if (skb) { icsk->icsk_af_ops->sk_rx_dst_set (sk, skb);security_inet_conn_established (sk, skb);sk_mark_napi_id (sk, skb); }
net/ipv4/tcp_ipv4.c +3 -0
@@ -2060,10 +2060,13 @@ int tcp_v4_rcv(struct sk_buff *skb) goto discard_and_relse;if (tcp_v4_inbound_md5_hash (sk, skb, dif, sdif)) goto discard_and_relse; +if (tcp_authopt_inbound_check (sk, skb)) + goto discard_and_relse; +nf_reset_ct (skb);if (tcp_filter (sk, skb)) goto discard_and_relse; th = (const struct tcphdr *)skb->data;
net/ipv4/tcp_minisocks.c +2 -0
@@ -24,10 +24,11 @@#include <linux/slab.h> #include <linux/sysctl.h> #include <linux/workqueue.h> #include <linux/static_key.h> #include <net/tcp.h> +#include <net/tcp_authopt.h> #include <net/inet_common.h> #include <net/xfrm.h> #include <net/busy_poll.h> static bool tcp_in_window (u32 seq,u32 end_seq,u32 s_win,u32 e_win)
@@ -539,10 +540,11 @@ struct sock *tcp_create_openreq_child(const struct sock *sk,#ifdef CONFIG_TCP_MD5SIG newtp->md5sig_info =NULL ;/*XXX*/ if (newtp->af_specific->md5_lookup (sk, newsk)) newtp->tcp_header_len +=TCPOLEN_MD5SIG_ALIGNED ;#endif +tcp_authopt_openreq (newsk, sk, req);if (skb->len >=TCP_MSS_DEFAULT + newtp->tcp_header_len) newicsk->icsk_ack.last_seg_size = skb->len - newtp->tcp_header_len; newtp->rx_opt.mss_clamp = req->mss;tcp_ecn_openreq_child (newtp, req); newtp->fastopen_req =NULL ;
net/ipv4/tcp_output.c +73 -1
211 lines not on this page · raw message
net/ipv6/tcp_ipv6.c +4 -0
@@ -40,10 +40,11 @@#include <linux/icmpv6.h> #include <linux/random.h> #include <linux/indirect_call_wrapper.h> #include <net/tcp.h> +#include <net/tcp_authopt.h> #include <net/ndisc.h> #include <net/inet6_hashtables.h> #include <net/inet6_connection_sock.h> #include <net/ipv6.h> #include <net/transp_v6.h>
@@ -1733,10 +1734,13 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb) goto discard_and_relse;if (tcp_v6_inbound_md5_hash (sk, skb, dif, sdif)) goto discard_and_relse; +if (tcp_authopt_inbound_check (sk, skb)) + goto discard_and_relse; +if (tcp_filter (sk, skb)) goto discard_and_relse; th = (const struct tcphdr *)skb->data; hdr =ipv6_hdr (skb);tcp_v6_fill_cb (skb, hdr, th);
Signature · 1 line
2.25.1
Re: [RFCv3 07/15] tcp: authopt: Hook into tcp core
Quoted 23 lines · Leonard Crestez
The
tcp_authoptfeatures 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.Add calls into tcp authopt from send, receive and accept code.
Signed-off-by Leonard Crestez <[email protected]>--- include/net/tcp_authopt.h | 56 +++++++++ net/ipv4/tcp_authopt.c | 246 ++++++++++++++++++++++++++++++++++++++ net/ipv4/tcp_input.c | 17 +++ net/ipv4/tcp_ipv4.c | 3 + net/ipv4/tcp_minisocks.c | 2 + net/ipv4/tcp_output.c | 74 +++++++++++- net/ipv6/tcp_ipv6.c | 4 + 7 files changed, 401 insertions(+), 1 deletion(-)include/net/tcp_authopt.h +0
-0
include/net/tcp_authopt.h struct tcp_authopt_key_info { · line 26
Quoted 6 lines
include/net/tcp_authopt.h +1
-0@@ -21,10 +21,11 @@ struct tcp_authopt_key_info {/* Wire identifiers */ u8 send_id, recv_id;u8 alg_id;u8 keylen;u8 key[TCP_AUTHOPT_MAXKEYLEN ]; +u8 maclen;
I do not see maclen being enforced to 12, or a multiple of 4 ?
This means that later [2], tcp_authopt_hash() will leave up to 3 unitialized bytes in the TCP options, sent to the wire.
This is a security issue, since we will leak kernel memory.
Quoted 100 lines
include/net/tcp_authopt.h +55
-0struct sockaddr_storage addr;struct tcp_authopt_alg_imp *alg; };/** @@ -41,15 +42,53 @@ struct tcp_authopt_info {u32 src_isn;u32 dst_isn; };#ifdef CONFIG_TCP_AUTHOPT +struct tcp_authopt_key_info *tcp_authopt_select_key (const struct sock *sk, +const struct sock *addr_sk, +u8 *rnextkeyid);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); +int tcp_authopt_hash ( +char *hash_location, +struct tcp_authopt_key_info *key, +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); +} +int __tcp_authopt_inbound_check ( +struct sock *sk, +struct sk_buff *skb, +struct tcp_authopt_info *info); +static inline int tcp_authopt_inbound_check (struct sock *sk,struct sk_buff *skb) +{ +struct tcp_authopt_info *info =rcu_dereference (tcp_sk (sk)->authopt_info); + +if (info) +return __tcp_authopt_inbound_check (sk, skb, info); +else +return 0 ; +}#else +static inline struct tcp_authopt_key_info *tcp_authopt_select_key ( +const struct sock *sk, +const struct sock *addr_sk, +u8 *rnextkeyid) +{ +return NULL ; +}static inline int tcp_set_authopt (struct sock *sk,sockptr_t optval,unsigned int optlen) {return -ENOPROTOOPT ; }static inline int tcp_get_authopt_val (struct sock *sk,struct tcp_authopt *key)@@ -61,8 +100,25 @@ 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 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 int tcp_authopt_inbound_check (struct sock *sk,struct sk_buff *skb) +{ +return 0 ; +}#endif #endif /* _LINUX_TCP_AUTHOPT_H */ net/ipv4/tcp_authopt.c +5
-0@@ -203,10 +203,71 @@ static struct tcp_authopt_key_info *tcp_authopt_key_lookup_exact(const struct soreturn key_info;return NULL ; } +struct tcp_authopt_key_info *tcp_authopt_lookup_send (struct tcp_authopt_info *info, +const struct sock *addr_sk, +int send_id) +{ +struct tcp_authopt_key_info *result =NULL ;
net/ipv4/tcp_authopt.c static struct tcp_authopt_key_info *tcp_authopt_key_lookup_exact(const struct so · line 222
Quoted 10 lines
net/ipv4/tcp_authopt.c +10
-0+struct tcp_authopt_key_info *key; + +hlist_for_each_entry_rcu (key, &info->head, node,0 ) { +if (send_id >=0 && key->send_id != send_id) +continue ; +if (key->flags &TCP_AUTHOPT_KEY_ADDR_BIND ) { +if (addr_sk->sk_family ==AF_INET ) { +struct sockaddr_in *key_addr = (struct sockaddr_in *)&key->addr; +const struct in_addr *daddr = + (const struct in_addr *)&addr_sk->sk_daddr;
Why a cast is needed ? sk_daddr is a __be32, no need to cast it to in_addr
net/ipv4/tcp_authopt.c static struct tcp_authopt_key_info *tcp_authopt_key_lookup_exact(const struct so · line 224
Quoted 2 lines
net/ipv4/tcp_authopt.c +2
-0+ +if (WARN_ON (key_addr->sin_family !=AF_INET ))
Why a WARN_ON() is used ? If we expect this to trigger, then at minimumum WARN_ON_ONCE() please.
net/ipv4/tcp_authopt.c static struct tcp_authopt_key_info *tcp_authopt_key_lookup_exact(const struct so · line 227
Quoted 3 lines
net/ipv4/tcp_authopt.c +3
-0+continue ; +if (memcmp (daddr, &key_addr->sin_addr,sizeof (*daddr))) +continue ;
Using memcmp() to compare two __be32 is overkill.
net/ipv4/tcp_authopt.c static struct tcp_authopt_key_info *tcp_authopt_key_lookup_exact(const struct so · line 231
Quoted 4 lines
net/ipv4/tcp_authopt.c +4
-0+ } +if (addr_sk->sk_family ==AF_INET6 ) { +struct sockaddr_in6 *key_addr = (struct sockaddr_in6 *)&key->addr; +const struct in6_addr *daddr = &addr_sk->sk_v6_daddr;
Not sure why a variable is used, you need it once.
net/ipv4/tcp_authopt.c static struct tcp_authopt_key_info *tcp_authopt_key_lookup_exact(const struct so · line 235
Quoted 4 lines
net/ipv4/tcp_authopt.c +4
-0+ +if (WARN_ON (key_addr->sin6_family !=AF_INET6 )) +continue ; +if (memcmp (daddr, &key_addr->sin6_addr,sizeof (*daddr)))
ipv6_addr_equal() should be faster.
Quoted 17 lines
net/ipv4/tcp_authopt.c +17
-0+continue ; + } + } +if (result &&net_ratelimit ()) +pr_warn ("ambiguous tcp authentication keys configured for send\n" ); + result = key; + } + +return result; +} + +/** +* tcp_authopt_select_key - select key for sending +* +* addr_sk is the sock used for comparing daddr, it is only different from sk in +* the synack case. +*
net/ipv4/tcp_authopt.c static struct tcp_authopt_key_info *tcp_authopt_key_lookup_exact(const struct so · line 262
Quoted 10 lines
net/ipv4/tcp_authopt.c +10
-0+* Result is protected by RCU and can't be stored, it may only be passed to +* tcp_authopt_hash and only under a single rcu_read_lock. +*/ +struct tcp_authopt_key_info *tcp_authopt_select_key (const struct sock *sk, +const struct sock *addr_sk, +u8 *rnextkeyid) +{ +struct tcp_authopt_info *info; + + info =rcu_dereference (tcp_sk (sk)->authopt_info);
distro kernels will have CONFIG_TCP_AUTHOPT set, meaning that we will add a cache line miss for every incoming TCP packet even on hosts not using any RFC5925 TCP flow.
For TCP MD5 we are using a static key, to avoid this extra cost.
Quoted 49 lines
net/ipv4/tcp_authopt.c +32
-0+if (!info) +return NULL ; + +return tcp_authopt_lookup_send (info, addr_sk,-1 ); +} +static struct tcp_authopt_info *__tcp_authopt_info_get_or_create (struct sock *sk) {struct tcp_sock *tp =tcp_sk (sk);struct tcp_authopt_info *info;@@ -387,16 +448,69 @@ int tcp_set_authopt_key(struct sock *sk, sockptr_t optval, unsigned int optlen) key_info->recv_id = opt.recv_id; key_info->alg_id = opt.alg; key_info->alg = alg; key_info->keylen = opt.keylen;memcpy (key_info->key, opt.key, opt.keylen); + key_info->maclen = alg->maclen;memcpy (&key_info->addr, &opt.addr,sizeof (key_info->addr));hlist_add_head_rcu (&key_info->node, &info->head);return 0 ; } +static int tcp_authopt_clone_keys (struct sock *newsk, +const struct sock *oldsk, +struct tcp_authopt_info *new_info, +struct tcp_authopt_info *old_info) +{ +struct tcp_authopt_key_info *old_key; +struct tcp_authopt_key_info *new_key; + +hlist_for_each_entry_rcu (old_key, &old_info->head, node,lockdep_sock_is_held (sk)) { + new_key =sock_kmalloc (newsk,sizeof (*new_key),GFP_ATOMIC ); +if (!new_key) +return -ENOMEM ; +memcpy (new_key, old_key,sizeof (*new_key)); +tcp_authopt_alg_incref (old_key->alg); +hlist_add_head_rcu (&new_key->node, &new_info->head); + } + +return 0 ; +} + +/** Called to create accepted sockets. +* +* Need to copy authopt info from listen socket. +*/ +int __tcp_authopt_openreq (struct sock *newsk,const struct sock *oldsk,struct request_sock *req)
net/ipv4/tcp_authopt.c int tcp_set_authopt_key(struct sock *sk, sockptr_t optval, unsigned int optlen) · line 494
Quoted 10 lines
net/ipv4/tcp_authopt.c +10
-0+{ +struct tcp_authopt_info *old_info; +struct tcp_authopt_info *new_info; +int err; + + old_info =rcu_dereference (tcp_sk (oldsk)->authopt_info); +if (!old_info) +return 0 ; + + new_info =kmalloc (sizeof (*new_info),GFP_ATOMIC | __GFP_ZERO);
kzalloc() is your friend. (same remark for your other patches, where you are using __GFP_ZERO) Also see additional comment [1]
net/ipv4/tcp_authopt.c int tcp_set_authopt_key(struct sock *sk, sockptr_t optval, unsigned int optlen) · line 504
Quoted 10 lines
net/ipv4/tcp_authopt.c +10
-0+if (!new_info) +return -ENOMEM ; + +sk_nocaps_add (newsk,NETIF_F_GSO_MASK ); + new_info->src_isn =tcp_rsk (req)->snt_isn; + new_info->dst_isn =tcp_rsk (req)->rcv_isn; +INIT_HLIST_HEAD (&new_info->head); + err =tcp_authopt_clone_keys (newsk, oldsk, new_info, old_info); +if (err) { +__tcp_authopt_info_free (newsk, new_info);
[1]
Are we leaving in place old value of newsk->authopt_info ? If this is copied from the listener, I think you need to add a tcp_sk(newsk)->authopt_info = NULL; before the kzalloc() call done above.
Quoted 54 lines
net/ipv4/tcp_authopt.c +32
-0+return err; + } +rcu_assign_pointer (tcp_sk (newsk)->authopt_info, new_info); + +return 0 ; +} +/* feed traffic key into shash */ static int tcp_authopt_shash_traffic_key(struct shash_desc *desc, struct sock *sk, struct sk_buff *skb, bool input,@@ -815,10 +929,16 @@ static int tcp_authopt_hash_packet(struct crypto_shash *tfm,return err;return crypto_shash_final (desc, macbuf); } +/** +* __tcp_authopt_calc_mac - Compute packet MAC using key +* +* @macbuf: output buffer. Must be large enough to fit the digestsize of the +* underlying transform before truncation. Please use TCP_AUTHOPT_MAXMACBUF +*/ int __tcp_authopt_calc_mac (struct sock *sk,struct sk_buff *skb,struct tcp_authopt_key_info *key,bool input,char *macbuf)@@ -859,5 +979,131 @@ int __tcp_authopt_calc_mac(struct sock *sk, out:tcp_authopt_put_mac_shash (key, mac_tfm);return err; } + +/** +* tcp_authopt_hash - fill in the mac +* +* The key must come from tcp_authopt_select_key. +*/ +int tcp_authopt_hash (char *hash_location, +struct tcp_authopt_key_info *key, +struct sock *sk, +struct sk_buff *skb) +{ +/* MAC inside option is truncated to 12 bytes but crypto API needs output +* buffer to be large enough so we use a buffer on the stack. +*/ +u8 macbuf[TCP_AUTHOPT_MAXMACBUF ]; +int err; + +if (WARN_ON (key->maclen >sizeof (macbuf))) +return -ENOBUFS ;
net/ipv4/tcp_authopt.c int __tcp_authopt_calc_mac(struct sock *sk, · line 1012
Quoted 10 lines
net/ipv4/tcp_authopt.c +10
-0+ + err =__tcp_authopt_calc_mac (sk, skb, key, false, macbuf); +if (err) { +/* If mac calculation fails and caller doesn't handle the error +* try to make it obvious inside the packet. +*/ +memset (hash_location,0 , key->maclen); +return err; + } +memcpy (hash_location, macbuf, key->maclen);
[2]
This is the place were we do not make sure to clear the padding bytes (if key->maclen is not a multiple of 4)
Quoted 453 lines
net/ipv4/tcp_authopt.c +97
-0+ +return 0 ; +} + +static struct tcp_authopt_key_info *tcp_authopt_lookup_recv (struct sock *sk, +struct sk_buff *skb, +struct tcp_authopt_info *info, +int recv_id) +{ +struct tcp_authopt_key_info *result =NULL ; +struct tcp_authopt_key_info *key; + +/* multiple matches will cause occasional failures */ +hlist_for_each_entry_rcu (key, &info->head, node,0 ) { +if (recv_id >=0 && key->recv_id != recv_id) +continue ; +if (key->flags &TCP_AUTHOPT_KEY_ADDR_BIND ) { +if (sk->sk_family ==AF_INET ) { +struct sockaddr_in *key_addr = (struct sockaddr_in *)&key->addr; +struct iphdr *iph = (struct iphdr *)skb_network_header (skb); + +if (WARN_ON (key_addr->sin_family !=AF_INET )) +continue ; +if (WARN_ON (iph->version !=4 )) +continue ; +if (memcmp (&iph->saddr, &key_addr->sin_addr,sizeof (iph->saddr))) +continue ; + } +if (sk->sk_family ==AF_INET6 ) { +struct sockaddr_in6 *key_addr = (struct sockaddr_in6 *)&key->addr; +struct ipv6hdr *iph = (struct ipv6hdr *)skb_network_header (skb); + +if (WARN_ON (key_addr->sin6_family !=AF_INET6 )) +continue ; +if (WARN_ON (iph->version !=6 )) +continue ; +if (memcmp (&iph->saddr, &key_addr->sin6_addr,sizeof (iph->saddr))) +continue ; + } + } +if (result &&net_ratelimit ()) +pr_warn ("ambiguous tcp authentication keys configured for receive\n" ); + result = key; + } + +return result; +} + +int __tcp_authopt_inbound_check (struct sock *sk,struct sk_buff *skb,struct tcp_authopt_info *info) +{ +struct tcphdr *th = (struct tcphdr *)skb_transport_header (skb); +struct tcphdr_authopt *opt; +struct tcp_authopt_key_info *key; +u8 macbuf[TCP_AUTHOPT_MAXMACBUF ]; +int err; + + opt = (struct tcphdr_authopt *)tcp_authopt_find_option (th); + key =tcp_authopt_lookup_recv (sk, skb, info, opt ? opt->keyid :-1 ); + +/* nothing found or expected */ +if (!opt && !key) +return 0 ; +if (!opt && key) { +net_info_ratelimited ("TCP Authentication Missing\n" ); +return -EINVAL ; + } +if (opt && !key) { +/* RFC5925 Section 7.3: +* A TCP-AO implementation MUST allow for configuration of the behavior +* of segments with TCP-AO but that do not match an MKT. The initial +* default of this configuration SHOULD be to silently accept such +* connections. +*/ +if (info->flags &TCP_AUTHOPT_FLAG_REJECT_UNEXPECTED ) { +net_info_ratelimited ("TCP Authentication Unexpected: Rejected\n" ); +return -EINVAL ; + }else { +net_info_ratelimited ("TCP Authentication Unexpected: Accepted\n" ); +return 0 ; + } + } + +/* bad inbound key len */ +if (key->maclen +4 != opt->len) +return -EINVAL ; + + err =__tcp_authopt_calc_mac (sk, skb, key, true, macbuf); +if (err) +return err; + +if (memcmp (macbuf, opt->mac, key->maclen)) { +net_info_ratelimited ("TCP Authentication Failed\n" ); +return -EINVAL ; + } + +return 0 ; +}net/ipv4/tcp_input.c +17
-0@@ -70,10 +70,11 @@#include <linux/sysctl.h> #include <linux/kernel.h> #include <linux/prefetch.h> #include <net/dst.h> #include <net/tcp.h> +#include <net/tcp_authopt.h> #include <net/inet_common.h> #include <linux/ipsec.h> #include <asm/unaligned.h> #include <linux/errqueue.h> #include <trace/events/tcp.h> @@ -5967,18 +5968,34 @@ void tcp_init_transfer(struct sock *sk, int bpf_op, struct sk_buff *skb)if (!icsk->icsk_ca_initialized)tcp_init_congestion_control (sk);tcp_init_buffer_space (sk); } +static void tcp_authopt_finish_connect (struct sock *sk,struct sk_buff *skb) +{ +#ifdef CONFIG_TCP_AUTHOPT +struct tcp_authopt_info *info; + + info =rcu_dereference_protected (tcp_sk (sk)->authopt_info,lockdep_sock_is_held (sk)); +if (!info) +return ; + + info->src_isn =ntohl (tcp_hdr (skb)->ack_seq) -1 ; + info->dst_isn =ntohl (tcp_hdr (skb)->seq); +#endif +} +void tcp_finish_connect (struct sock *sk,struct sk_buff *skb) {struct tcp_sock *tp =tcp_sk (sk);struct inet_connection_sock *icsk =inet_csk (sk);tcp_set_state (sk,TCP_ESTABLISHED ); icsk->icsk_ack.lrcvtime = tcp_jiffies32; +tcp_authopt_finish_connect (sk, skb); +if (skb) { icsk->icsk_af_ops->sk_rx_dst_set (sk, skb);security_inet_conn_established (sk, skb);sk_mark_napi_id (sk, skb); }net/ipv4/tcp_ipv4.c +3
-0@@ -2060,10 +2060,13 @@ int tcp_v4_rcv(struct sk_buff *skb) goto discard_and_relse;if (tcp_v4_inbound_md5_hash (sk, skb, dif, sdif)) goto discard_and_relse; +if (tcp_authopt_inbound_check (sk, skb)) + goto discard_and_relse; +nf_reset_ct (skb);if (tcp_filter (sk, skb)) goto discard_and_relse; th = (const struct tcphdr *)skb->data;net/ipv4/tcp_minisocks.c +2
-0@@ -24,10 +24,11 @@#include <linux/slab.h> #include <linux/sysctl.h> #include <linux/workqueue.h> #include <linux/static_key.h> #include <net/tcp.h> +#include <net/tcp_authopt.h> #include <net/inet_common.h> #include <net/xfrm.h> #include <net/busy_poll.h> static bool tcp_in_window (u32 seq,u32 end_seq,u32 s_win,u32 e_win)@@ -539,10 +540,11 @@ struct sock *tcp_create_openreq_child(const struct sock *sk,#ifdef CONFIG_TCP_MD5SIG newtp->md5sig_info =NULL ;/*XXX*/ if (newtp->af_specific->md5_lookup (sk, newsk)) newtp->tcp_header_len +=TCPOLEN_MD5SIG_ALIGNED ;#endif +tcp_authopt_openreq (newsk, sk, req);if (skb->len >=TCP_MSS_DEFAULT + newtp->tcp_header_len) newicsk->icsk_ack.last_seg_size = skb->len - newtp->tcp_header_len; newtp->rx_opt.mss_clamp = req->mss;tcp_ecn_openreq_child (newtp, req); newtp->fastopen_req =NULL ;net/ipv4/tcp_output.c +73
-1@@ -37,10 +37,11 @@#define pr_fmt (fmt) "TCP: " fmt#include <net/tcp.h> #include <net/mptcp.h> +#include <net/tcp_authopt.h> #include <linux/compiler.h> #include <linux/gfp.h> #include <linux/module.h> #include <linux/static_key.h> @@ -411,10 +412,11 @@ static inline bool tcp_urg_mode(const struct tcp_sock *tp)#define OPTION_SACK_ADVERTISE (1 << 0)#define OPTION_TS (1 << 1)#define OPTION_MD5 (1 << 2)#define OPTION_WSCALE (1 << 3) +#define OPTION_AUTHOPT (1 << 4)#define OPTION_FAST_OPEN_COOKIE (1 << 8)#define OPTION_SMC (1 << 9)#define OPTION_MPTCP (1 << 10)static void smc_options_write (__be32 *ptr,u16 *options)@@ -435,16 +437,21 @@ static void smc_options_write(__be32 *ptr, u16 *options)struct tcp_out_options {u16 options;/* bit field of OPTION_* */ u16 mss;/* 0 to disable */ u8 ws;/* window scale, 0 to disable */ u8 num_sack_blocks;/* number of SACK blocks to include */ -u8 hash_size;/* bytes in hash_location */ u8 bpf_opt_len;/* length of BPF hdr option */ +#ifdef CONFIG_TCP_AUTHOPT +u8 authopt_rnextkeyid;/* rnextkey */ +#endif __u8 *hash_location;/* temporary pointer, overloaded */ __u32 tsval, tsecr;/* need to include OPTION_TS */ struct tcp_fastopen_cookie *fastopen_cookie;/* Fast open cookie */ struct mptcp_out_options mptcp; +#ifdef CONFIG_TCP_AUTHOPT +struct tcp_authopt_key_info *authopt_key; +#endif };static void mptcp_options_write (__be32 *ptr,const struct tcp_sock *tp,struct tcp_out_options *opts) {@@ -617,10 +624,24 @@ static void tcp_options_write(__be32 *ptr, struct tcp_sock *tp,/* overload cookie hash location */ opts->hash_location = (__u8 *)ptr; ptr +=4 ; } +#ifdef CONFIG_TCP_AUTHOPT +if (unlikely (OPTION_AUTHOPT & options)) { +struct tcp_authopt_key_info *key = opts->authopt_key; + +WARN_ON (!key); + *ptr++ =htonl ((TCPOPT_AUTHOPT <<24 ) | ((4 + key->maclen) <<16 ) | + (key->send_id <<8 ) | opts->authopt_rnextkeyid); +/* overload cookie hash location */ + opts->hash_location = (__u8 *)ptr; +/* maclen is currently always 12 but try to align nicely anyway. */ + ptr += (key->maclen +3 ) /4 ; + } +#endif +if (unlikely (opts->mss)) { *ptr++ =htonl ((TCPOPT_MSS <<24 ) | (TCPOLEN_MSS <<16 ) | opts->mss); }@@ -752,10 +773,28 @@ static void mptcp_set_option_cond(const struct request_sock *req, } } } } +static int tcp_authopt_init_options (const struct sock *sk, +const struct sock *addr_sk, +struct tcp_out_options *opts) +{ +#ifdef CONFIG_TCP_AUTHOPT +struct tcp_authopt_key_info *key; + + key =tcp_authopt_select_key (sk, addr_sk, &opts->authopt_rnextkeyid); +if (key) { + opts->options |=OPTION_AUTHOPT ; + opts->authopt_key = key; +return 4 + key->maclen; + } +#endif + +return 0 ; +} +/* Compute TCP options for SYN packets. This is not the final * network wire format yet. */ static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb, struct tcp_out_options *opts,@@ -774,10 +813,11 @@ static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb, opts->options |=OPTION_MD5 ; remaining -= TCPOLEN_MD5SIG_ALIGNED; } }#endif + remaining -=tcp_authopt_init_options (sk, sk, opts);/* We always get an MSS option. The option bytes which will be seen in * normal data packets should timestamps be used, must be in the MSS * advertised. But we subtract them from tp->mss_cache so that * calculations in tcp_sendmsg are simpler etc. So account for this @@ -862,10 +902,11 @@ static unsigned int tcp_synack_options(const struct sock *sk,*/ if (synack_type !=TCP_SYNACK_COOKIE ) ireq->tstamp_ok &= !ireq->sack_ok; }#endif + remaining -=tcp_authopt_init_options (sk,req_to_sk (req), opts);/* We always send an MSS option. */ opts->mss = mss; remaining -=TCPOLEN_MSS_ALIGNED ;@@ -930,10 +971,11 @@ static unsigned int tcp_established_options(struct sock *sk, struct sk_buff *skb opts->options |=OPTION_MD5 ; size += TCPOLEN_MD5SIG_ALIGNED; } }#endif + size +=tcp_authopt_init_options (sk, sk, opts);if (likely (tp->rx_opt.tstamp_ok)) { opts->options |=OPTION_TS ; opts->tsval = skb ?tcp_skb_timestamp (skb) + tp->tsoffset :0 ; opts->tsecr = tp->rx_opt.ts_recent;@@ -1277,10 +1319,14 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, inet =inet_sk (sk); tcb =TCP_SKB_CB (skb);memset (&opts,0 ,sizeof (opts)); +#ifdef CONFIG_TCP_AUTHOPT +/* for tcp_authopt_init_options inside tcp_syn_options or tcp_established_options */ +rcu_read_lock (); +#endif if (unlikely (tcb->tcp_flags &TCPHDR_SYN )) { tcp_options_size =tcp_syn_options (sk, skb, &opts, &md5); }else { tcp_options_size =tcp_established_options (sk, skb, &opts, &md5);@@ -1365,10 +1411,17 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb,sk_nocaps_add (sk,NETIF_F_GSO_MASK ); tp->af_specific->calc_md5_hash (opts.hash_location, md5, sk, skb); }#endif +#ifdef CONFIG_TCP_AUTHOPT +if (opts.authopt_key) { +sk_nocaps_add (sk,NETIF_F_GSO_MASK ); +tcp_authopt_hash (opts.hash_location, opts.authopt_key, sk, skb); + } +rcu_read_unlock (); +#endif /* BPF prog is the last one writing header option */ bpf_skops_write_hdr_opt (sk, skb,NULL ,NULL ,0 , &opts); INDIRECT_CALL_INET(icsk->icsk_af_ops->send_check,@@ -1836,12 +1889,21 @@ unsigned int tcp_current_mss(struct sock *sk)u32 mtu =dst_mtu (dst);if (mtu !=inet_csk (sk)->icsk_pmtu_cookie) mss_now =tcp_sync_mss (sk, mtu); } +#ifdef CONFIG_TCP_AUTHOPT +/* Even if the result is not used rcu_read_lock is required when scanning for +* tcp authentication keys. Otherwise lockdep will complain. +*/ +rcu_read_lock (); +#endif header_len =tcp_established_options (sk,NULL , &opts, &md5) +sizeof (struct tcphdr ); +#ifdef CONFIG_TCP_AUTHOPT +rcu_read_unlock (); +#endif /* The mss_cache is sized based on tp->tcp_header_len, which assumes * some common options. If this is an odd packet (because we have SACK * blocks etc) then our calculated header_len will be different, and * we have to adjust mss_now correspondingly */ if (header_len != tp->tcp_header_len) {@@ -3566,10 +3628,14 @@ struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst, }#ifdef CONFIG_TCP_MD5SIG rcu_read_lock (); md5 =tcp_rsk (req)->af_specific->req_md5_lookup (sk,req_to_sk (req)); +#endif +#ifdef CONFIG_TCP_AUTHOPT +/* for tcp_authopt_init_options inside tcp_synack_options */ +rcu_read_lock ();#endif skb_set_hash (skb,tcp_rsk (req)->txhash,PKT_HASH_TYPE_L4 );/* bpf program will be interested in the tcp_flags */ TCP_SKB_CB (skb)->tcp_flags =TCPHDR_SYN |TCPHDR_ACK ; tcp_header_size = tcp_synack_options(sk, req, mss, skb, &opts, md5,@@ -3603,10 +3669,16 @@ struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst,if (md5)tcp_rsk (req)->af_specific->calc_md5_hash (opts.hash_location, md5,req_to_sk (req), skb);rcu_read_unlock ();#endif +#ifdef CONFIG_TCP_AUTHOPT +/* If signature fails we do nothing */ +if (opts.authopt_key) +tcp_authopt_hash (opts.hash_location, opts.authopt_key,req_to_sk (req), skb); +rcu_read_unlock (); +#endif bpf_skops_write_hdr_opt ((struct sock *)sk, skb, req, syn_skb, synack_type, &opts); skb->skb_mstamp_ns = now;net/ipv6/tcp_ipv6.c +4
-0@@ -40,10 +40,11 @@#include <linux/icmpv6.h> #include <linux/random.h> #include <linux/indirect_call_wrapper.h> #include <net/tcp.h> +#include <net/tcp_authopt.h> #include <net/ndisc.h> #include <net/inet6_hashtables.h> #include <net/inet6_connection_sock.h> #include <net/ipv6.h> #include <net/transp_v6.h> @@ -1733,10 +1734,13 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb) goto discard_and_relse;if (tcp_v6_inbound_md5_hash (sk, skb, dif, sdif)) goto discard_and_relse; +if (tcp_authopt_inbound_check (sk, skb)) + goto discard_and_relse; +if (tcp_filter (sk, skb)) goto discard_and_relse; th = (const struct tcphdr *)skb->data; hdr =ipv6_hdr (skb);tcp_v6_fill_cb (skb, hdr, th);
Re: [RFCv3 07/15] tcp: authopt: Hook into tcp core
Quoted 19 lines · Eric Dumazet
Quoted 18 lines · Leonard Crestez
The
tcp_authoptfeatures 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.Add calls into tcp authopt from send, receive and accept code.
Signed-off-by Leonard Crestez <[email protected]>--- include/net/tcp_authopt.h | 56 +++++++++ net/ipv4/tcp_authopt.c | 246 ++++++++++++++++++++++++++++++++++++++ net/ipv4/tcp_input.c | 17 +++ net/ipv4/tcp_ipv4.c | 3 + net/ipv4/tcp_minisocks.c | 2 + net/ipv4/tcp_output.c | 74 +++++++++++- net/ipv6/tcp_ipv6.c | 4 + 7 files changed, 401 insertions(+), 1 deletion(-)
Quoted 13 lines
include/net/tcp_authopt.h struct tcp_authopt_key_info { · line 26
Quoted 11 lines
include/net/tcp_authopt.h +1
-0@@ -21,10 +21,11 @@ struct tcp_authopt_key_info {/* Wire identifiers */ u8 send_id, recv_id;u8 alg_id;u8 keylen;u8 key[TCP_AUTHOPT_MAXKEYLEN ]; +u8 maclen;I do not see maclen being enforced to 12, or a multiple of 4 ?
For both current algorithms the maclen value is 12. I just implemented RFC5926, there is no way to control this from userspace.
Quoted 4 lines
This means that later [2],
tcp_authopt_hash()will leave up to 3 unitialized bytes in the TCP options, sent to the wire.This is a security issue, since we will leak kernel memory.
Filling the remainder with zeroes does make sense, or at least WARN_ON(maclen != 4) so that it's obvious to anyone who attempts to extend the algorithms.
Quoted 35 lines
Quoted 5 lines
include/net/tcp_authopt.h +5
-0+struct tcp_authopt_key_info *tcp_authopt_lookup_send (struct tcp_authopt_info *info, +const struct sock *addr_sk, +int send_id) +{ +struct tcp_authopt_key_info *result =NULL ;include/net/tcp_authopt.h struct tcp_authopt_key_info {
Quoted 10 lines
include/net/tcp_authopt.h +10
-0+struct tcp_authopt_key_info *key; + +hlist_for_each_entry_rcu (key, &info->head, node,0 ) { +if (send_id >=0 && key->send_id != send_id) +continue ; +if (key->flags &TCP_AUTHOPT_KEY_ADDR_BIND ) { +if (addr_sk->sk_family ==AF_INET ) { +struct sockaddr_in *key_addr = (struct sockaddr_in *)&key->addr; +const struct in_addr *daddr = + (const struct in_addr *)&addr_sk->sk_daddr;Why a cast is needed ?
sk_daddris a__be32, no need to cast it toin_addrinclude/net/tcp_authopt.h
Quoted 2 lines
include/net/tcp_authopt.h +2
-0+ +if (WARN_ON (key_addr->sin_family !=AF_INET ))Why a
WARN_ON()is used ? If we expect this to trigger, then at minimumumWARN_ON_ONCE()please.include/net/tcp_authopt.h
Quoted 3 lines
include/net/tcp_authopt.h +3
-0+continue ; +if (memcmp (daddr, &key_addr->sin_addr,sizeof (*daddr))) +continue ;Using
memcmp()to compare two__be32is overkill.include/net/tcp_authopt.h
Quoted 4 lines
include/net/tcp_authopt.h +4
-0+ } +if (addr_sk->sk_family ==AF_INET6 ) { +struct sockaddr_in6 *key_addr = (struct sockaddr_in6 *)&key->addr; +const struct in6_addr *daddr = &addr_sk->sk_v6_daddr;Not sure why a variable is used, you need it once.
Quoted 6 lines
include/net/tcp_authopt.h
Quoted 4 lines
include/net/tcp_authopt.h +4
-0+ +if (WARN_ON (key_addr->sin6_family !=AF_INET6 )) +continue ; +if (memcmp (daddr, &key_addr->sin6_addr,sizeof (*daddr)))
ipv6_addr_equal()should be faster.
OK, I will replace the comparisons.
Checking address family is mostly paranoia on my part, I don't know if a real scenario exists for AF mismatch. Still need to check ipv4-mapped ipv6 addresses, not sure if those can receive ipv4 skbs on an ipv6 socket.
Quoted 12 lines
include/net/tcp_authopt.h
Quoted 7 lines
include/net/tcp_authopt.h +7
-0+struct tcp_authopt_key_info *tcp_authopt_select_key (const struct sock *sk, +const struct sock *addr_sk, +u8 *rnextkeyid) +{ +struct tcp_authopt_info *info; + + info =rcu_dereference (tcp_sk (sk)->authopt_info);distro kernels will have
CONFIG_TCP_AUTHOPTset, meaning that we will add a cache line miss for every incoming TCP packet even on hosts not using any RFC5925 TCP flow.
Quoted 1 line
For TCP MD5 we are using a static key, to avoid this extra cost.
OK, will add a static_key.
The check for "does socket have tcp_authopt" also belongs in an inline wrapper, similar to inbound check
Quoted 6 lines
Quoted 6 lines
include/net/tcp_authopt.h +6
-0+int __tcp_authopt_openreq (struct sock *newsk,const struct sock *oldsk,struct request_sock *req) +{ +struct tcp_authopt_info *old_info; +struct tcp_authopt_info *new_info; +int err; +
Quoted 8 lines
include/net/tcp_authopt.h
Quoted 5 lines
include/net/tcp_authopt.h +5
-0+ old_info =rcu_dereference (tcp_sk (oldsk)->authopt_info); +if (!old_info) +return 0 ; + + new_info =kmalloc (sizeof (*new_info),GFP_ATOMIC | __GFP_ZERO);
kzalloc()is your friend. (same remark for your other patches, where you are using__GFP_ZERO) Also see additional comment [1]
OK
Quoted 16 lines
include/net/tcp_authopt.h
Quoted 11 lines
include/net/tcp_authopt.h +10
-0+if (!new_info) +return -ENOMEM ; + +sk_nocaps_add (newsk,NETIF_F_GSO_MASK ); + new_info->src_isn =tcp_rsk (req)->snt_isn; + new_info->dst_isn =tcp_rsk (req)->rcv_isn; +INIT_HLIST_HEAD (&new_info->head); + err =tcp_authopt_clone_keys (newsk, oldsk, new_info, old_info); +if (err) { +__tcp_authopt_info_free (newsk, new_info);Are we leaving in place old value of newsk->authopt_info ? If this is copied from the listener, I think you need to add a tcp_sk(newsk)->authopt_info = NULL; before the kzalloc() call done above.
Yes, authopt_info should be set to NULL on error because keeping the listen socket's value is wrong and dangerous (double free).
Leaving authopt_info NULL or malloc failure is still possible dangerous because it means all keys are ignored and accepted. Not clear how we could cause tcp_create_openreq_child to fail instead.
This is a problem in a few other parts: if cryptography fails the outbound MAC is filled with zeros because there's not obvious way to make TX fail at that point.
Quoted 6 lines
Quoted 6 lines
include/net/tcp_authopt.h +6
-0+ err =__tcp_authopt_calc_mac (sk, skb, key, false, macbuf); +if (err) { +/* If mac calculation fails and caller doesn't handle the error +* try to make it obvious inside the packet. +*/ +memset (hash_location,0 , key->maclen);
Quoted 8 lines
include/net/tcp_authopt.h
Quoted 3 lines
include/net/tcp_authopt.h +3
-0+return err; + } +memcpy (hash_location, macbuf, key->maclen);[2]
This is the place were we do not make sure to clear the padding bytes (ifkey->maclenis not a multiple of 4)
Yes. It might make sense to fix in caller because it's the caller which decides to align options.
kernel test robot
build botRe: [RFCv3 07/15] tcp: authopt: Hook into tcp core
In reply to Leonard Crestezreproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross #…
Hi Leonard,
[FYI, it's a private test report for your RFC patch.] [auto build test WARNING on 3a62c333497b164868fdcd241842a1dd4e331825]
url: https://github.com/0day-ci/linux/commits/Leonard-Crestez/tcp-Initial-support-for-RFC5925-auth-option/20210825-053714 base: 3a62c333497b164868fdcd241842a1dd4e331825 config: arc-allyesconfig (attached as .config) compiler: arceb-elf-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross
# https://github.com/0day-ci/linux/commit/ed738605a859a09457d1ab0570d8cf8bcfe3f536
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Leonard-Crestez/tcp-Initial-support-for-RFC5925-auth-option/20210825-053714 git checkout ed738605a859a09457d1ab0570d8cf8bcfe3f536
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross ARCH=arc
If you fix the issue, kindly add following tag as appropriate
All warnings (new ones prefixed by >>):
>> net/ipv4/tcp_authopt.c:208:30: warning: no previous prototype for 'tcp_authopt_lookup_send' [-Wmissing-prototypes]
208 | struct tcp_authopt_key_info *tcp_authopt_lookup_send(struct tcp_authopt_info *info,
| ^~~~~~~~~~~~~~~~~~~~~~~
In file included from include/linux/rbtree.h:22,
from include/linux/mm_types.h:10,
from include/linux/mmzone.h:21,
from include/linux/gfp.h:6,
from include/linux/mm.h:10,
from include/linux/bvec.h:14,
from include/linux/skbuff.h:17,
from include/linux/tcp.h:17,
from include/net/tcp.h:20,
from net/ipv4/tcp_authopt.c:4:
net/ipv4/tcp_authopt.c: In function 'tcp_authopt_clone_keys':
net/ipv4/tcp_authopt.c:468:87: error: 'sk' undeclared (first use in this function); did you mean 's8'?
468 | hlist_for_each_entry_rcu(old_key, &old_info->head, node, lockdep_sock_is_held(sk)) {
| ^~
include/linux/rcupdate.h:318:22: note: in definition of macro 'RCU_LOCKDEP_WARN'
318 | if ((c) && debug_lockdep_rcu_enabled() && !__warned) { \
| ^
include/linux/rculist.h:706:14: note: in expansion of macro '__list_check_rcu'
706 | for (__list_check_rcu(dummy, ## cond, 0), \
| ^~~~~~~~~~~~~~~~
net/ipv4/tcp_authopt.c:468:9: note: in expansion of macro 'hlist_for_each_entry_rcu'
468 | hlist_for_each_entry_rcu(old_key, &old_info->head, node, lockdep_sock_is_held(sk)) {
| ^~~~~~~~~~~~~~~~~~~~~~~~
net/ipv4/tcp_authopt.c:468:87: note: each undeclared identifier is reported only once for each function it appears in
468 | hlist_for_each_entry_rcu(old_key, &old_info->head, node, lockdep_sock_is_held(sk)) {
| ^~
include/linux/rcupdate.h:318:22: note: in definition of macro 'RCU_LOCKDEP_WARN'
318 | if ((c) && debug_lockdep_rcu_enabled() && !__warned) { \
| ^
include/linux/rculist.h:706:14: note: in expansion of macro '__list_check_rcu'
706 | for (__list_check_rcu(dummy, ## cond, 0), \
| ^~~~~~~~~~~~~~~~
net/ipv4/tcp_authopt.c:468:9: note: in expansion of macro 'hlist_for_each_entry_rcu'
468 | hlist_for_each_entry_rcu(old_key, &old_info->head, node, lockdep_sock_is_held(sk)) {
| ^~~~~~~~~~~~~~~~~~~~~~~~
net/ipv4/tcp_authopt.c: In function 'tcp_authopt_shash_traffic_key':
net/ipv4/tcp_authopt.c:542:42: warning: variable 'daddr' set but not used [-Wunused-but-set-variable]
542 | struct in6_addr *daddr;
| ^~~~~
net/ipv4/tcp_authopt.c:541:42: warning: variable 'saddr' set but not used [-Wunused-but-set-variable]
541 | struct in6_addr *saddr;
| ^~~~~ net/ipv4/tcp_authopt.c: At top level:
net/ipv4/tcp_authopt.c:940:5: warning: no previous prototype for '__tcp_authopt_calc_mac' [-Wmissing-prototypes]
940 | int __tcp_authopt_calc_mac(struct sock *sk,
| ^~~~~~~~~~~~~~~~~~~~~~
vim +/tcp_authopt_lookup_send +208 net/ipv4/tcp_authopt.c
207
> 208 struct tcp_authopt_key_info *tcp_authopt_lookup_send(struct tcp_authopt_info *info,
209 const struct sock *addr_sk,
210 int send_id)
211 {
212 struct tcp_authopt_key_info *result = NULL;
213 struct tcp_authopt_key_info *key;
214
215 hlist_for_each_entry_rcu(key, &info->head, node, 0) {
216 if (send_id >= 0 && key->send_id != send_id)
217 continue;
218 if (key->flags & TCP_AUTHOPT_KEY_ADDR_BIND) {
219 if (addr_sk->sk_family == AF_INET) {
220 struct sockaddr_in *key_addr = (struct sockaddr_in *)&key->addr;
221 const struct in_addr *daddr =
222 (const struct in_addr *)&addr_sk->sk_daddr;
223
224 if (WARN_ON(key_addr->sin_family != AF_INET))
225 continue;
226 if (memcmp(daddr, &key_addr->sin_addr, sizeof(*daddr)))
227 continue;
228 }
229 if (addr_sk->sk_family == AF_INET6) {
230 struct sockaddr_in6 *key_addr = (struct sockaddr_in6 *)&key->addr;
231 const struct in6_addr *daddr = &addr_sk->sk_v6_daddr;
232
233 if (WARN_ON(key_addr->sin6_family != AF_INET6))
234 continue;
235 if (memcmp(daddr, &key_addr->sin6_addr, sizeof(*daddr)))
236 continue;
237 }
238 }
239 if (result && net_ratelimit())
240 pr_warn("ambiguous tcp authentication keys configured for send\n");
241 result = key;
242 }
243
244 return result;
245 }
246
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org
kernel test robot
build botRe: [RFCv3 07/15] tcp: authopt: Hook into tcp core
In reply to Leonard Crestezreproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross #…
Hi Leonard,
[FYI, it's a private test report for your RFC patch.] [auto build test ERROR on 3a62c333497b164868fdcd241842a1dd4e331825]
url: https://github.com/0day-ci/linux/commits/Leonard-Crestez/tcp-Initial-support-for-RFC5925-auth-option/20210825-053714 base: 3a62c333497b164868fdcd241842a1dd4e331825 config: m68k-allmodconfig (attached as .config) compiler: m68k-linux-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross
# https://github.com/0day-ci/linux/commit/ed738605a859a09457d1ab0570d8cf8bcfe3f536
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Leonard-Crestez/tcp-Initial-support-for-RFC5925-auth-option/20210825-053714 git checkout ed738605a859a09457d1ab0570d8cf8bcfe3f536
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross ARCH=m68k
If you fix the issue, kindly add following tag as appropriate
All errors (new ones prefixed by >>, old ones prefixed by <<):
>> ERROR: modpost: "__tcp_authopt_inbound_check" [net/ipv6/ipv6.ko] undefined!
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org
kernel test robot
build botRe: [RFCv3 07/15] tcp: authopt: Hook into tcp core
In reply to Leonard Crestezreproduce:
Hi Leonard,
[FYI, it's a private test report for your RFC patch.] [auto build test WARNING on 3a62c333497b164868fdcd241842a1dd4e331825]
url: https://github.com/0day-ci/linux/commits/Leonard-Crestez/tcp-Initial-support-for-RFC5925-auth-option/20210825-053714 base: 3a62c333497b164868fdcd241842a1dd4e331825 config: x86_64-rhel-8.3-kselftests (attached as .config) compiler: gcc-9 (Debian 9.3.0-22) 9.3.0 reproduce: # apt-get install sparse # sparse version: v0.6.3-348-gf0e6938b-dirty # https://github.com/0day-ci/linux/commit/ed738605a859a09457d1ab0570d8cf8bcfe3f536 git remote add linux-review https://github.com/0day-ci/linux git fetch --no-tags linux-review Leonard-Crestez/tcp-Initial-support-for-RFC5925-auth-option/20210825-053714 git checkout ed738605a859a09457d1ab0570d8cf8bcfe3f536 # save the attached .config to linux build tree make W=1 C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=x86_64 SHELL=/bin/bash
If you fix the issue, kindly add following tag as appropriate
sparse warnings: (new ones prefixed by >>)
>> net/ipv4/tcp_authopt.c:208:29: sparse: sparse: symbol 'tcp_authopt_lookup_send' was not declared. Should it be static?
net/ipv4/tcp_authopt.c:720:16: sparse: sparse: incorrect type in assignment (different base types) @@ expected unsigned int @@ got restricted __be32 [usertype] @@
net/ipv4/tcp_authopt.c:720:16: sparse: expected unsigned int
net/ipv4/tcp_authopt.c:720:16: sparse: got restricted __be32 [usertype]
net/ipv4/tcp_authopt.c:721:16: sparse: sparse: incorrect type in assignment (different base types) @@ expected unsigned int @@ got restricted __be32 [usertype] @@
net/ipv4/tcp_authopt.c:721:16: sparse: expected unsigned int
net/ipv4/tcp_authopt.c:721:16: sparse: got restricted __be32 [usertype]
net/ipv4/tcp_authopt.c:940:5: sparse: sparse: symbol '__tcp_authopt_calc_mac' was not declared. Should it be static? net/ipv4/tcp_authopt.c:128:9: sparse: sparse: context imbalance in 'tcp_authopt_alg_get_tfm' - wrong count at exit net/ipv4/tcp_authopt.c:134:9: sparse: sparse: context imbalance in 'tcp_authopt_alg_put_tfm' - unexpected unlock
Please review and possibly fold the followup patch.
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org
kernel test robot
build bot[RFC PATCH] tcp: authopt: tcp_authopt_lookup_send() can be static
In reply to Leonard Cresteznet/ipv4/tcp_authopt.c:208:29: warning: symbol 'tcp_authopt_lookup_send' was not declared. Should it be static?
net/ipv4/tcp_authopt.c:208:29: warning: symbol 'tcp_authopt_lookup_send' was not declared. Should it be static?
--- tcp_authopt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
net/ipv4/tcp_authopt.c +3 -3
@@ -205,9 +205,9 @@ static struct tcp_authopt_key_info *tcp_authopt_key_lookup_exact(const struct soreturn NULL ; } -struct tcp_authopt_key_info *tcp_authopt_lookup_send (struct tcp_authopt_info *info, -const struct sock *addr_sk, -int send_id) +static struct tcp_authopt_key_info *tcp_authopt_lookup_send (struct tcp_authopt_info *info, +const struct sock *addr_sk, +int send_id) {struct tcp_authopt_key_info *result =NULL ;struct tcp_authopt_key_info *key;
RFC v3 8/15 tcp: authopt: Add snmp counters
In reply to Leonard CrestezAdd LINUX_MIB_TCPAUTHOPTFAILURE and increment on failure. This can be use by userspace to count the number of failed authentications.
All types of authentication failures are reported under a single counter.
--- include/uapi/linux/snmp.h | 1 + net/ipv4/proc.c | 1 + net/ipv4/tcp_authopt.c | 3 +++ 3 files changed, 5 insertions(+)
include/uapi/linux/snmp.h +1 -0
@@ -290,10 +290,11 @@ enumLINUX_MIB_TCPDUPLICATEDATAREHASH ,/* TCPDuplicateDataRehash */ LINUX_MIB_TCPDSACKRECVSEGS ,/* TCPDSACKRecvSegs */ LINUX_MIB_TCPDSACKIGNOREDDUBIOUS ,/* TCPDSACKIgnoredDubious */ LINUX_MIB_TCPMIGRATEREQSUCCESS ,/* TCPMigrateReqSuccess */ LINUX_MIB_TCPMIGRATEREQFAILURE ,/* TCPMigrateReqFailure */ +LINUX_MIB_TCPAUTHOPTFAILURE ,/* TCPAuthOptFailure */ __LINUX_MIB_MAX };/* linux Xfrm mib definitions */ enum
net/ipv4/proc.c +1 -0
@@ -295,10 +295,11 @@ static const struct snmp_mib snmp4_net_list[] = {SNMP_MIB_ITEM ("TcpDuplicateDataRehash" ,LINUX_MIB_TCPDUPLICATEDATAREHASH ),SNMP_MIB_ITEM ("TCPDSACKRecvSegs" ,LINUX_MIB_TCPDSACKRECVSEGS ),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 };static void icmpmsg_put_line (struct seq_file *seq,unsigned long *vals,unsigned short *type,int count)
net/ipv4/tcp_authopt.c +3 -0
@@ -1071,10 +1071,11 @@ int __tcp_authopt_inbound_check(struct sock *sk, struct sk_buff *skb, struct tcp/* nothing found or expected */ if (!opt && !key)return 0 ;if (!opt && key) { +NET_INC_STATS (sock_net (sk),LINUX_MIB_TCPAUTHOPTFAILURE );net_info_ratelimited ("TCP Authentication Missing\n" );return -EINVAL ; }if (opt && !key) {/* RFC5925 Section 7.3:
@@ -1082,10 +1083,11 @@ int __tcp_authopt_inbound_check(struct sock *sk, struct sk_buff *skb, struct tcp* of segments with TCP-AO but that do not match an MKT. The initial * default of this configuration SHOULD be to silently accept such * connections. */ if (info->flags &TCP_AUTHOPT_FLAG_REJECT_UNEXPECTED ) { +NET_INC_STATS (sock_net (sk),LINUX_MIB_TCPAUTHOPTFAILURE );net_info_ratelimited ("TCP Authentication Unexpected: Rejected\n" );return -EINVAL ; }else {net_info_ratelimited ("TCP Authentication Unexpected: Accepted\n" );return 0 ;
@@ -1099,10 +1101,11 @@ int __tcp_authopt_inbound_check(struct sock *sk, struct sk_buff *skb, struct tcp err =__tcp_authopt_calc_mac (sk, skb, key, true, macbuf);if (err)return err;if (memcmp (macbuf, opt->mac, key->maclen)) { +NET_INC_STATS (sock_net (sk),LINUX_MIB_TCPAUTHOPTFAILURE );net_info_ratelimited ("TCP Authentication Failed\n" );return -EINVAL ; }return 0 ;
Signature · 1 line
2.25.1
RFC v3 9/15 selftests: tcp_authopt: Test key address binding
In reply to Leonard CrestezBy default TCP-AO keys apply to all possible peers but it's possible to have different keys for different remote hosts.
This patch adds initial tests for the behavior behind the TCP_AUTHOPT_KEY_BIND_ADDR flag. Server rejection is tested via client timeout so this can be slightly slow.
--- .../tcp_authopt_test/netns_fixture.py | 63 +++++++ .../tcp_authopt/tcp_authopt_test/server.py | 82 ++++++++++ .../tcp_authopt/tcp_authopt_test/test_bind.py | 143 ++++++++++++++++ .../tcp_authopt/tcp_authopt_test/utils.py | 154 ++++++++++++++++++ 4 files changed, 442 insertions(+) create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/netns_fixture.py create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/server.py create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/test_bind.py create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/utils.py
tools/testing/selftests/tcp_authopt/tcp_authopt_test/netns_fixture.py new file +63 -0
@@ -0,0 +1,63 @@ +# SPDX-License-Identifier: GPL-2.0 +import subprocess +import socket +from ipaddress import IPv4Address +from ipaddress import IPv6Address + + +class NamespaceFixture: + """Create a pair of namespaces connected by one veth pair + + Each end of the pair has multiple addresses but everything is in the same subnet + """ + + ns1_name = "tcp_authopt_test_1" + ns2_name = "tcp_authopt_test_2" + + @classmethod + def get_ipv4_addr(cls, ns=1, index=1) -> IPv4Address: + return IPv4Address("10.10.0.0") + (ns << 8) + index + + @classmethod + def get_ipv6_addr(cls, ns=1, index=1) -> IPv6Address: + return IPv6Address("fd00::") + (ns << 16) + index + + @classmethod + def get_addr(cls, address_family=socket.AF_INET, ns=1, index=1): + if address_family == socket.AF_INET: + return cls.get_ipv4_addr(ns, index) + elif address_family == socket.AF_INET6: + return cls.get_ipv6_addr(ns, index) + else: + raise ValueError(f"Bad address_family={address_family}") + + def __init__(self, **kw): + for k, v in kw.items(): + setattr(self, k, v) + + def __enter__(self): + script = f""" +set -e -x +ip netns del {self.ns1_name} || true +ip netns del {self.ns2_name} || true +ip netns add {self.ns1_name} +ip netns add {self.ns2_name} +ip link add veth0 netns {self.ns1_name} type veth peer name veth0 netns {self.ns2_name} +ip netns exec {self.ns1_name} ip link set veth0 up +ip netns exec {self.ns2_name} ip link set veth0 up +""" + for index in [1, 2, 3]: + script += f"ip -n {self.ns1_name} addr add {self.get_ipv4_addr(1, index)}/16 dev veth0\n" + script += f"ip -n {self.ns2_name} addr add {self.get_ipv4_addr(2, index)}/16 dev veth0\n" + script += f"ip -n {self.ns1_name} addr add {self.get_ipv6_addr(1, index)}/64 dev veth0 nodad\n" + script += f"ip -n {self.ns2_name} addr add {self.get_ipv6_addr(2, index)}/64 dev veth0 nodad\n" + subprocess.run(script, shell=True, check=True) + return self + + def __exit__(self, *a): + script = f""" +set -e -x +ip netns del {self.ns1_name} || true +ip netns del {self.ns2_name} || true +""" + subprocess.run(script, shell=True, check=True)
tools/testing/selftests/tcp_authopt/tcp_authopt_test/server.py new file +82 -0
82 lines not on this page · raw message
tools/testing/selftests/tcp_authopt/tcp_authopt_test/test_bind.py new file +143 -0
143 lines not on this page · raw message
tools/testing/selftests/tcp_authopt/tcp_authopt_test/utils.py new file +154 -0
154 lines not on this page · raw message
Signature · 1 line
2.25.1
Re: [RFCv3 09/15] selftests: tcp_authopt: Test key address binding
Quoted 8 lines · Leonard Crestez
By default TCP-AO keys apply to all possible peers but it's possible to have different keys for different remote hosts.
This patch adds initial tests for the behavior behind the
TCP_AUTHOPT_KEY_BIND_ADDRflag. Server rejection is tested via client timeout so this can be slightly slow.Signed-off-by Leonard Crestez <[email protected]>
Quoted 11 lines
--- .../tcp_authopt_test/netns_fixture.py | 63 +++++++ .../tcp_authopt/tcp_authopt_test/server.py | 82 ++++++++++ .../tcp_authopt/tcp_authopt_test/test_bind.py | 143 ++++++++++++++++ .../tcp_authopt/tcp_authopt_test/utils.py | 154 ++++++++++++++++++ 4 files changed, 442 insertions(+) create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/netns_fixture.py create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/server.py create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/test_bind.py create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/utils.py
This should be under selftests/net as a single "tcp_authopt" directory from what I can tell.
Re: [RFCv3 09/15] selftests: tcp_authopt: Test key address binding
Quoted 21 lines · David Ahern
Quoted 14 lines · Leonard Crestez
By default TCP-AO keys apply to all possible peers but it's possible to have different keys for different remote hosts.
This patch adds initial tests for the behavior behind the
TCP_AUTHOPT_KEY_BIND_ADDRflag. Server rejection is tested via client timeout so this can be slightly slow.Signed-off-by Leonard Crestez <[email protected]>--- .../tcp_authopt_test/netns_fixture.py | 63 +++++++ .../tcp_authopt/tcp_authopt_test/server.py | 82 ++++++++++ .../tcp_authopt/tcp_authopt_test/test_bind.py | 143 ++++++++++++++++ .../tcp_authopt/tcp_authopt_test/utils.py | 154 ++++++++++++++++++ 4 files changed, 442 insertions(+)Quoted 5 lines
create mode 100644
tools/testing/selftests/tcp_authopt/tcp_authopt_test/netns_fixture.pycreate mode 100644tools/testing/selftests/tcp_authopt/tcp_authopt_test/server.pycreate mode 100644tools/testing/selftests/tcp_authopt/tcp_authopt_test/test_bind.pycreate mode 100644tools/testing/selftests/tcp_authopt/tcp_authopt_test/utils.py
Quoted 2 lines
This should be under selftests/net as a single "
tcp_authopt" directory from what I can tell.
Maybe? I found no clear guidelines for organizing tests by subsystem. I just did a grep for .py in selftests and placed mine next to tc-testing.
Having a tcp_authopt_test code directory under tcp_authopt is the standard pattern for python packages, otherwise all submodules with utilities of dubious generality are dumped at the global level. Removing the tcp_authopt/tcp_authopt_test structure is awkward in python.
One way to deal with this is to add my test code in tools/testing/selftests/net/tcp_authopt and my setup.cfg and similar directly in tools/testing/selftests/net. This would make "net" the root of the package and make it easy to add other networking pytests. This seems close to what you mean.
kselftest itself does not seem to offer any special support for python code, only some for C and shell. Maybe it could offer a "kselftest" package with common utilities that are used by multiple test packages and everything would be installed into a single virtualenv by makefiles.
--
Regards,
Leonard
RFC v3 10/15 selftests: tcp_authopt: Capture and verify packets
In reply to Leonard CrestezTools like tcpdump and wireshark can parse the TCP Authentication Option but there is not yet support to verify correct signatures.
This patch implements TCP-AO signature verification using scapy and the python cryptography package and applies it to captures of linux traffic in multiple scenarios (ipv4, ipv6 etc).
The python code is verified itself with a subset of IETF test vectors from this page:
https://datatracker.ietf.org/doc/html/draft-touch-tcpm-ao-test-vectors-02
--- .../full_tcp_sniff_session.py | 53 +++ .../tcp_authopt_test/tcp_authopt_alg.py | 276 ++++++++++++++ .../tcp_authopt_test/test_vectors.py | 359 ++++++++++++++++++ .../tcp_authopt_test/test_verify_capture.py | 123 ++++++ .../tcp_authopt/tcp_authopt_test/validator.py | 158 ++++++++ 5 files changed, 969 insertions(+) create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/full_tcp_sniff_session.py create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/tcp_authopt_alg.py create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/test_vectors.py create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/test_verify_capture.py create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/validator.py
tools/testing/selftests/tcp_authopt/tcp_authopt_test/full_tcp_sniff_session.py new file +53 -0
@@ -0,0 +1,53 @@ +# SPDX-License-Identifier: GPL-2.0 +import scapy.sessions +from scapy.layers.inet import TCP + +from .utils import SimpleWaitEvent + + +class FullTCPSniffSession(scapy.sessions.DefaultSession): + """Implementation of a scapy sniff session that can wait for a full TCP capture + + Allows another thread to wait for a complete FIN handshake without polling or sleep. + """ + + found_syn = False + found_synack = False + found_fin = False + found_client_fin = False + found_server_fin = False + + def __init__(self, server_port=None, **kw): + super().__init__(**kw) + self.server_port = server_port + self._close_event = SimpleWaitEvent() + + def on_packet_received(self, p): + super().on_packet_received(p) + if not p or not TCP in p: + return + th = p[TCP] + # logger.debug("sport=%d dport=%d flags=%s", th.sport, th.dport, th.flags) + if th.flags.S and not th.flags.A: + if th.dport == self.server_port or self.server_port is None: + self.found_syn = True + if th.flags.S and th.flags.A: + if th.sport == self.server_port or self.server_port is None: + self.found_synack = True + if th.flags.F: + if self.server_port is None: + self.found_fin = True + self._close_event.set() + elif self.server_port == th.dport: + self.found_client_fin = True + self.found_fin = True + if self.found_server_fin and self.found_client_fin: + self._close_event.set() + elif self.server_port == th.sport: + self.found_server_fin = True + self.found_fin = True + if self.found_server_fin and self.found_client_fin: + self._close_event.set() + + def wait_close(self, timeout=10): + self._close_event.wait(timeout=timeout)
tools/testing/selftests/tcp_authopt/tcp_authopt_test/tcp_authopt_alg.py new file +276 -0
276 lines not on this page · raw message
tools/testing/selftests/tcp_authopt/tcp_authopt_test/test_vectors.py new file +359 -0
359 lines not on this page · raw message
tools/testing/selftests/tcp_authopt/tcp_authopt_test/test_verify_capture.py new file +123 -0
123 lines not on this page · raw message
tools/testing/selftests/tcp_authopt/tcp_authopt_test/validator.py new file +158 -0
158 lines not on this page · raw message
Signature · 1 line
2.25.1
RFC v3 11/15 selftests: Initial tcp_authopt support for nettest
In reply to Leonard CrestezAdd support for configuring TCP Authentication Option. Only a single key with default options.
--- tools/testing/selftests/net/nettest.c | 34 ++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-)
tools/testing/selftests/net/nettest.c +33 -1
@@ -100,10 +100,12 @@ struct sock_args {struct sockaddr_in v4;struct sockaddr_in6 v6; } md5_prefix;unsigned int prefix_len; +const char *authopt_password; +/* expected addresses and device index for connection */ const char *expected_dev;const char *expected_server_dev;int expected_ifindex;
@@ -250,10 +252,27 @@ static int switch_ns(const char *ns)close (fd);return ret; } +static int tcp_set_authopt (int sd,struct sock_args *args) +{ +struct tcp_authopt_key key; +int rc; + +memset (&key,0 ,sizeof (key)); +strcpy ((char *)key.key, args->authopt_password); + key.keylen =strlen (args->authopt_password); + key.alg =TCP_AUTHOPT_ALG_HMAC_SHA_1_96 ; + + rc =setsockopt (sd,IPPROTO_TCP ,TCP_AUTHOPT_KEY , &key,sizeof (key)); +if (rc <0 ) +log_err_errno ("setsockopt(TCP_AUTHOPT_KEY)" ); + +return rc; +} +static int tcp_md5sig (int sd,void *addr,socklen_t alen,struct sock_args *args) {int keylen =strlen (args->password);struct tcp_md5sig md5sig = {};int opt =TCP_MD5SIG ;
@@ -1508,10 +1527,15 @@ static int do_server(struct sock_args *args, int ipc_fd)if (args->password &&tcp_md5_remote (lsd, args)) {close (lsd); goto err_exit; } +if (args->authopt_password &&tcp_set_authopt (lsd, args)) { +close (lsd); + goto err_exit; + } +ipc_write (ipc_fd,1 );while (1 ) {log_msg ("waiting for client connection.\n" );FD_ZERO (&rfds);FD_SET (lsd, &rfds);
@@ -1630,10 +1654,13 @@ static int connectsock(void *addr, socklen_t alen, struct sock_args *args) goto out;if (args->password &&tcp_md5sig (sd, addr, alen, args)) goto err; +if (args->authopt_password &&tcp_set_authopt (sd, args)) + goto err; +if (args->bind_test_only) goto out;if (connect (sd, addr, alen) <0 ) {if (errno !=EINPROGRESS ) {
@@ -1819,11 +1846,11 @@ static int ipc_parent(int cpid, int fd, struct sock_args *args)wait (&status);return client_status; } -#define GETOPT_STR "sr:l:c:p:t:g:P:DRn:M:X:m:d:I:BN:O:SCi6xL:0:1:2:3:Fbq" +#define GETOPT_STR "sr:l:c:p:t:g:P:DRn:M:X:m:A:d:I:BN:O:SCi6xL:0:1:2:3:Fbq"static void print_usage (char *prog) {printf ( "usage: %s OPTS\n"
@@ -1856,10 +1883,12 @@ static void print_usage(char *prog)" -n num number of times to send message\n" "\n" " -M password use MD5 sum protection\n" " -X password MD5 password for client mode\n" " -m prefix/len prefix and length to use for MD5 key\n" +" -A password use RFC5925 TCP Authentication option\n" +"\n" " -g grp multicast group (e.g., 239.1.1.1)\n" " -i interactive mode (default is echo and terminate)\n" "\n" " -0 addr Expected local address\n" " -1 addr Expected remote address\n"
@@ -1970,10 +1999,13 @@ int main(int argc, char *argv[]) args.client_pw = optarg;break ;case 'm' : args.md5_prefix_str = optarg;break ; +case 'A' : + args.authopt_password = optarg; +break ;case 'S' : args.use_setsockopt =1 ;break ;case 'C' : args.use_cmsg =1 ;
Signature · 1 line
2.25.1
RFC v3 12/15 selftests: Initial tcp_authopt support for fcnal-test
In reply to Leonard CrestezJust test that a correct password is passed or otherwise a timeout is obtained.
--- tools/testing/selftests/net/fcnal-test.sh | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+)
tools/testing/selftests/net/fcnal-test.sh +22 -0
@@ -788,10 +788,31 @@ ipv4_ping() } ################################################################################ # IPv4 TCP +# +# TCP Authentication Option Tests +# +ipv4_tcp_authopt() +{ + # basic use case + log_start + run_cmd nettest -s -A ${MD5_PW} & + sleep 1 + run_cmd_nsb nettest -r ${NSA_IP} -A ${MD5_PW} + log_test $? 0 "AO: Simple password" + + # wrong password + log_start + show_hint "Should timeout since client uses wrong password" + run_cmd nettest -s -A ${MD5_PW} & + sleep 1 + run_cmd_nsb nettest -r ${NSA_IP} -A ${MD5_WRONG_PW} + log_test $? 2 "AO: Client uses wrong password" +} + # # MD5 tests without VRF # ipv4_tcp_md5_novrf() {
@@ -1119,10 +1140,11 @@ ipv4_tcp_novrf() show_hint "Should fail 'Connection refused'" run_cmd nettest -d ${NSA_DEV} -r ${a} log_test_addr ${a} $? 1 "No server, device client, local conn" ipv4_tcp_md5_novrf + ipv4_tcp_authopt } ipv4_tcp_vrf() { local a
Signature · 1 line
2.25.1
RFC v3 13/15 selftests: Add -t tcp_authopt option for fcnal-test.sh
In reply to Leonard CrestezThis script is otherwise very slow to run!
--- tools/testing/selftests/net/fcnal-test.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+)
tools/testing/selftests/net/fcnal-test.sh +12 -0
@@ -1328,10 +1328,21 @@ ipv4_tcp() log_subsection "With VRF" setup "yes" ipv4_tcp_vrf } + +only_tcp_authopt() +{ + log_section "TCP Authentication" + setup + set_sysctl net.ipv4.tcp_l3mdev_accept=0 + log_subsection "IPv4 no VRF" + ipv4_tcp_authopt +} + + ################################################################################ # IPv4 UDP ipv4_udp_novrf() {
@@ -4018,10 +4029,11 @@ do ipv6_bind|bind6) ipv6_addr_bind;; ipv6_runtime) ipv6_runtime;; ipv6_netfilter) ipv6_netfilter;; use_cases) use_cases;; + tcp_authopt) only_tcp_authopt;; # setup namespaces and config, but do not run any tests setup) setup; exit 0;; vrf_setup) setup "yes"; exit 0;;
Signature · 1 line
2.25.1
RFC v3 14/15 tcp: authopt: Add key selection controls
In reply to Leonard CrestezThe RFC requires that TCP can report the keyid and rnextkeyid values being sent or received, implement this via getsockopt values.
The RFC also requires that user can select the sending key and that the sending key is automatically switched based on rnextkeyid. These requirements can conflict so we implement both and add a flag which specifies if user or peer request takes priority.
Also add an option to control rnextkeyid explicitly from userspace.
--- Documentation/networking/tcp_authopt.rst | 25 ++++++++++ include/net/tcp_authopt.h | 10 ++++ include/uapi/linux/tcp.h | 31 ++++++++++++ net/ipv4/tcp_authopt.c | 60 +++++++++++++++++++++++- 4 files changed, 124 insertions(+), 2 deletions(-)
Documentation/networking/tcp_authopt.rst +25 -0
@@ -35,10 +35,35 @@ Keys can be bound to remote addresses in a way that is similar to TCP_MD5. RFC5925 requires that key ids do not overlap when tcp identifiers (addr/port) overlap. This is not enforced by linux, configuring ambiguous keys will result in packet drops and lost connections. +Key selection +------------- + +On getsockopt(TCP_AUTHOPT) information is provided about keyid/rnextkeyid in +the last send packet and about the keyid/rnextkeyd in the last valid received +packet. + +By default the sending keyid is selected to match the "rnextkeyid" value sent +by the remote side. If that keyid is not available (or for new connections) a +random matching key is selected. + +If the `TCP_AUTHOPT_LOCK_KEYID` is set then the sending key is selected by the +`tcp_authopt.send_local_id` field and rnextkeyid is ignored. If no key with +local_id == send_local_id is configured then a random matching key is +selected. + +The current sending key is cached in the socket and will not change unless +requested by remote rnextkeyid or by setsockopt. + +The rnextkeyid value sent on the wire is usually the recv_id of the current +key used for sending. If the TCP_AUTHOPT_LOCK_RNEXTKEY flag is set in +`tcp_authopt.flags` the value of `tcp_authopt.send_rnextkeyid` is send +instead. This can be used to implement smooth rollover: the peer will switch +its keyid to the received rnextkeyid when it is available. + ABI Reference ============= .. kernel-doc:: include/uapi/linux/tcp.h :identifiers: tcp_authopt tcp_authopt_flag tcp_authopt_key tcp_authopt_key_flag tcp_authopt_alg
include/net/tcp_authopt.h +10 -0
@@ -36,11 +36,21 @@ struct tcp_authopt_key_info {*/ struct tcp_authopt_info {/** @head: List of tcp_authopt_key_info */ struct hlist_head head;struct rcu_head rcu; +/** +* @send_keyid - Current key used for sending, cached. +* +* Once a key is found it only changes by user or remote request. +*/ +struct tcp_authopt_key_info *send_key;u32 flags; +u8 send_keyid; +u8 send_rnextkeyid; +u8 recv_keyid; +u8 recv_rnextkeyid;u32 src_isn;u32 dst_isn; };#ifdef CONFIG_TCP_AUTHOPT
include/uapi/linux/tcp.h +31 -0
@@ -346,10 +346,24 @@ struct tcp_diag_md5sig {/** * enum tcp_authopt_flag - flags for `tcp_authopt.flags` */ enum tcp_authopt_flag { +/** +* @TCP_AUTHOPT_FLAG_LOCK_KEYID: keyid controlled by sockopt +* +* If this is set `tcp_authopt.send_keyid` is used to determined sending +* key. Otherwise a key with send_id == recv_rnextkeyid is preferred. +*/ +TCP_AUTHOPT_FLAG_LOCK_KEYID = (1 <<0 ), +/** +* @TCP_AUTHOPT_FLAG_LOCK_RNEXTKEYID: Override rnextkeyid from userspace +* +* If this is set then `tcp_authopt.send_rnextkeyid` is sent on outbound +* packets. Other the recv_id of the current sending key is sent. +*/ +TCP_AUTHOPT_FLAG_LOCK_RNEXTKEYID = (1 <<1 ),/** * @TCP_AUTHOPT_FLAG_REJECT_UNEXPECTED: * Configure behavior of segments with TCP-AO coming from hosts for which no * key is configured. The default recommended by RFC is to silently accept * such connections.
@@ -361,10 +375,27 @@ enum tcp_authopt_flag {* struct tcp_authopt - Per-socket options related to TCP Authentication Option */ struct tcp_authopt {/** @flags: Combination of &enum tcp_authopt_flag */ __u32 flags; +/** +* @send_keyid: `tcp_authopt_key.send_id` of preferred send key +* +* This is only used if `TCP_AUTHOPT_FLAG_LOCK_KEYID` is set. +*/ +__u8 send_keyid; +/** +* @send_rnextkeyid: The rnextkeyid to send in packets +* +* This is controlled by the user iff TCP_AUTHOPT_FLAG_LOCK_RNEXTKEYID is +* set. Otherwise rnextkeyid is the recv_id of the current key. +*/ +__u8 send_rnextkeyid; +/** @recv_keyid: A recently-received keyid value. Only for getsockopt. */ +__u8 recv_keyid; +/** @recv_rnextkeyid: A recently-received rnextkeyid value. Only for getsockopt. */ +__u8 recv_rnextkeyid; };/** * enum tcp_authopt_key_flag - flags for `tcp_authopt.flags` *
net/ipv4/tcp_authopt.c +58 -2
143 lines not on this page · raw message
Signature · 1 line
2.25.1
RFC v3 15/15 selftests: tcp_authopt: Add tests for rollover
In reply to Leonard CrestezRFC5925 requires that the use can examine or control the keys being used. This is implemented in linux via fields on the TCP_AUTHOPT sockopt.
Add socket-level tests for the adjusting keyids on live connections and checking the they are reflected on the peer.
Also check smooth transitions via rnextkeyid.
--- .../tcp_authopt_test/linux_tcp_authopt.py | 16 +- .../tcp_authopt_test/test_rollover.py | 181 ++++++++++++++++++ 2 files changed, 194 insertions(+), 3 deletions(-) create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/test_rollover.py
tools/testing/selftests/tcp_authopt/tcp_authopt_test/linux_tcp_authopt.py +13 -3
@@ -20,10 +20,12 @@ def BIT(x): TCP_AUTHOPT = 38 TCP_AUTHOPT_KEY = 39 TCP_AUTHOPT_MAXKEYLEN = 80 +TCP_AUTHOPT_FLAG_LOCK_KEYID = BIT(0) +TCP_AUTHOPT_FLAG_LOCK_RNEXTKEYID = BIT(1) TCP_AUTHOPT_FLAG_REJECT_UNEXPECTED = BIT(2) TCP_AUTHOPT_KEY_DEL = BIT(0) TCP_AUTHOPT_KEY_EXCLUDE_OPTS = BIT(1) TCP_AUTHOPT_KEY_BIND_ADDR = BIT(2)
@@ -35,24 +37,32 @@ TCP_AUTHOPT_ALG_AES_128_CMAC_96 = 2 @dataclass class tcp_authopt: """Like linux struct tcp_authopt""" flags: int = 0 - sizeof = 4 + send_keyid: int = 0 + send_rnextkeyid: int = 0 + recv_keyid: int = 0 + recv_rnextkeyid: int = 0 + sizeof = 8 def pack(self) -> bytes: return struct.pack( - "I", + "IBBBB", self.flags, + self.send_keyid, + self.send_rnextkeyid, + self.recv_keyid, + self.recv_rnextkeyid, ) def __bytes__(self): return self.pack() @classmethod def unpack(cls, b: bytes): - tup = struct.unpack("I", b) + tup = struct.unpack("IBBBB", b) return cls(*tup) def set_tcp_authopt(sock, opt: tcp_authopt): return sock.setsockopt(socket.IPPROTO_TCP, TCP_AUTHOPT, bytes(opt))
tools/testing/selftests/tcp_authopt/tcp_authopt_test/test_rollover.py new file +181 -0
181 lines not on this page · raw message
Signature · 1 line
2.25.1