Skip to main content

embedded_svc/
ipv4.rs

1use core::fmt::Display;
2use core::str::FromStr;
3
4/// For backwards compatibility. Might be removed in future versions.
5pub use core::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
6
7#[cfg(feature = "use_serde")]
8use serde::{Deserialize, Serialize};
9
10#[derive(Copy, Clone, Debug, Eq, PartialEq)]
11#[cfg_attr(feature = "defmt", derive(defmt::Format))]
12#[cfg_attr(feature = "std", derive(Hash))]
13#[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))]
14pub struct Mask(pub u8);
15
16impl FromStr for Mask {
17    type Err = &'static str;
18
19    fn from_str(s: &str) -> Result<Self, Self::Err> {
20        s.parse::<u8>()
21            .map_err(|_| "Invalid subnet mask")
22            .map_or_else(Err, |mask| {
23                if (1..=32).contains(&mask) {
24                    Ok(Mask(mask))
25                } else {
26                    Err("Mask should be a number between 1 and 32")
27                }
28            })
29    }
30}
31
32impl Display for Mask {
33    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
34        write!(f, "{}", self.0)
35    }
36}
37
38impl TryFrom<Ipv4Addr> for Mask {
39    type Error = ();
40
41    fn try_from(ip: Ipv4Addr) -> Result<Self, Self::Error> {
42        let octets = ip.octets();
43        let addr: u32 = ((octets[0] as u32 & 0xff) << 24)
44            | ((octets[1] as u32 & 0xff) << 16)
45            | ((octets[2] as u32 & 0xff) << 8)
46            | (octets[3] as u32 & 0xff);
47
48        if addr.leading_ones() + addr.trailing_zeros() == 32 {
49            Ok(Mask(addr.leading_ones() as u8))
50        } else {
51            Err(())
52        }
53    }
54}
55
56impl From<Mask> for Ipv4Addr {
57    fn from(mask: Mask) -> Self {
58        let addr: u32 = ((1 << (32 - mask.0)) - 1) ^ 0xffffffffu32;
59
60        let (a, b, c, d) = (
61            ((addr >> 24) & 0xff) as u8,
62            ((addr >> 16) & 0xff) as u8,
63            ((addr >> 8) & 0xff) as u8,
64            (addr & 0xff) as u8,
65        );
66
67        Ipv4Addr::new(a, b, c, d)
68    }
69}
70
71#[derive(Copy, Clone, Debug, Eq, PartialEq)]
72#[cfg_attr(feature = "defmt", derive(defmt::Format))]
73#[cfg_attr(feature = "std", derive(Hash))]
74#[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))]
75pub struct Subnet {
76    #[cfg_attr(feature = "defmt", defmt(Debug2Format))]
77    #[cfg_attr(feature = "use_serde", serde(serialize_with = "ipv4_serialize"))]
78    #[cfg_attr(feature = "use_serde", serde(deserialize_with = "ipv4_deserialize"))]
79    pub gateway: Ipv4Addr,
80    pub mask: Mask,
81}
82
83impl Display for Subnet {
84    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
85        write!(f, "{}/{}", self.gateway, self.mask)
86    }
87}
88
89impl FromStr for Subnet {
90    type Err = &'static str;
91
92    fn from_str(s: &str) -> Result<Self, Self::Err> {
93        let mut split = s.split('/');
94        if let Some(gateway_str) = split.next() {
95            if let Some(mask_str) = split.next() {
96                if split.next().is_none() {
97                    if let Ok(gateway) = gateway_str.parse::<Ipv4Addr>() {
98                        return mask_str.parse::<Mask>().map(|mask| Self { gateway, mask });
99                    } else {
100                        return Err("Invalid IP address format, expected XXX.XXX.XXX.XXX");
101                    }
102                }
103            }
104        }
105
106        Err("Expected <gateway-ip-address>/<mask>")
107    }
108}
109
110#[derive(Copy, Clone, Debug, Eq, PartialEq)]
111#[cfg_attr(feature = "defmt", derive(defmt::Format))]
112#[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))]
113pub struct ClientSettings {
114    #[cfg_attr(feature = "defmt", defmt(Debug2Format))]
115    #[cfg_attr(feature = "use_serde", serde(serialize_with = "ipv4_serialize"))]
116    #[cfg_attr(feature = "use_serde", serde(deserialize_with = "ipv4_deserialize"))]
117    pub ip: Ipv4Addr,
118    pub subnet: Subnet,
119    #[cfg_attr(feature = "defmt", defmt(Debug2Format))]
120    #[cfg_attr(feature = "use_serde", serde(serialize_with = "ipv4_opt_serialize"))]
121    #[cfg_attr(
122        feature = "use_serde",
123        serde(deserialize_with = "ipv4_opt_deserialize")
124    )]
125    pub dns: Option<Ipv4Addr>,
126    #[cfg_attr(feature = "defmt", defmt(Debug2Format))]
127    #[cfg_attr(feature = "use_serde", serde(serialize_with = "ipv4_opt_serialize"))]
128    #[cfg_attr(
129        feature = "use_serde",
130        serde(deserialize_with = "ipv4_opt_deserialize")
131    )]
132    pub secondary_dns: Option<Ipv4Addr>,
133}
134
135impl Default for ClientSettings {
136    fn default() -> ClientSettings {
137        ClientSettings {
138            ip: Ipv4Addr::new(192, 168, 71, 200),
139            subnet: Subnet {
140                gateway: Ipv4Addr::new(192, 168, 71, 1),
141                mask: Mask(24),
142            },
143            dns: Some(Ipv4Addr::new(8, 8, 8, 8)),
144            secondary_dns: Some(Ipv4Addr::new(8, 8, 4, 4)),
145        }
146    }
147}
148
149#[derive(Default, Clone, Debug, PartialEq, Eq)]
150#[cfg_attr(feature = "defmt", derive(defmt::Format))]
151#[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))]
152pub struct DHCPClientSettings {
153    pub hostname: Option<heapless::String<30>>,
154}
155
156#[derive(Clone, Debug, PartialEq, Eq)]
157#[cfg_attr(feature = "defmt", derive(defmt::Format))]
158#[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))]
159pub enum ClientConfiguration {
160    DHCP(DHCPClientSettings),
161    Fixed(ClientSettings),
162}
163
164impl ClientConfiguration {
165    pub fn as_fixed_settings_ref(&self) -> Option<&ClientSettings> {
166        match self {
167            Self::Fixed(client_settings) => Some(client_settings),
168            _ => None,
169        }
170    }
171
172    pub fn as_fixed_settings_mut(&mut self) -> &mut ClientSettings {
173        match self {
174            Self::Fixed(client_settings) => client_settings,
175            _ => {
176                *self = ClientConfiguration::Fixed(Default::default());
177                self.as_fixed_settings_mut()
178            }
179        }
180    }
181}
182
183impl Default for ClientConfiguration {
184    fn default() -> ClientConfiguration {
185        ClientConfiguration::DHCP(Default::default())
186    }
187}
188
189#[derive(Clone, Debug, Eq, PartialEq)]
190#[cfg_attr(feature = "defmt", derive(defmt::Format))]
191#[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))]
192pub struct RouterConfiguration {
193    pub subnet: Subnet,
194    pub dhcp_enabled: bool,
195    #[cfg_attr(feature = "defmt", defmt(Debug2Format))]
196    #[cfg_attr(feature = "use_serde", serde(serialize_with = "ipv4_opt_serialize"))]
197    #[cfg_attr(
198        feature = "use_serde",
199        serde(deserialize_with = "ipv4_opt_deserialize")
200    )]
201    pub dns: Option<Ipv4Addr>,
202    #[cfg_attr(feature = "defmt", defmt(Debug2Format))]
203    #[cfg_attr(feature = "use_serde", serde(serialize_with = "ipv4_opt_serialize"))]
204    #[cfg_attr(
205        feature = "use_serde",
206        serde(deserialize_with = "ipv4_opt_deserialize")
207    )]
208    pub secondary_dns: Option<Ipv4Addr>,
209}
210
211impl Default for RouterConfiguration {
212    fn default() -> RouterConfiguration {
213        RouterConfiguration {
214            subnet: Subnet {
215                gateway: Ipv4Addr::new(192, 168, 71, 1),
216                mask: Mask(24),
217            },
218            dhcp_enabled: true,
219            dns: Some(Ipv4Addr::new(8, 8, 8, 8)),
220            secondary_dns: Some(Ipv4Addr::new(8, 8, 4, 4)),
221        }
222    }
223}
224
225#[derive(Clone, Debug, Eq, PartialEq)]
226#[cfg_attr(feature = "defmt", derive(defmt::Format))]
227#[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))]
228pub enum Configuration {
229    Client(ClientConfiguration),
230    Router(RouterConfiguration),
231}
232
233impl Default for Configuration {
234    fn default() -> Self {
235        Self::Client(Default::default())
236    }
237}
238
239#[derive(Copy, Clone, Debug, Eq, PartialEq)]
240#[cfg_attr(feature = "defmt", derive(defmt::Format))]
241#[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))]
242pub struct IpInfo {
243    #[cfg_attr(feature = "defmt", defmt(Debug2Format))]
244    #[cfg_attr(feature = "use_serde", serde(serialize_with = "ipv4_serialize"))]
245    #[cfg_attr(feature = "use_serde", serde(deserialize_with = "ipv4_deserialize"))]
246    pub ip: Ipv4Addr,
247    pub subnet: Subnet,
248    #[cfg_attr(feature = "defmt", defmt(Debug2Format))]
249    #[cfg_attr(feature = "use_serde", serde(serialize_with = "ipv4_opt_serialize"))]
250    #[cfg_attr(
251        feature = "use_serde",
252        serde(deserialize_with = "ipv4_opt_deserialize")
253    )]
254    pub dns: Option<Ipv4Addr>,
255    #[cfg_attr(feature = "defmt", defmt(Debug2Format))]
256    #[cfg_attr(feature = "use_serde", serde(serialize_with = "ipv4_opt_serialize"))]
257    #[cfg_attr(
258        feature = "use_serde",
259        serde(deserialize_with = "ipv4_opt_deserialize")
260    )]
261    pub secondary_dns: Option<Ipv4Addr>,
262}
263
264pub trait Interface {
265    type Error;
266
267    fn get_iface_configuration(&self) -> Result<Configuration, Self::Error>;
268    fn set_iface_configuration(&mut self, conf: &Configuration) -> Result<(), Self::Error>;
269
270    fn is_iface_up(&self) -> bool;
271
272    fn get_ip_info(&self) -> Result<IpInfo, Self::Error>;
273}
274
275#[cfg(feature = "use_serde")]
276fn ipv4_serialize<S>(ipv4: &Ipv4Addr, serializer: S) -> Result<S::Ok, S::Error>
277where
278    S: serde::Serializer,
279{
280    ipv4.octets().serialize(serializer)
281}
282
283#[cfg(feature = "use_serde")]
284fn ipv4_deserialize<'de, D>(deserializer: D) -> Result<Ipv4Addr, D::Error>
285where
286    D: serde::Deserializer<'de>,
287{
288    <[u8; 4]>::deserialize(deserializer).map(Ipv4Addr::from)
289}
290
291#[cfg(feature = "use_serde")]
292fn ipv4_opt_serialize<S>(ipv4: &Option<Ipv4Addr>, serializer: S) -> Result<S::Ok, S::Error>
293where
294    S: serde::Serializer,
295{
296    ipv4.map(|ip| ip.octets()).serialize(serializer)
297}
298
299#[cfg(feature = "use_serde")]
300fn ipv4_opt_deserialize<'de, D>(deserializer: D) -> Result<Option<Ipv4Addr>, D::Error>
301where
302    D: serde::Deserializer<'de>,
303{
304    <Option<[u8; 4]>>::deserialize(deserializer).map(|octets| octets.map(Ipv4Addr::from))
305}