Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for TCP/UDP checksum offloading with partial checksums #1

Open
wants to merge 12 commits into
base: UNIKRAFT-2_1_x
Choose a base branch
from

Conversation

skuenzer
Copy link
Member

@skuenzer skuenzer commented Nov 11, 2021

This PR implements checksum offloading capabilities for TCP and UDP, where a partial checksum is computed in software. This checksum covers only the pseudo header, as soon as a packet leaves the host, the physical NIC does complete the calculation from there on. We need this feature to properly support the virtio-net and netfront network interfaces (and likely for other network adapters as well).
For this purpose, we introduce two pbuf flags in order to be able to handle mixed traffic:

  • PBUF_FLAG_CSUM_PARTIAL marks a packet as containing only a partially computed TCP or UDP checksum. The csum_start and csum_offset fields point to the checksum field of the header and to where the computation needs
    to be completed. As soon as the stack receives such a packet, we skip checksum validation but check csum_start and csum_offset. Similar to Linux, we assume that traffic with a partial checksum is only resulting from in-memory communication where corruption is unlikely, e.g., guest-to-guest, host-to-guest.
  • PBUF_FLAG_DATA_VALID marks a packet whose UDP or TCP checksum has already been validated (e.g., physical NIC on the host). On reception, lwIP will skip checksum validation. For improving loopback traffic, this flag is set for transmitted TCP and UDP packets.

A current limitation of this implementation is the lack of support for forwarding and L2-bridging. The missing piece of the puzzle is a function that can complete the checksum computation in the software for partially checksummed packets that are forwarded to devices that do not support partially checksummed packets.

Another version of this PR that applies to lwIP's master branch is also sent to the lwIP community for review.

This commit syncs the netif checksum check and control macros
with the `master` branch of lwip. State on master:
  61c67fc
It basically introduces the macro `NETIF_CHECKSUM_ENABLED` that
resolves into condition only instead of an `if`-statement as
`IF__NETIF_CHECKSUM_ENABLED` does. This enables the macro to be
used as part of conditional expressions.

