mbox series

[00/19] tcp: Initial support for RFC5925 auth option

Message ID cover.1632240523.git.cdleonard@gmail.com (mailing list archive)
Headers show
Series tcp: Initial support for RFC5925 auth option | expand

Message

Leonard Crestez Sept. 21, 2021, 4:14 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. The goal of this series is to
allow routers using the linux TCP stack to interoperate with vendors
such as Cisco and Juniper.

Both algorithms described in RFC5926 are implemented but the code is not
very easily extensible beyond that. In particular there are several code
paths making stack allocations based on RFC5926 maximum, those would
have to be increased.

This version is incorporates previous feedback and expands the handling
of timewait sockets and RST packets. Here are some known flaws and
limits:

* Interaction with TCP-MD5 is not tested in all corners
* Interaction with FASTOPEN not tested but unlikely to work because
sequence number assumptions for syn/ack.
* Sequence Number Extension not implemented so connections will flap
every ~4G of traffic.
* Not clear if crypto_shash_setkey might sleep. If some implementation
do that then maybe they could be excluded through alloc flags.
* Traffic key is not cached (reducing performance)
* User is responsible for ensuring keys do not overlap

I labeled this as [PATCH]] because the issues above are not critical.

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 used in linux and in
kselftests but virtualenvs not very much. This test suite uses `tox` to
create a private virtualenv and hide dependencies. There is no clear
guidance for how to add python-based kselftests so I made it up.

Limited testing support is also included in nettest and fcnal-test.sh.
Coverage is extremely limited, I did not expand it because the tests run
too slowly.

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
This can be use for easy interoperability testing with cisco/juniper/etc.

Changes since RFCv3:
* Implement TCP_AUTHOPT handling for timewait and reset replies. Write
tests to execute these paths by injecting packets with scapy
* Handle combining md5 and authopt: if both are configured use authopt.
* Fix locking issues around send_key, introduced in on of the later
patches.
* Handle IPv4-mapped-IPv6 addresses: it used to be that an ipv4 SYN sent
to an ipv6 socket with TCP-AO triggered WARN
* Implement un-namespaced sysctl disabled this feature by default
* Allocate new key before removing any old one in setsockopt (Dmitry)
* Remove tcp_authopt_key_info.local_id because it's no longer used (Dmitry)
* Propagate errors from TCP_AUTHOPT getsockopt (Dmitry)
* Fix no-longer-correct TCP_AUTHOPT_KEY_DEL docs (Dmitry)
* Simplify crypto allocation (Eric)
* Use kzmalloc instead of __GFP_ZERO (Eric)
* Add static_key_false tcp_authopt_needed (Eric)
* Clear authopt_info copied from oldsk in __tcp_authopt_openreq (Eric)
* Replace memcmp in ipv4 and ipv6 addr comparisons (Eric)
* Export symbols for CONFIG_IPV6=m (kernel test robot)
* Mark more functions static (kernel test robot)
* Fix build with CONFIG_PROVE_RCU_LIST=y (kernel test robot)
Link: https://lore.kernel.org/netdev/cover.1629840814.git.cdleonard@gmail.com/

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 (19):
  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: Disable via sysctl by default
  selftests: tcp_authopt: Test key address binding
  tcp: ipv6: Add AO signing for tcp_v6_send_response
  tcp: authopt: Add support for signing skb-less replies
  tcp: ipv4: Add AO signing for skb-less replies
  selftests: tcp_authopt: Add scapy-based packet signing code
  selftests: tcp_authopt: Add packet-level tests
  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/ip-sysctl.rst        |    6 +
 Documentation/networking/tcp_authopt.rst      |   69 +
 include/linux/tcp.h                           |    9 +
 include/net/tcp.h                             |    1 +
 include/net/tcp_authopt.h                     |  200 +++
 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/sysctl_net_ipv4.c                    |   10 +
 net/ipv4/tcp.c                                |   30 +
 net/ipv4/tcp_authopt.c                        | 1450 +++++++++++++++++
 net/ipv4/tcp_input.c                          |   17 +
 net/ipv4/tcp_ipv4.c                           |  101 +-
 net/ipv4/tcp_minisocks.c                      |   12 +
 net/ipv4/tcp_output.c                         |   80 +-
 net/ipv6/tcp_ipv6.c                           |   56 +-
 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 +
 .../selftests/tcp_authopt/requirements.txt    |   40 +
 tools/testing/selftests/tcp_authopt/run.sh    |   15 +
 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  |   41 +
 .../full_tcp_sniff_session.py                 |   81 +
 .../tcp_authopt_test/linux_tcp_authopt.py     |  248 +++
 .../tcp_authopt_test/linux_tcp_md5sig.py      |   95 ++
 .../tcp_authopt_test/netns_fixture.py         |   83 +
 .../tcp_authopt_test/scapy_conntrack.py       |  150 ++
 .../tcp_authopt_test/scapy_tcp_authopt.py     |  211 +++
 .../tcp_authopt_test/scapy_utils.py           |  176 ++
 .../tcp_authopt/tcp_authopt_test/server.py    |   95 ++
 .../tcp_authopt/tcp_authopt_test/sockaddr.py  |  112 ++
 .../tcp_connection_fixture.py                 |  269 +++
 .../tcp_authopt/tcp_authopt_test/test_bind.py |  145 ++
 .../tcp_authopt_test/test_rollover.py         |  180 ++
 .../tcp_authopt_test/test_sockopt.py          |  185 +++
 .../tcp_authopt_test/test_vectors.py          |  359 ++++
 .../tcp_authopt_test/test_verify_capture.py   |  555 +++++++
 .../tcp_authopt/tcp_authopt_test/utils.py     |  102 ++
 .../tcp_authopt/tcp_authopt_test/validator.py |  127 ++
 47 files changed, 5544 insertions(+), 10 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 100644 tools/testing/selftests/tcp_authopt/requirements.txt
 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/linux_tcp_md5sig.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/scapy_conntrack.py
 create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/scapy_tcp_authopt.py
 create mode 100644 tools/testing/selftests/tcp_authopt/tcp_authopt_test/scapy_utils.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_connection_fixture.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: 07b855628c226511542d0911cba1b180541fbb84

