Skip to content

Adding SLAAC support #948

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

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions src/iface/interface/ipv6.rs
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ impl InterfaceInner {

pub(super) fn process_icmpv6<'frame>(
&mut self,
_sockets: &mut SocketSet,
sockets: &mut SocketSet,
ip_repr: Ipv6Repr,
ip_payload: &'frame [u8],
) -> Option<Packet<'frame>> {
Expand All @@ -360,7 +360,7 @@ impl InterfaceInner {
#[cfg(feature = "socket-icmp")]
{
use crate::socket::icmp::Socket as IcmpSocket;
for icmp_socket in _sockets
for icmp_socket in sockets
.items_mut()
.filter_map(|i| IcmpSocket::downcast_mut(&mut i.socket))
{
Expand Down Expand Up @@ -393,9 +393,9 @@ impl InterfaceInner {
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
Icmpv6Repr::Ndisc(repr) if ip_repr.hop_limit == 0xff => match self.caps.medium {
#[cfg(feature = "medium-ethernet")]
Medium::Ethernet => self.process_ndisc(ip_repr, repr),
Medium::Ethernet => self.process_ndisc(sockets, ip_repr, repr),
#[cfg(feature = "medium-ieee802154")]
Medium::Ieee802154 => self.process_ndisc(ip_repr, repr),
Medium::Ieee802154 => self.process_ndisc(sockets, ip_repr, repr),
#[cfg(feature = "medium-ip")]
Medium::Ip => None,
},
Expand All @@ -413,9 +413,20 @@ impl InterfaceInner {
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
pub(super) fn process_ndisc<'frame>(
&mut self,
sockets: &mut SocketSet,
ip_repr: Ipv6Repr,
repr: NdiscRepr<'frame>,
) -> Option<Packet<'frame>> {
// todo add feature slaac
{
use crate::socket::slaac::Socket as SlaacSocket;
if let Some(slaac_socket) = sockets
.items_mut()
.find_map(|i| SlaacSocket::downcast_mut(&mut i.socket))
{
slaac_socket.process(self, &ip_repr, &repr);
}
}
match repr {
NdiscRepr::NeighborAdvert {
lladdr,
Expand Down
9 changes: 9 additions & 0 deletions src/iface/interface/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ macro_rules! check {
};
}
use check;
use crate::iface::packet::IpPayload::Icmpv6;
use crate::wire::ip::Repr;

/// A network interface.
///
Expand Down Expand Up @@ -669,6 +671,13 @@ impl Interface {
Packet::new(ip, IpPayload::Udp(udp, dns)),
)
}),
Socket::Slaac(socket) => socket.dispatch(&mut self.inner, |inner, (ipv6_repr, ndisc)| {
respond(
inner,
PacketMeta::default(),
Packet::new_ipv6(ipv6_repr, Icmpv6(Icmpv6Repr::Ndisc(ndisc))),
)
}),
};

match result {
Expand Down
7 changes: 7 additions & 0 deletions src/socket/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ pub mod udp;

#[cfg(feature = "async")]
mod waker;
pub mod slaac;

#[cfg(feature = "async")]
pub(crate) use self::waker::WakerRegistration;
Expand Down Expand Up @@ -69,6 +70,8 @@ pub enum Socket<'a> {
Dhcpv4(dhcpv4::Socket<'a>),
#[cfg(feature = "socket-dns")]
Dns(dns::Socket<'a>),
// todo add feature
Slaac(slaac::Socket),
}

impl<'a> Socket<'a> {
Expand All @@ -86,6 +89,8 @@ impl<'a> Socket<'a> {
Socket::Dhcpv4(s) => s.poll_at(cx),
#[cfg(feature = "socket-dns")]
Socket::Dns(s) => s.poll_at(cx),
// todo
Socket::Slaac(s) => s.poll_at(cx),
}
}
}
Expand Down Expand Up @@ -139,3 +144,5 @@ from_socket!(tcp::Socket<'a>, Tcp);
from_socket!(dhcpv4::Socket<'a>, Dhcpv4);
#[cfg(feature = "socket-dns")]
from_socket!(dns::Socket<'a>, Dns);
// todo add feature
from_socket!(slaac::Socket, Slaac);
174 changes: 174 additions & 0 deletions src/socket/slaac.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
use core::task::Waker;
use crate::iface::Context;
use crate::socket::{PollAt, WakerRegistration};
use crate::socket::slaac::SlaacState::Renewing;
use crate::time::{Duration, Instant};
use crate::wire::{DhcpPacket, DhcpRepr, IpProtocol, Ipv6Address, Ipv6Cidr, Ipv6Repr, NdiscRepr, UdpRepr};

#[derive(Debug)]
struct DiscoveringState {
/// When to send next request
retry_at: Instant,
}
struct RequestState {
/// When to send next request
retry_at: Instant,
/// How many retries have been done
router: Ipv6Address,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Config {
/// IP address
pub cdir: Ipv6Cidr,
/// Router address, also known as default gateway
pub router: Option<Ipv6Address>,
/// DNS servers
pub dns_servers: heapless::Vec<Ipv6Address, { crate::wire::dhcpv4::MAX_DNS_SERVER_COUNT }>,
}

#[derive(Debug)]
struct RenewState {
// active network configuration
config: Config,

/// Renew timer. When reached, we will start attempting
/// to configure a new IPv6 address
///
/// Must be less or equal than `rebind_at`.
renew_at: Instant,

/// Expiration timer. When reached, this IPv6 address is no longer valid, so it must be
/// thrown away and the ethernet interface deconfigured.
expires_at: Instant,
}

#[derive(Debug)]
enum SlaacState {
/// waiting for router advertisement
Discovering(DiscoveringState),
Renewing(RenewState),
}

#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Event {
/// Configuration has been lost (for example, the lease has expired)
Deconfigured,
/// Configuration has been newly acquired, or modified.
Configured(Config),
}


#[derive(Debug)]
pub struct Socket {
state: SlaacState,

/// Waker registration
#[cfg(feature = "async")]
waker: WakerRegistration,
/// Set to true on config/state change, cleared back to false by the `config` function.
config_changed: bool,
}

impl Socket {
pub fn new() -> Self {
Self {
state: SlaacState::Discovering(DiscoveringState {
retry_at: Instant::from_secs(0),
}),
#[cfg(feature = "async")]
waker: WakerRegistration::new(),
config_changed: true,
}
}
}

impl Socket {
pub(crate) fn process(
&mut self,
cx: &mut Context,
ip_repr: &Ipv6Repr,
ndisc: &NdiscRepr,
) {
match (&mut self.state, ndisc) {
(SlaacState::Discovering(state), NdiscRepr::RouterAdvert {router_lifetime, prefix_info, .. }) => {
if let Some(prefix) = prefix_info {
self.config_changed();
self.state = Renewing(RenewState {
config: Config {
cdir: Ipv6Cidr::new(prefix.prefix, prefix.prefix_len),
router: None,
dns_servers: Default::default(),
},
renew_at: cx.now() + *router_lifetime / 2,
expires_at: cx.now() + *router_lifetime,
});
} else {
// does not contain prefix?!
}
}
(SlaacState::Renewing(_), _) => {}
_ => {}
}
}
pub(crate) fn dispatch<F, E>(&mut self, cx: &mut Context, emit: F) -> Result<(), E>
where
F: FnOnce(&mut Context, (Ipv6Repr, NdiscRepr)) -> Result<(), E>,
{
match &mut self.state {
SlaacState::Discovering(state) => {
state.retry_at = cx.now() + Duration::from_secs(5);
}
SlaacState::Renewing(state) => {
state.expires_at = cx.now() + Duration::from_secs(5);
}
}
Ok(())
}
}

impl Socket {
pub(crate) fn poll_at(&self, _cx: &mut Context) -> PollAt {
let t = match &self.state {
SlaacState::Discovering(discover) => discover.retry_at,
SlaacState::Renewing(renew) => renew.renew_at,
};
PollAt::Time(t)
}

/// Query the socket for configuration changes.
///
/// The socket has an internal "configuration changed" flag. If
/// set, this function returns the configuration and resets the flag.
pub fn poll(&mut self) -> Option<Event> {
if !self.config_changed {
return None
}
if let SlaacState::Renewing(state) = &self.state {
self.config_changed = false;
Some(Event::Configured(Config {
cdir: state.config.cdir,
router: state.config.router,
dns_servers: state.config.dns_servers.clone()
}))
} else {
self.config_changed = false;
Some(Event::Deconfigured)
}
}

/// This function _must_ be called when the configuration provided to the
/// interface, changes. It will update the `config_changed` field
/// so that a subsequent call to `poll` will yield an event, and wake a possible waker.
pub(crate) fn config_changed(&mut self) {
self.config_changed = true;
#[cfg(feature = "async")]
self.waker.wake();
}

#[cfg(feature = "async")]
pub fn register_waker(&mut self, waker: &Waker) {
self.waker.register(waker)
}
}
Loading