Signed-off-by: Simon Kuenzer <[email protected]>
…F_CHECKSUM_ENABLED`

When `LWIP_CHECKSUM_CTRL_PER_NETIF` is not active, the macros
`IF__NETIF_CHECKSUM_ENABLED` and `NETIF_SET_CHECKSUM_CTRL` can
cause compiler errors under the following circumstances:
- Whenever an `else` case is used together with
  `IF__NETIF_CHECKSUM_ENABLED`, it results in an else case
  without if statement:

  IF__NETIF_CHECKSUM_ENABLED(...) {
    /**/
  } else {
    /**/
  }

  This commit resolve the macro always into an `if`-statement.
  The originally intended condition is kept: Unlike
  LWIP_CHECKSUM_ENABLED, it becomes constantly `true` when
  `LWIP_CHECKSUM_CTRL_PER_NETIF` is inactive. This
  allows the compiler to optimize out any `else` cases.

- Whenever `NETIF_SET_CHECKSUM_CTRL` is used as a single-statement
  compound under an `if`-statement or `for`-loops
  (without curly braces `{`, `}`), unintentionally, the successive
  statement becomes the statement for `if` or `for`:

  if (condition)
    NETIF_SET_CHECKSUM_CTRL(...)
  next_statement();

  This commit introduces the best practice construct to avoid such
  programming mistakes: `do {} while (0)`

Signed-off-by: Simon Kuenzer <[email protected]>
Introduces routines to compute the checksum of an TCP/UDP
pseudo header only. Such a checksum can be used to
initialize the checksum field for partial hardware checksum
offloading.

Signed-off-by: Simon Kuenzer <[email protected]>
`PBUF_FLAG_CSUM_PARTIAL` indicates that a checksum within a
pbuf is only partially computed. For such pbufs, netif drivers
can offload completing the calculation to hardware. On
receiving, drivers are able to indicate the stack that a
checksum is only partially computed. Typically, this can happen
in virtual environments (e.g., virtio-net, netfront) where mixed
types of traffic can be received (partially and fully computed
checksums).

The additional pbuf field `csum_start` points to the pbuf payload
byte where checksum computation must continue until the end of the
payload of a pbuf chain. `csum_offset` points to the checksum
field. For implementation simplicity `PBUF_FLAG_CSUM_PARTIAL`
can only be set once for a pbuf chain with the heading pbuf.

Signed-off-by: Simon Kuenzer <[email protected]>
Implements partial checksumming of TCP segments and control
packets. Resulting pbufs will have `PBUF_FLAG_CSUM_PARTIAL`
set and `csum_start` and `csum_offset` will point to the
corresponding locations within the pbuf payload.

Partial checksumming for TCP is only enabled if
`CHECKSUM_PARTIAL_TCP` and `CHECKSUM_GEN_TCP` are enabled
via `lwipopts.h`.
If netif checksum control flags are activated, the control flags
`NETIF_CHECKSUM_GEN_TCP` and `NETIF_CHECKSUM_PARTIAL_TCP` must
also be set.

Signed-off-by: Simon Kuenzer <[email protected]>
Implements checking of partial checksummed TCP segments and
control packets. netif drivers that support partial checksums
will set `PBUF_FLAG_CSUM_PARTIAL` for such received pbufs.

Similar to Linux's implementation, the partial TCP checksum
is not checked on reception. It is assumed that such packets
can only be originated from in-memory packet transfers (for
example from another guest on the same physical machine in
case of virtualization).
In case the netif driver provides `csum_start` and
`csum_offset`, these pointers must point to the checksum
field of the TCP header. Otherwise such a packet is dropped.

This handling of partial TCP checksums is only enabled if
`CHECKSUM_PARTIAL_TCP` and `CHECKSUM_CHECK_TCP` are enabled
via `lwipopts.h`. If netif checksum control flags are
activated, the control flags `NETIF_CHECKSUM_CHECK_TCP` and
`NETIF_CHECKSUM_PARTIAL_TCP` must also be set.

Signed-off-by: Simon Kuenzer <[email protected]>
Implements partial checksumming of UDP packets. Resulting
pbufs will have `PBUF_FLAG_CSUM_PARTIAL` set and
`csum_start` and `csum_offset` will point to the
corresponding locations within the pbuf payload.

Partial checksumming for UDP is only enabled if
`CHECKSUM_PARTIAL_UDP` and `CHECKSUM_GEN_UDP` are enabled
via `lwipopts.h`.
If netif checksum control flags are activated, the control flags
`NETIF_CHECKSUM_GEN_UDP` and `NETIF_CHECKSUM_PARTIAL_UDP` must
also be set.

Signed-off-by: Simon Kuenzer <[email protected]>
Implements checking of partial checksummed UDP packets.
netif drivers that support partial checksums will set
`PBUF_FLAG_CSUM_PARTIAL` for such received pbufs.

The partial UDP checksum is not checked on reception. It
is assumed that such packets can only be originated from
in-memory packet transfers (for example from another guest
on the same physical machine in case of virtualization).
In case the netif driver provides `csum_start` and
`csum_offset`, these pointers must point to the checksum
field of the UDP header. Otherwise such a packet is dropped.

This handling of partial UDP checksums is only enabled if
`CHECKSUM_PARTIAL_UDP` and `CHECKSUM_CHECK_UDP` are enabled
via `lwipopts.h`. If netif checksum control flags are
activated, the control flags `NETIF_CHECKSUM_CHECK_UDP` and
`NETIF_CHECKSUM_PARTIAL_UDP` must also be set.

Signed-off-by: Simon Kuenzer <[email protected]>
`PBUF_FLAG_DATA_VALID` indicates that the L4 checksum within
a pbuf is already validated and further checks are not
necessary by the stack. For implementation simplicity
`PBUF_FLAG_DATA_VALID` can only be set once for a pbuf chain
with the heading pbuf.

Typically, this flag is set by netif drivers in virtual
environments for traffic that is already validated, e.g.,
by receiving it on a physical network card.

Signed-off-by: Simon Kuenzer <[email protected]>
Checking of TCP checksums can be skipped for pbufs that are marked with
`PBUF_FLAG_DATA_VALID`.

This feature is only enabled when `CHECKSUM_CHECK_TCP` and
`CHECKSUM_SKIPVALID_TCP` is set. If netif checksum control
flags are activated, the control flags
`NETIF_CHECKSUM_CHECK_TCP` and
`NETIF_CHECKSUM_SKIPVALID_TCP` must also be set.

Signed-off-by: Simon Kuenzer <[email protected]>
Checking of UDP checksums can be skipped for pbufs that are marked with
`PBUF_FLAG_DATA_VALID`.

This feature is only enabled when `CHECKSUM_CHECK_UDP` and
`CHECKSUM_SKIPVALID_UDP` is set. If netif checksum control
flags are activated, the control flags
`NETIF_CHECKSUM_CHECK_UDP` and
`NETIF_CHECKSUM_SKIPVALID_UDP` must also be set.

Signed-off-by: Simon Kuenzer <[email protected]>
In order to allow skipping of checksum checks when looping
back traffic, `PBUF_FLAG_DATA_VALID` is set on every
generated UDP, TCP packet.

Netif drivers that should not set the flag for outgoing
traffic (e.g., virtio-net) should ignore this flag.

Signed-off-by: Simon Kuenzer <[email protected]>
Copy link
Member

@mogasergiu mogasergiu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me.

Reviewed-by: Sergiu Moga [email protected]

@razvand razvand added the enhancement New feature or request label Nov 27, 2021
unikraft-bot pushed a commit that referenced this pull request Apr 7, 2023
Reproducer (in bash):

base64 -d <<< "H4sIAP/9L2QCA+3WoQ2AMBSE4QoCTFHBBJfgSRF4RDfpRmgmYBpGQRBCk4ZiSfk/+fJMK+5dZRVpzSQzSs6oPierDV4y87WxLQLwE42SfNCdDyHJB9/xZwAARPbMJbUq4JJmu4JVT1cAAACfbGIqoqcMzy90eu+aBw2+N28WFgAA" | gunzip | test/fuzz/lwip_fuzz2

Crash log:

../../src/core/altcp_tcp.c:178:13: runtime error: member access within null pointer of type 'struct tcp_pcb'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior ../../src/core/altcp_tcp.c:178:13 in
AddressSanitizer:DEADLYSIGNAL
=================================================================
==192415==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000048 (pc 0x557065081703 bp 0x0aae0cb71204 sp 0x7ffd034dabc0 T0)
==192415==The signal is caused by a READ memory access.
==192415==Hint: address points to the zero page.
    #0 0x557065081703 in altcp_tcp_setup_callbacks /.../lwip/test/fuzz/../../src/core/altcp_tcp.c:178:19
    #1 0x55706508206f in altcp_tcp_setup /.../lwip/test/fuzz/../../src/core/altcp_tcp.c:189:3
    lwip-tcpip#2 0x55706508206f in altcp_tcp_accept /.../lwip/test/fuzz/../../src/core/altcp_tcp.c:84:5
    lwip-tcpip#3 0x557065095592 in tcp_input /.../lwip/test/fuzz/../../src/core/tcp_in.c:380:9
    lwip-tcpip#4 0x5570650e752f in ip4_input /.../lwip/test/fuzz/../../src/core/ipv4/ip4.c:743:9
    lwip-tcpip#5 0x55706513d4de in ethernet_input /.../lwip/test/fuzz/../../src/netif/ethernet.c:186:9
    lwip-tcpip#6 0x557064fe0959 in input_pkt /.../lwip/test/fuzz/fuzz_common.c:209:9
    lwip-tcpip#7 0x557064fdeb6a in input_pkts /.../lwip/test/fuzz/fuzz_common.c:257:9
    lwip-tcpip#8 0x557064fdeb6a in lwip_fuzztest /.../lwip/test/fuzz/fuzz_common.c:669:3
    lwip-tcpip#9 0x7ff4f578e189 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    lwip-tcpip#10 0x7ff4f578e244 in __libc_start_main csu/../csu/libc-start.c:381:3
    lwip-tcpip#11 0x557064f20420 in _start (/.../lwip/test/fuzz/lwip_fuzz2+0x81420) (BuildId: 8680a96430d5749c90111fe9c3a3d4f881a5dbcd)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /.../lwip/test/fuzz/../../src/core/altcp_tcp.c:178:19 in altcp_tcp_setup_callbacks
==192415==ABORTING
Aborted
@razvand razvand assigned razvand and unassigned marcrittinghaus Aug 19, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
Status: No status
Status: No status
Development

Successfully merging this pull request may close these issues.

4 participants