mbox series

[RFCv3,00/15] tcp: Initial support for RFC5925 auth option

Message ID cover.1629840814.git.cdleonard@gmail.com
Headers show
Series tcp: Initial support for RFC5925 auth option | expand

Message

Leonard Crestez Aug. 24, 2021, 9:34 p.m. UTC
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
Link: https://lore.kernel.org/netdev/cover.1628544649.git.cdleonard@gmail.com/
 
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.
Link: https://lore.kernel.org/netdev/01383a8751e97ef826ef2adf93bfde3a08195a43.1626693859.git.cdleonard@gmail.com/

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

Comments

Eric Dumazet Aug. 24, 2021, 11:02 p.m. UTC | #1
On 8/24/21 2:34 PM, Leonard Crestez wrote:
> The 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
> 
>

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)
Herbert Xu Aug. 25, 2021, 8:08 a.m. UTC | #2
On Tue, Aug 24, 2021 at 04:34:58PM -0700, Eric Dumazet wrote:
> 
> On 8/24/21 2:34 PM, Leonard Crestez wrote:
> > The 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.
> > 
> 
> 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,
Ard Biesheuvel Aug. 25, 2021, 4:04 p.m. UTC | #3
On Wed, 25 Aug 2021 at 10:08, Herbert Xu <herbert@gondor.apana.org.au> wrote:
>
> On Tue, Aug 24, 2021 at 04:34:58PM -0700, Eric Dumazet wrote:
> >
> > On 8/24/21 2:34 PM, Leonard Crestez wrote:
> > > The 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.
> > >
> >
> > 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.
Leonard Crestez Aug. 25, 2021, 4:37 p.m. UTC | #4
On 25.08.2021 08:18, David Ahern wrote:
> On 8/24/21 2:34 PM, Leonard Crestez wrote:
>> 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_ADDR flag. Server rejection is tested via client
>> timeout so this can be slightly slow.
>>
>> Signed-off-by: Leonard Crestez <cdleonard@gmail.com>
>> ---
>>   .../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.

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
Leonard Crestez Aug. 25, 2021, 6:56 p.m. UTC | #5
On 8/25/21 8:55 PM, Eric Dumazet wrote:
> On Wed, Aug 25, 2021 at 9:35 AM Leonard Crestez <cdleonard@gmail.com> wrote:
>>
>> On 25.08.2021 02:34, Eric Dumazet wrote:
>>> On 8/24/21 2:34 PM, Leonard Crestez wrote:
>>>> The 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.
>>>>
>>>
>>> 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).
> 
> Well, do not use arrays of NR_CPUS and instead use normal per_cpu
> accessors (as in __tcp_alloc_md5sig_pool)
> 
>>
>> 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.

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