Prevent panic when no suitable source address is found. If no suitable
address is found, the loopback address is used instead. The function can
still panic when the destination address is unspecified.
More tests are added:
- Tests when the interface has no addresses. The loopback address is
used as source address.
- Tests when the interface only has a link-local address. The link-local
address is used as source address, unless the destination address is
the loopback address. In this case, the loopback address is used as
source address.
Splitting the accept and process functions into separate functions for
IPv4 and IPv6 allows us to avoid the `IpRepr` enum and instead use the
`Ipv4Repr` and `Ipv6Repr` structs directly. This reduces the binary size
by 1.5 KiB.
RFC6724 defines how the source address should be selected when given a
destination address. Instead of selecting the first address in the list
of interface addresses, the source address is selected following the
standard.
Lookup is O(n) now. However, it previously did 32 (or 128 for ipv6!)
map lookups. Since the route table typically doesn't have that many
routes, the new code is likely faster even if it's O(n).
This continues work started in #579, with the goal of "remove unspecified variants from wire".
"unspecified" variants are bad, they've been a source of bugs in the past. The issue with them is that
depending on the context it may or may not make sense for the value to be unspecified.
It's better to not have them, and then use Option only where the value can really be unspecified.
This removes lots of `Address::Unspecified => unreachable!()` and similar match arms, which shows the
unreachable variant was actively unwanted in many places.
This fixes the "unspecified src addr" panic:
- Picking src addr is now the resposibility of the sockets, not the interface. All sockets now emit IpReprs with properly assigned src addr.
- Removed the `assert!(!ip_repr.src_addr().is_unspecified());`. This assert is WRONG even if
now sockets pick the source address, because there ARE cases where we indeed want to send a
packet with zero src addr, for example in DHCP.
The intent was to run custom code after the user is done modifying the socket,
for example to update a (not yet existing) port->socket map in SocketSet. However
this wouldn't work, since the SocketRef would have to borrow the SocketSet at
the same time as the Socket to be able to notify the SocketSet.
I believe such indexing can be achieved by setting a "dirty" bit *before* giving
the socket to the user, then on poll() reindexing all dirty sockets. This could
even be faster: if user gets a socket multiple times between polls, it'd be reindexed
only once.
- Add `medium` in `DeviceCapabilities`.
- Rename EthernetInterface to Interface.
- Add support to Interface for both Ethernet and IP mediums. The medium to use is detected from `device.capabilities().medium`.
- Ethernet-only features are gated behind the "ethernet" feature, as before.
- IP features are always enabled for now.
These were flagged by `cargo clippy`:
warning: you seem to be trying to use match for destructuring a
single pattern. Consider using `if let`
warning: called `.nth(0)` on a `std::iter::Iterator`, when `.next()`
is equivalent
warning: using `write!()` with a format string that ends in a single
newline
warning: useless conversion to the same type:
`smoltcp::wire::Ipv4Address`
warning: called `map(f)` on an `Option` value where `f` is a closure
that returns the unit type `()`
warning: returning the result of a `let` binding from a block
warning: use of `unwrap_or` followed by a function call
The previous model was flawed. Consider the following case:
* The main loop looks as follows (pseudocode):
loop {
let _ = (tcp:1234).read_all()
wait(iface.poll())
}
* The remote end is continuously transmitting data and at some
point fills the window of (tcp:1234), stopping the transmission
afterwards.
* The local end processes the packets and, as a part of egress
routine, emits an ACK. That also updates the window, and
the socket's poll_at() routine returns None, since there is
nothing to transmit or retransmit.
* The local end now waits indefinitely even though it can start
processing the data in the socket buffers right now.
- Add the ttl member to the IpRepr
- Add the ttl member along with setters and getters to the tcp and udp
socket types
- Add unit tests for the new set_ttl parameter
- Update usage of IpRepr to include the ttl value
To be precise, I'm talking about IPX, AppleTalk and DECnet here,
not things like PPPoE, ATAoE, FCoE, or PTP, which make sense
to implement on top of EthernetInterface but do not work on
the same level on top of it as IP.