wasmer_wasix/net/
mod.rs

1use std::{
2    mem::transmute,
3    net::{IpAddr, Ipv4Addr, Ipv6Addr},
4    time::Duration,
5};
6
7use virtual_net::{IpCidr, IpRoute, NetworkError};
8use wasmer::{MemoryView, WasmPtr};
9use wasmer_types::MemorySize;
10use wasmer_wasix_types::{
11    types::{
12        __wasi_addr_ip4_t, __wasi_addr_ip6_t, __wasi_addr_port_t, __wasi_addr_port_u,
13        __wasi_addr_t, __wasi_addr_u, __wasi_cidr_t, __wasi_cidr_u, OptionTag, OptionTimestamp,
14        Route,
15    },
16    wasi::{Addressfamily, Errno},
17};
18
19pub mod socket;
20
21pub use virtual_net::MAX_SOCKET_PAYLOAD;
22
23#[allow(dead_code)]
24pub(crate) fn read_ip<M: MemorySize>(
25    memory: &MemoryView,
26    ptr: WasmPtr<__wasi_addr_t, M>,
27) -> Result<IpAddr, Errno> {
28    let addr_ptr = ptr.deref(memory);
29    let addr = addr_ptr.read().map_err(crate::mem_error_to_wasi)?;
30
31    let o = addr.u.octs;
32    Ok(match addr.tag {
33        Addressfamily::Inet4 => IpAddr::V4(Ipv4Addr::new(o[0], o[1], o[2], o[3])),
34        Addressfamily::Inet6 => {
35            let [a, b, c, d, e, f, g, h] = unsafe { transmute::<[u8; 16], [u16; 8]>(o) };
36            IpAddr::V6(Ipv6Addr::new(a, b, c, d, e, f, g, h))
37        }
38        _ => return Err(Errno::Inval),
39    })
40}
41
42pub(crate) fn read_ip_v4<M: MemorySize>(
43    memory: &MemoryView,
44    ptr: WasmPtr<__wasi_addr_ip4_t, M>,
45) -> Result<Ipv4Addr, Errno> {
46    let addr_ptr = ptr.deref(memory);
47    let addr = addr_ptr.read().map_err(crate::mem_error_to_wasi)?;
48
49    let o = addr.octs;
50    Ok(Ipv4Addr::new(o[0], o[1], o[2], o[3]))
51}
52
53pub(crate) fn read_ip_v6<M: MemorySize>(
54    memory: &MemoryView,
55    ptr: WasmPtr<__wasi_addr_ip6_t, M>,
56) -> Result<Ipv6Addr, Errno> {
57    let addr_ptr = ptr.deref(memory);
58    let addr = addr_ptr.read().map_err(crate::mem_error_to_wasi)?;
59
60    let [a, b, c, d, e, f, g, h] = unsafe { transmute::<[u8; 16], [u16; 8]>(addr.segs) };
61    Ok(Ipv6Addr::new(a, b, c, d, e, f, g, h))
62}
63
64pub fn write_ip<M: MemorySize>(
65    memory: &MemoryView,
66    ptr: WasmPtr<__wasi_addr_t, M>,
67    ip: IpAddr,
68) -> Result<(), Errno> {
69    let ip = match ip {
70        IpAddr::V4(ip) => {
71            let o = ip.octets();
72            __wasi_addr_t {
73                tag: Addressfamily::Inet4,
74                _padding: 0,
75                u: __wasi_addr_u {
76                    octs: [o[0], o[1], o[2], o[3], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
77                },
78            }
79        }
80        IpAddr::V6(ip) => {
81            let o = ip.octets();
82            __wasi_addr_t {
83                tag: Addressfamily::Inet6,
84                _padding: 0,
85                u: __wasi_addr_u { octs: o },
86            }
87        }
88    };
89
90    let addr_ptr = ptr.deref(memory);
91    addr_ptr.write(ip).map_err(crate::mem_error_to_wasi)?;
92    Ok(())
93}
94
95#[allow(dead_code)]
96pub(crate) fn read_cidr<M: MemorySize>(
97    memory: &MemoryView,
98    ptr: WasmPtr<__wasi_cidr_t, M>,
99) -> Result<IpCidr, Errno> {
100    let addr_ptr = ptr.deref(memory);
101    let addr = addr_ptr.read().map_err(crate::mem_error_to_wasi)?;
102
103    let o = addr.u.octs;
104    Ok(match addr.tag {
105        Addressfamily::Inet4 => IpCidr {
106            ip: IpAddr::V4(Ipv4Addr::new(o[0], o[1], o[2], o[3])),
107            prefix: o[4],
108        },
109        Addressfamily::Inet6 => {
110            let [a, b, c, d, e, f, g, h] = {
111                let o = [
112                    o[0], o[1], o[2], o[3], o[4], o[5], o[6], o[7], o[8], o[9], o[10], o[11],
113                    o[12], o[13], o[14], o[15],
114                ];
115                unsafe { transmute::<[u8; 16], [u16; 8]>(o) }
116            };
117            IpCidr {
118                ip: IpAddr::V6(Ipv6Addr::new(a, b, c, d, e, f, g, h)),
119                prefix: o[16],
120            }
121        }
122        _ => return Err(Errno::Inval),
123    })
124}
125
126#[allow(dead_code)]
127pub(crate) fn write_cidr<M: MemorySize>(
128    memory: &MemoryView,
129    ptr: WasmPtr<__wasi_cidr_t, M>,
130    cidr: IpCidr,
131) -> Result<(), Errno> {
132    let p = cidr.prefix;
133    let cidr = match cidr.ip {
134        IpAddr::V4(ip) => {
135            let o = ip.octets();
136            __wasi_cidr_t {
137                tag: Addressfamily::Inet4,
138                _padding: 0,
139                u: __wasi_cidr_u {
140                    octs: [
141                        o[0], o[1], o[2], o[3], p, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
142                    ],
143                },
144            }
145        }
146        IpAddr::V6(ip) => {
147            let o = ip.octets();
148            __wasi_cidr_t {
149                tag: Addressfamily::Inet6,
150                _padding: 0,
151                u: __wasi_cidr_u {
152                    octs: [
153                        o[0], o[1], o[2], o[3], o[4], o[5], o[6], o[7], o[8], o[9], o[10], o[11],
154                        o[12], o[13], o[14], o[15], p,
155                    ],
156                },
157            }
158        }
159    };
160
161    let addr_ptr = ptr.deref(memory);
162    addr_ptr.write(cidr).map_err(crate::mem_error_to_wasi)?;
163    Ok(())
164}
165
166pub(crate) fn read_ip_port<M: MemorySize>(
167    memory: &MemoryView,
168    ptr: WasmPtr<__wasi_addr_port_t, M>,
169) -> Result<(IpAddr, u16), Errno> {
170    let addr_ptr = ptr.deref(memory);
171    let addr = addr_ptr.read().map_err(crate::mem_error_to_wasi)?;
172    let o = addr.u.octs;
173    Ok(match addr.tag {
174        Addressfamily::Inet4 => {
175            let port = u16::from_ne_bytes([o[0], o[1]]);
176            (IpAddr::V4(Ipv4Addr::new(o[2], o[3], o[4], o[5])), port)
177        }
178        Addressfamily::Inet6 => {
179            let octets: [u8; 16] = o[2..18].try_into().unwrap();
180            (
181                IpAddr::V6(Ipv6Addr::from(octets)),
182                u16::from_ne_bytes([o[0], o[1]]),
183            )
184        }
185        _ => {
186            tracing::debug!("invalid address family ({})", addr.tag as u8);
187            return Err(Errno::Inval);
188        }
189    })
190}
191
192#[allow(dead_code)]
193pub(crate) fn write_ip_port<M: MemorySize>(
194    memory: &MemoryView,
195    ptr: WasmPtr<__wasi_addr_port_t, M>,
196    ip: IpAddr,
197    port: u16,
198) -> Result<(), Errno> {
199    let p = port.to_be_bytes();
200    let ipport = match ip {
201        IpAddr::V4(ip) => {
202            let o = ip.octets();
203            __wasi_addr_port_t {
204                tag: Addressfamily::Inet4,
205                _padding: 0,
206                u: __wasi_addr_port_u {
207                    octs: [
208                        p[0], p[1], o[0], o[1], o[2], o[3], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
209                    ],
210                },
211            }
212        }
213        IpAddr::V6(ip) => {
214            let o = ip.octets();
215            __wasi_addr_port_t {
216                tag: Addressfamily::Inet6,
217                _padding: 0,
218                u: __wasi_addr_port_u {
219                    octs: [
220                        p[0], p[1], o[0], o[1], o[2], o[3], o[4], o[5], o[6], o[7], o[8], o[9],
221                        o[10], o[11], o[12], o[13], o[14], o[15],
222                    ],
223                },
224            }
225        }
226    };
227
228    let addr_ptr = ptr.deref(memory);
229    addr_ptr.write(ipport).map_err(crate::mem_error_to_wasi)?;
230    Ok(())
231}
232
233#[allow(dead_code)]
234pub(crate) fn read_route<M: MemorySize>(
235    memory: &MemoryView,
236    ptr: WasmPtr<Route, M>,
237) -> Result<IpRoute, Errno> {
238    let route_ptr = ptr.deref(memory);
239    let route = route_ptr.read().map_err(crate::mem_error_to_wasi)?;
240
241    Ok(IpRoute {
242        cidr: {
243            let o = route.cidr.u.octs;
244            match route.cidr.tag {
245                Addressfamily::Inet4 => IpCidr {
246                    ip: IpAddr::V4(Ipv4Addr::new(o[0], o[1], o[2], o[3])),
247                    prefix: o[4],
248                },
249                Addressfamily::Inet6 => {
250                    let [a, b, c, d, e, f, g, h] = {
251                        let o = [
252                            o[0], o[1], o[2], o[3], o[4], o[5], o[6], o[7], o[8], o[9], o[10],
253                            o[11], o[12], o[13], o[14], o[15],
254                        ];
255                        unsafe { transmute::<[u8; 16], [u16; 8]>(o) }
256                    };
257                    IpCidr {
258                        ip: IpAddr::V6(Ipv6Addr::new(a, b, c, d, e, f, g, h)),
259                        prefix: o[16],
260                    }
261                }
262                _ => return Err(Errno::Inval),
263            }
264        },
265        via_router: {
266            let o = route.via_router.u.octs;
267            match route.via_router.tag {
268                Addressfamily::Inet4 => IpAddr::V4(Ipv4Addr::new(o[0], o[1], o[2], o[3])),
269                Addressfamily::Inet6 => {
270                    let [a, b, c, d, e, f, g, h] = unsafe { transmute::<[u8; 16], [u16; 8]>(o) };
271                    IpAddr::V6(Ipv6Addr::new(a, b, c, d, e, f, g, h))
272                }
273                _ => return Err(Errno::Inval),
274            }
275        },
276        preferred_until: match route.preferred_until.tag {
277            OptionTag::None => None,
278            OptionTag::Some => Some(Duration::from_nanos(route.preferred_until.u)),
279            _ => return Err(Errno::Inval),
280        },
281        expires_at: match route.expires_at.tag {
282            OptionTag::None => None,
283            OptionTag::Some => Some(Duration::from_nanos(route.expires_at.u)),
284            _ => return Err(Errno::Inval),
285        },
286    })
287}
288
289pub(crate) fn write_route<M: MemorySize>(
290    memory: &MemoryView,
291    ptr: WasmPtr<Route, M>,
292    route: IpRoute,
293) -> Result<(), Errno> {
294    let cidr = {
295        let p = route.cidr.prefix;
296        match route.cidr.ip {
297            IpAddr::V4(ip) => {
298                let o = ip.octets();
299                __wasi_cidr_t {
300                    tag: Addressfamily::Inet4,
301                    _padding: 0,
302                    u: __wasi_cidr_u {
303                        octs: [
304                            o[0], o[1], o[2], o[3], p, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
305                        ],
306                    },
307                }
308            }
309            IpAddr::V6(ip) => {
310                let o = ip.octets();
311                __wasi_cidr_t {
312                    tag: Addressfamily::Inet6,
313                    _padding: 0,
314                    u: __wasi_cidr_u {
315                        octs: [
316                            o[0], o[1], o[2], o[3], o[4], o[5], o[6], o[7], o[8], o[9], o[10],
317                            o[11], o[12], o[13], o[14], o[15], p,
318                        ],
319                    },
320                }
321            }
322        }
323    };
324    let via_router = match route.via_router {
325        IpAddr::V4(ip) => {
326            let o = ip.octets();
327            __wasi_addr_t {
328                tag: Addressfamily::Inet4,
329                _padding: 0,
330                u: __wasi_addr_u {
331                    octs: [o[0], o[1], o[2], o[3], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
332                },
333            }
334        }
335        IpAddr::V6(ip) => {
336            let o = ip.octets();
337            __wasi_addr_t {
338                tag: Addressfamily::Inet6,
339                _padding: 0,
340                u: __wasi_addr_u { octs: o },
341            }
342        }
343    };
344    let preferred_until = match route.preferred_until {
345        None => OptionTimestamp {
346            tag: OptionTag::None,
347            u: 0,
348        },
349        Some(u) => OptionTimestamp {
350            tag: OptionTag::Some,
351            u: u.as_nanos() as u64,
352        },
353    };
354    let expires_at = match route.expires_at {
355        None => OptionTimestamp {
356            tag: OptionTag::None,
357            u: 0,
358        },
359        Some(u) => OptionTimestamp {
360            tag: OptionTag::Some,
361            u: u.as_nanos() as u64,
362        },
363    };
364
365    let route = Route {
366        cidr,
367        via_router,
368        preferred_until,
369        expires_at,
370    };
371
372    let route_ptr = ptr.deref(memory);
373    route_ptr.write(route).map_err(crate::mem_error_to_wasi)?;
374    Ok(())
375}
376
377pub fn net_error_into_wasi_err(net_error: NetworkError) -> Errno {
378    match net_error {
379        NetworkError::InvalidFd => Errno::Badf,
380        NetworkError::AlreadyExists => Errno::Exist,
381        NetworkError::Lock => Errno::Io,
382        NetworkError::IOError => Errno::Io,
383        NetworkError::AddressInUse => Errno::Addrinuse,
384        NetworkError::AddressNotAvailable => Errno::Addrnotavail,
385        NetworkError::BrokenPipe => Errno::Pipe,
386        NetworkError::ConnectionAborted => Errno::Connaborted,
387        NetworkError::ConnectionRefused => Errno::Connrefused,
388        NetworkError::ConnectionReset => Errno::Connreset,
389        NetworkError::Interrupted => Errno::Intr,
390        NetworkError::InvalidData => Errno::Io,
391        NetworkError::InvalidInput => Errno::Inval,
392        NetworkError::MessageSize => Errno::Msgsize,
393        NetworkError::NotConnected => Errno::Notconn,
394        NetworkError::NoDevice => Errno::Nodev,
395        NetworkError::PermissionDenied => Errno::Perm,
396        NetworkError::TimedOut => Errno::Timedout,
397        NetworkError::UnexpectedEof => Errno::Proto,
398        NetworkError::WouldBlock => Errno::Again,
399        NetworkError::WriteZero => Errno::Nospc,
400        NetworkError::TooManyOpenFiles => Errno::Mfile,
401        NetworkError::InsufficientMemory => Errno::Nomem,
402        NetworkError::Unsupported => Errno::Notsup,
403        NetworkError::UnknownError => Errno::Io,
404    }
405}