This allows to set a timer between the completion of a datagram in the
reassembly buffer and the deletion of the corresponding reassembly
buffer entry. This allows to ignore potentially late incoming link-layer
duplicates of fragments of the datagram that then will have the
reassembly buffer entry be blocked.
This was noted in this [discussion] for classic 6LoWPAN reassembly (and
minimal fragment forwarding) and is recommended in the current
[selective fragment recovery draft][SFR draft].
[discussion]: https://mailarchive.ietf.org/arch/msg/6lo/Ez0tzZDqawVn6AFhYzAFWUOtJns
[SFR draft]: https://tools.ietf.org/html/draft-ietf-6lo-fragment-recovery-07#section-6
As analyzed in #12678 there are cases where different reports can be
generated for the different snips of the packet send via the `sock`.
To catch all errors generated by the stack, the sock has to subscribe
for all snips of the packet sent. If any of the snips reports an error
distinct from `GNRC_NETERR_SUCCESS` or the previous one, we report that
status instead of just the first we receive. This way we are ensured to
have the first error reported by the stack for the given packet.
The name `fragment_msg` or `frag_msg`/`msg_frag` always to me was a bit
misplaced, as it basically implements an asynchronous fragmentation
buffer and doesn't necessarily have anything to do with messages.
This change
1. changes the name to `fb` (for fragmentation buffer)
2. factors its code out to its own sub-module so it can be re-used by
other 6LoWPAN fragmentation schemes like [Selective Fragment
Recovery]
[Selective Fragment Recovery]: https://tools.ietf.org/html/draft-ietf-6lo-fragment-recovery-05
The interface is already fetched in the beginning of the function and
doesn't change during its run, so getting the interface again at this
point is just redundant.
When decoding IPHC in a fragmented datagram, relying on the size of the
allocated space for the decoded packet is wrong when fragments are
forwarded and decoded on an intermediate node (for which the reassembly
buffer's space is used): Using the full datagram size for allocation in
this case would be wasteful, so the allocated space is only marginally
larger than the fragment's compressed form.
This in turn results in the wrong UDP payload size being chosen and
even worse being forwarded to the subsequent nodes.
This change uses the (virtual) reassembly buffer's `datagram_size`
instead of relying on the allocated space for the encoded
datagram/fragment.
`_match_to_idx()` was removed from source address selection (which was
the only one setting the filter parameter to a non-NULL value), so it
is the parameter is not needed anymore.
When source address selection is done, both RFC and comments in the code
state, that a longest prefix match should *only* be used as a
tie-breaker between more than one viable candidate. If there is only one
address, there is
a) no need for a tie-breaker
b) in the case of either the destination address or the single remaining
address being ULAs ([which are considered to be of global scope]
[RFC4193]) possibly not matching, as `fd00::/7` and e.g. `2001::/8`
do not have a common prefix.
(b) in fact causes the match function to return -1, causing the source
address selection to return -1, causing the outer function to return the
first address it found (which most often is the link-local address),
causing e.g. a ping to an ULA to fail, even is there is a global
address.
[RFC4193]: https://tools.ietf.org/html/rfc4193
Different platforms evaluate `printf()` for NULL pointers differently,
resulting tests checking for a certain output to fail. This unifies that
(debug) output for the static packet buffer statistics.
Similar as with #12513, when the NIB is compiled in 6LN mode (but not
6LR mode), the address-resolution state-machine (ARSM) functionality is
disabled in favor of the more simpler address resolution proposed in RFC
6775.
However, if a non-6LN interface is also compiled in (without making it
a router or border router) it will never join the solicited-nodes
multicast address of addresses added to it, resulting in address
resolution to that interface to fail.
If the interface is not a 6LN (which in case 6LN mode is disabled is
always false), a warning is now printed, encouraging the user to
activate the ARSM functionality if needed.
When the NIB is compiled for 6LN mode (but not a 6LBR), the Stateless
Address Autoconfiguration (SLAAC) functionality is disabled, as it is
typically not required; see `sys/include/net/gnrc/ipv6/nib/conf.h`, ll.
46 and 55. However, if a non-6LN interface is also compiled in (still
without making the node a border router) an auto-configured address will
be assigned in accordance with [RFC 6775] to the interface, just
assuming the interface is a 6LN interface. As it then only performs
duplicate address detection RFC-6775-style then, the address then never
becomes valid, as the duplicate address detection according to [RFC
4862] (part of the SLAAC functionality) is never performed.
As auto-configuring an address without SLAAC doesn't make sense, this
fix makes the interface skip it completely, but provides a warning to
the user, so they know what to do.
[RFC 6775]: https://tools.ietf.org/html/rfc6775#section-5.2
[RFC 4862]: https://tools.ietf.org/html/rfc4862#section-5.4
The functions now are semantic distinct:
- gnrc_netif_is_6lo(): the interface is a 6Lo interface
- gnrc_netif_is_6ln(): the interface is using Neighbor Discovery
according to RFC 6775
We want to check if the interface is an interface requiring the 6Lo
adaptation layer, not if it is a 6LN according to RFC 6775 [[1]].
[1]: https://tools.ietf.org/html/rfc6775#section-2
When writing to the IPv6 header the implementation currently doesn't
take the packet with the (potentially) duplicated header, but the
packet with the original one, which leads to the packet sent and then
released in `gnrc_netif_ethernet.c` first and then accessed again in
further iterations of the "writing to the IPv6 header" loop, which
causes access to an invalid pointer, causing a crash.
Fixes#11980
While 485dbd1fda (from #12175) was right
in assuming that the for most ICMPv6 error messages the originating
packet's destination address must not be a multicast, this is not the
case for _all_ ICMPv6 error messages (see [RFC 4443], section 2.4(e.3)).
Additionally, 485dbd1fda removed the
check for the source address ([RFC 4443], section 2.4(e.6)), which this
PR re-adds.
[RFC 4443]: https://tools.ietf.org/html/rfc4443#section-2.4
Rather than dispatching the packet automatically once it is complete,
`gnrc_sixlowpan_frag_rb_add()` now only returns success, and leaves it
to the caller to dispatch the packet.
While it is correct to not use an invalid address as a source address,
it is incorrect to assume that addresses not assigned to the interface
(`idx == -1` in the respective piece of code) are invalid: Other than
classic forwarding via a FIB, forwarded packets utilizing a IPv6
routing header will pass this check, like any other packet sent by this
node. The source address for these is not on the given node, so e.g.
source routing is not possible at the moment.
The IPv6 (extension) headers of the first fragment received are re-used
for the reassembled packet, so when receiving a subsequent packet we
need to distinguish, if we just want to release the payload or all of
the packet after the packet data was added to the reassembly buffer.
Without this change an attacker would be able to stop the emcute server
by sending a crafted packet triggering this branch. The solution is
using `continue` instead of `return`.
Due to some changes to the minimal forwarding draft and in preparation
for Selective Fragment Recovery some changes to the VRB API were
needed. Now the index of a VRB entry is only (L2 src, tag) not as
before (L2 src, L2 dst, length, tag).
I know that the current `rbuf_base` causes waste, as all the fields not
used by the new index are effectively not used by the VRB. I'd like to
fix that however in a later change, since that also requires some
modifications of the classic reassembly buffer, and thus would
complicate the review and testing of the change.
Sources for the index change:
- https://tools.ietf.org/html/draft-ietf-6lo-minimal-fragment-04#section-1
- https://mailarchive.ietf.org/arch/browse/6lo/?gbt=1&index=DLCTxC2X4bRNtYPHhtEkavMWlz4
This fixes a denial of service where an attacker would be able to cause
a NULL pointer dereference by sending a spoofed packet. This attack only
requires knowledge about pending message ids.
TCP options have up to three fields (kind, length, value). The
current code only checks for the presence of the first field. Before
accessing the second field (length) the code must ensure that a length
field is even present.
A received packet is outputted in DEBUG _after_ it was already parsed,
but with a reference to the already parsed header. The result is that
there can be some garbage in the output and the packet is not dumped in
total. As without parsing we do not have access to the header yet, we
use the `gnrc_netif_addr_to_str()` helper function instead of parsing
the destination address by hand.
before this commit the src address was checked for multicast, but the dst address should be checked. Therefore udp multicast packets would be flooded back to the src as ICMPv6 error, as not all nodes had a UDP receiver registered.
When a keepalive timeout occurs keepalive_retry_cnt remains zero,
so when the connection is re-established _on_keepalive_evt will
immediately disconnect instead of actually sending a keepalive ping.
The sequence looks like:
1. _on_connack: start con->keepalive_timer
2. Server does not respond to keepalive pings
3. _on_keepalive_evt: con->keepalive_retry_cnt reaches zero
4. Connection torn down and ASYMCUTE_DISCONNECTED sent to application
5. Application starts reconnection
6. _on_connack: start con->keepalive_timer again
7. First _on_keepalive_evt: con->keepalive_retry_cnt is still zero
8. Repeat from 4.
So this simply resets keepalive_retry_cnt in _on_connack when
the keepalive timer is restarted. It's a new connection, so
resetting the keepalive retry counter make senses regardless.
Signed-off-by: Derek Hageman <hageman@inthat.cloud>
From the gnrc_pktbuf_mark documentation:
It's not guaranteed that `result->data` points to the
same address as the original `pkt->data.
Thus it should be necessary to update the `hdr` pointer.
If an address was pre-configured by the upper layer its validity is
currently ignored. It is neither checked if the address is on the
interface at all nor is it checked if it is valid.
This change provides a fix for that by checking both facts.
The length field in an MQTT packet carries the _total_ length of the
packet. If it is below 256 (i.e. fits in one byte) only one byte is
used for the length field. If it is larger than that 3 bytes are used,
with the first byte having the value `0x01` and the remaining bytes
representing the length in as a 2 byte unsigned integer in network byte
order. Resulting from that it can be assessed that the check in
`emcutes`'s `set_len()` function is wrong as it needs to be checked if
`len` is lesser or equal to `0xff - 1`. `len <= (0xff - 1)` can be
simplified to `len < 0xff`. For some larger packages this safes 2 bytes
of wasted packet space.
`len` is used with the `memcpy()` to copy the payload to `tbuf`. With a
payload provided that is just long enough to fill `tbuf`, `len += 6`
leads to the `memcpy()` overriding data after `tbuf` (e.g. the
`mutex` that is unlocked right after) and thus resulting in potential
segmentation faults.
Additionally `+ 6` can only be applied if the total packet length is
below 256 (see spec), so `len + pos` is what needs to be provided to the
corresponding send functions instead (`pos` adapts to the header length
of the PUBLISH message).
When reworking the reception of IPv6 packets I reset a previously set
`ipv6` snip as follows when the IPv6 extension handler returns a
packet (see first hunk of this commit):
```C
ipv6 = pkt->next->next
```
With `gnrc_ipv6_ext` this makes *somewhat* sense, `pkt->next` was
previously equal to `ipv6` and after the function call `pkt->next`
is the marked extension header, while `pkt->next->next` is the IPv6
header. However, since `ipv6` is already write-protected i.e.
`ipv6->users == 1` (see ll. 665-675), any additional call of
`gnrc_pktbuf_start_write()` [won't][start-write-doc] duplicate the
packet. In fact, the only `gnrc_pktbuf_start_write()` in
`gnrc_ipv6_ext` is used to send the *result* to the subscribers of that
extension header type, leaving the original packet unchanged for the
caller. As such `ipv6` remains the pointer to the IPv6 header whether
we set it in the line above or not. So we actually don't need that
line.
However, the extension header handling also returns a packet when
`gnrc_ipv6_ext` is not compiled in. In that case it is just a dummy
define that returns the packet you give provide it which means that
this still holds true: `pkt->next == ipv6`.
So setting `ipv6` in this case is actually harmful, as `ipv6` now
points to the NETIF header [following the IPv6 header][pkt-structure]
in the packet and this causes the `user` counter of that NETIF header
`hdr` to be decremented if `hdr->users > 1` in the write-protection I
removed in hunk 2 of this commit:
```C
/* pkt might not be writable yet, if header was given above */
ipv6 = gnrc_pktbuf_start_write(ipv6);
if (ipv6 == NULL) {
DEBUG("ipv6: unable to get write access to packet: dropping it\n");
gnrc_pktbuf_release(pkt);
return;
}
```
But as we already established, `ipv6->users` is already 1, so we don't
actually need the write protection here either.
Since the packet stays unchanged after the `ipv6` snip, we also don't
need to re-search for `netif_hdr` after the other two lines are
removed.
[start-write-doc]: https://doc.riot-os.org/group__net__gnrc__pktbuf.html#ga640418467294ae3d408c109ab27bd617
[pkt-structure]: https://doc.riot-os.org/group__net__gnrc__pkt.html#ga278e783e56a5ee6f1bd7b81077ed82a7
The `addr` parameter of the NIB's `_handle_dad()` function can come
from anywhere (e.g. in the fallback to classic SLAAC the destination
address of the IP header is used), so putting that pointer in a timer
is not a good idea. Instead we use the version of the address that is
stored within the interface.