Comments

Jakub Kicinski Sept. 21, 2021, 11:13 p.m. UTC | #1
On Tue, 21 Sep 2021 19:14:43 +0300 Leonard Crestez wrote:
> 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.

Hopefully there will be some feedback / discussion, but even if
everyone acks this you'll need to fix all the transient build
failures, and kdoc warnings added - and repost. 
git rebase --exec='make' and scripts/kernel-doc are your allies.
Francesco Ruggeri Sept. 22, 2021, 8:23 p.m. UTC | #2
On Tue, Sep 21, 2021 at 9:15 AM Leonard Crestez <cdleonard@gmail.com> wrote:
> * Sequence Number Extension not implemented so connections will flap
> every ~4G of traffic.

Could you expand on this?
What exactly do you mean by flap? Will the connection be terminated?
I assume that depending on the initial sequence numbers the first flaps
may occur well before 4G.
Do you use a SNE of 0 in the hash computation, or do you just not include
the SNE in it?

Thanks,
Francesco
Leonard Crestez Sept. 23, 2021, 7:38 a.m. UTC | #3
On 9/22/21 11:23 PM, Francesco Ruggeri wrote:
> On Tue, Sep 21, 2021 at 9:15 AM Leonard Crestez <cdleonard@gmail.com> wrote:
>> * Sequence Number Extension not implemented so connections will flap
>> every ~4G of traffic.
> 
> Could you expand on this?
> What exactly do you mean by flap? Will the connection be terminated?
> I assume that depending on the initial sequence numbers the first flaps
> may occur well before 4G.
> Do you use a SNE of 0 in the hash computation, or do you just not include
> the SNE in it?

SNE is hardcoded to zero, with the logical consequence of incorrect 
signatures on sequence number wrapping. The SNE has to be included 
because otherwise all signatures would be invalid.

You are correct that this can break much sooner than 4G of traffic, but 
still in the GB range on average. I didn't test the exact behavior (not 
clear how) but if signatures don't validate the connection will likely 
timeout.

My plan is to use TCP_REPAIR to control sequence numbers and test this 
reliably in an isolated environment (not interop with a cisco VM or 
similar). I want to implement TCP_REPAIR support for TCP-AO anyway.

It was skipped because the patch series is already quite large.

--
Regards,
Leonard
Leonard Crestez Sept. 23, 2021, 7:49 a.m. UTC | #4
On 9/22/21 2:13 AM, Jakub Kicinski wrote:
> On Tue, 21 Sep 2021 19:14:43 +0300 Leonard Crestez wrote:
>> 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.
> 
> Hopefully there will be some feedback / discussion, but even if
> everyone acks this you'll need to fix all the transient build
> failures, and kdoc warnings added - and repost.
> git rebase --exec='make' and scripts/kernel-doc are your allies.

Hello,

I already went through several round of testing with git rebase 
--exec='$test' but it seems I introduced a few new failures after 
several rounds of squashing fixes. I'll need to check kernel-doc 
comments for source files not referenced in documenation.

Many of the patch splits were artificially created in order to ease 
review, for example "signing packets" doesn't do anything without also 
"hooking in the tcp stack". Some static functions will trigger warnings 
because they're unused until the next patch, not clear what the 
preferred solution would be here. I could remove the "static" marker 
until the next patch or reverse the order and have the initial "tcp 
integration" patches call crypto code that just returns an error and 
fills-in a signature of zeros.

A large amount of the code is just selftests and much of it is not 
completely specific to TCP-AO. Maybe I could try to repost the parts 
that verify handling of timewait corners and resets in a variant that 
only handles "md5" and "unsigned"?

I already tried posting my scapy implementation of TCP-AO and MD5 to 
scapy upstream because it is not specific to linux .

--
Regards,
Leonard
Jakub Kicinski Sept. 23, 2021, 1:58 p.m. UTC | #5
On Thu, 23 Sep 2021 10:49:53 +0300 Leonard Crestez wrote:
> Many of the patch splits were artificially created in order to ease 
> review, for example "signing packets" doesn't do anything without also 
> "hooking in the tcp stack". Some static functions will trigger warnings 
> because they're unused until the next patch, not clear what the 
> preferred solution would be here. I could remove the "static" marker 
> until the next patch or reverse the order and have the initial "tcp 
> integration" patches call crypto code that just returns an error and 
> fills-in a signature of zeros.

Ease of review is important, so although discouraged transient warnings
are acceptable if the code is much easier to read that way. The problem
here was that the build was also broken, but looking at it again I
think you're just missing exports, please make sure to build test with
IPV6 compiled as a module:

ERROR: modpost: "tcp_authopt_hash" [net/ipv6/ipv6.ko] undefined!
ERROR: modpost: "__tcp_authopt_select_key" [net/ipv6/ipv6.ko] undefined!
David Ahern Sept. 25, 2021, 1:35 a.m. UTC | #6
On 9/23/21 1:38 AM, Leonard Crestez wrote:
> On 9/22/21 11:23 PM, Francesco Ruggeri wrote:
>> On Tue, Sep 21, 2021 at 9:15 AM Leonard Crestez <cdleonard@gmail.com>
>> wrote:
>>> * Sequence Number Extension not implemented so connections will flap
>>> every ~4G of traffic.
>>
>> Could you expand on this?
>> What exactly do you mean by flap? Will the connection be terminated?
>> I assume that depending on the initial sequence numbers the first flaps
>> may occur well before 4G.
>> Do you use a SNE of 0 in the hash computation, or do you just not include
>> the SNE in it?
> 
> SNE is hardcoded to zero, with the logical consequence of incorrect
> signatures on sequence number wrapping. The SNE has to be included
> because otherwise all signatures would be invalid.
> 
> You are correct that this can break much sooner than 4G of traffic, but
> still in the GB range on average. I didn't test the exact behavior (not
> clear how) but if signatures don't validate the connection will likely
> timeout.
> 

This is for BGP and LDP connections. What's the expected frequency of
rollover for large FIBs? Seems like it could be fairly often.
Leonard Crestez Sept. 25, 2021, 2:21 p.m. UTC | #7
On 9/25/21 4:35 AM, David Ahern wrote:
> On 9/23/21 1:38 AM, Leonard Crestez wrote:
>> On 9/22/21 11:23 PM, Francesco Ruggeri wrote:
>>> On Tue, Sep 21, 2021 at 9:15 AM Leonard Crestez <cdleonard@gmail.com>
>>> wrote:
>>>> * Sequence Number Extension not implemented so connections will flap
>>>> every ~4G of traffic.
>>>
>>> Could you expand on this?
>>> What exactly do you mean by flap? Will the connection be terminated?
>>> I assume that depending on the initial sequence numbers the first flaps
>>> may occur well before 4G.
>>> Do you use a SNE of 0 in the hash computation, or do you just not include
>>> the SNE in it?
>>
>> SNE is hardcoded to zero, with the logical consequence of incorrect
>> signatures on sequence number wrapping. The SNE has to be included
>> because otherwise all signatures would be invalid.
>>
>> You are correct that this can break much sooner than 4G of traffic, but
>> still in the GB range on average. I didn't test the exact behavior (not
>> clear how) but if signatures don't validate the connection will likely
>> timeout.
>>
> 
> This is for BGP and LDP connections. What's the expected frequency of
> rollover for large FIBs? Seems like it could be fairly often.

Implementing SNE is obviously required for standard conformance, I'm not 
claiming it is not needed. I will include this in a future version.

I skipped it because it has very few interactions with the rest of the 
code so it can be implemented separately. Many tests can pass just fine 
ignoring SNE.

--
Regards,
Leonard
Leonard Crestez Sept. 25, 2021, 2:25 p.m. UTC | #8
On 9/23/21 4:58 PM, Jakub Kicinski wrote:
> On Thu, 23 Sep 2021 10:49:53 +0300 Leonard Crestez wrote:
>> Many of the patch splits were artificially created in order to ease
>> review, for example "signing packets" doesn't do anything without also
>> "hooking in the tcp stack". Some static functions will trigger warnings
>> because they're unused until the next patch, not clear what the
>> preferred solution would be here. I could remove the "static" marker
>> until the next patch or reverse the order and have the initial "tcp
>> integration" patches call crypto code that just returns an error and
>> fills-in a signature of zeros.
> 
> Ease of review is important, so although discouraged transient warnings
> are acceptable if the code is much easier to read that way. The problem
> here was that the build was also broken, but looking at it again I
> think you're just missing exports, please make sure to build test with
> IPV6 compiled as a module:
> 
> ERROR: modpost: "tcp_authopt_hash" [net/ipv6/ipv6.ko] undefined!
> ERROR: modpost: "__tcp_authopt_select_key" [net/ipv6/ipv6.ko] undefined!

The kernel build robot sent me an email regarding IPv6=m last time, I 
fixed that issue but introduced another. I check for IPv6=m specifically 
but only did "make net/ipv4/ net/ipv6/" and missed the error.

I went through the series and solved checkpatch, kernel-doc and 
compilation issues in a more systematic fashion, I will repost later.

--
Regards,
Leonard