openthread_fuchsia/backing/
nat64.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use super::*;
6use crate::Platform;
7use crate::ot::{InfraInterface, Ip4Address, Ip6Address, Ip6Prefix};
8use anyhow::{Error, Result};
9use fuchsia_sync::Mutex;
10use futures::future::BoxFuture;
11use openthread_sys::*;
12use std::task::{Context, Poll};
13
14pub(crate) struct Nat64Instance {
15    nat64_prefix_req_sender: Mutex<
16        fmpsc::UnboundedSender<Mutex<Option<BoxFuture<'static, (ot::NetifIndex, Ip6Prefix)>>>>,
17    >,
18}
19
20const K_VALID_NAT64_PREFIX_LENGTHS: [u8; 6] = [96, 64, 56, 48, 40, 32];
21const K_WELL_KNOWN_IPV4_ONLY_ADDRESS1: Ip4Address = Ip4Address::new(192, 0, 0, 170);
22const K_WELL_KNOWN_IPV4_ONLY_ADDRESS2: Ip4Address = Ip4Address::new(192, 0, 0, 171);
23
24pub(crate) struct Nat64PlatformInstance {
25    nat64_prefix_req_receiver:
26        fmpsc::UnboundedReceiver<Mutex<Option<BoxFuture<'static, (ot::NetifIndex, Ip6Prefix)>>>>,
27    nat64_pending_fut: Mutex<Option<BoxFuture<'static, (ot::NetifIndex, Ip6Prefix)>>>,
28}
29
30impl Nat64Instance {
31    pub fn new(
32        nat64_prefix_req_sender: fmpsc::UnboundedSender<
33            Mutex<Option<BoxFuture<'static, (ot::NetifIndex, Ip6Prefix)>>>,
34        >,
35    ) -> Nat64Instance {
36        Nat64Instance { nat64_prefix_req_sender: Mutex::new(nat64_prefix_req_sender) }
37    }
38}
39
40impl Nat64PlatformInstance {
41    pub fn new(
42        nat64_prefix_req_receiver: fmpsc::UnboundedReceiver<
43            Mutex<Option<BoxFuture<'static, (ot::NetifIndex, Ip6Prefix)>>>,
44        >,
45    ) -> Nat64PlatformInstance {
46        Nat64PlatformInstance { nat64_prefix_req_receiver, nat64_pending_fut: Mutex::new(None) }
47    }
48}
49
50// The prefix length must be 32, 40, 48, 56, 64, 96. IPv4 bytes are added
51// after the prefix, skipping over the bits 64 to 71 (byte at index `8`)
52// which must be set to zero. The suffix is set to zero (per RFC 6052).
53//
54//    +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
55//    |PL| 0-------------32--40--48--56--64--72--80--88--96--104---------|
56//    +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
57//    |32|     prefix    |v4(32)         | u | suffix                    |
58//    +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
59//    |40|     prefix        |v4(24)     | u |(8)| suffix                |
60//    +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
61//    |48|     prefix            |v4(16) | u | (16)  | suffix            |
62//    +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
63//    |56|     prefix                |(8)| u |  v4(24)   | suffix        |
64//    +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
65//    |64|     prefix                    | u |   v4(32)      | suffix    |
66//    +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
67//    |96|     prefix                                    |    v4(32)     |
68//    +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
69fn extract_ipv4_addr_from_ipv6(
70    ip6_addr: std::net::Ipv6Addr,
71    prefix_length: u8,
72) -> std::net::Ipv4Addr {
73    // Check the prefix_length is valid. If not, don't extract
74    if !K_VALID_NAT64_PREFIX_LENGTHS.contains(&prefix_length) {
75        return std::net::Ipv4Addr::new(0, 0, 0, 0);
76    }
77
78    let ip6_vec = ip6_addr.octets();
79
80    // get the first idx
81    let mut ip6_idx = (prefix_length / 8) as usize;
82    let mut ip4_idx: usize = 0;
83
84    let mut res: [u8; 4] = [0; 4];
85
86    while ip4_idx < 4 {
87        if ip6_idx == 8 {
88            ip6_idx += 1;
89            continue;
90        }
91        res[ip4_idx] = ip6_vec[ip6_idx];
92        ip4_idx += 1;
93        ip6_idx += 1;
94    }
95
96    std::net::Ipv4Addr::from(res)
97}
98
99// Get prefix from the IPv6 address
100fn get_ipv6_prefix(
101    ip_addr: std::net::Ipv6Addr,
102    prefix_length: usize,
103) -> Option<std::net::Ipv6Addr> {
104    if prefix_length > 128 {
105        return None;
106    }
107
108    let mut res = [0u16; 8];
109    let ip_seg = ip_addr.segments();
110    let (byte_count, reminder) = (prefix_length / 16, prefix_length % 16);
111    let mut res_idx = 0;
112    while res_idx < byte_count {
113        res[res_idx] = ip_seg[res_idx];
114        res_idx += 1;
115    }
116    if reminder > 0 && reminder < 16 {
117        let mask = ((1 << reminder) - 1) << (16 - reminder);
118        res[res_idx] = ip_seg[res_idx] & mask;
119    }
120    Some(std::net::Ipv6Addr::from(res))
121}
122
123// Check "ipv4only.arpa" via Adjacent Infrastructure Link.
124// Equivalent to posix implementation in "InfraNetif::DiscoverNat64PrefixDone()"
125fn process_ail_dns_lookup_result(
126    ip_vec: Vec<fidl_fuchsia_net::IpAddress>,
127) -> Result<Ip6Prefix, Error> {
128    let mut prefix: Option<Ip6Prefix> = None;
129    for ip in ip_vec {
130        if let fidl_fuchsia_net::IpAddress::Ipv6(ip_addr) = ip {
131            let ip6_address: Ip6Address = ip_addr.addr.into();
132            for length in K_VALID_NAT64_PREFIX_LENGTHS {
133                let ip4_address = extract_ipv4_addr_from_ipv6(ip6_address, length);
134                if ip4_address.eq(&K_WELL_KNOWN_IPV4_ONLY_ADDRESS1)
135                    || ip4_address.eq(&K_WELL_KNOWN_IPV4_ONLY_ADDRESS2)
136                {
137                    let mut found_duplicate = false;
138                    for dup_length in K_VALID_NAT64_PREFIX_LENGTHS {
139                        if dup_length == length {
140                            continue;
141                        }
142                        let ip4_address_dup = extract_ipv4_addr_from_ipv6(ip6_address, dup_length);
143
144                        if ip4_address_dup.eq(&ip4_address) {
145                            found_duplicate = true;
146                            break;
147                        }
148                    }
149                    if !found_duplicate {
150                        if let Some(ip6_prefix_addr) = get_ipv6_prefix(ip6_address, length.into()) {
151                            prefix = Some(Ip6Prefix::new(ip6_prefix_addr, length));
152                            break;
153                        }
154                    }
155                }
156                if prefix.is_some() {
157                    break;
158                }
159            }
160        }
161    }
162    match prefix {
163        Some(p) => Ok(p),
164        None => Err(Error::msg("NAT64 AIL result lookup is empty")),
165    }
166}
167
168impl PlatformBacking {
169    fn on_nat64_prefix_request(&self, infra_if_idx: ot::NetifIndex) {
170        #[unsafe(no_mangle)]
171        unsafe extern "C" fn otPlatInfraIfDiscoverNat64Prefix(infra_if_idx: u32) -> otError {
172            PlatformBacking::on_nat64_prefix_request(
173                // SAFETY: Must only be called from OpenThread thread
174                unsafe { PlatformBacking::as_ref() },
175                infra_if_idx,
176            );
177            ot::Error::None.into()
178        }
179
180        let fut = async move {
181            // async dns lookup
182            let name_lookup_proxy_res = fuchsia_component::client::connect_to_protocol::<
183                fidl_fuchsia_net_name::LookupMarker,
184            >();
185
186            if let Err(e) = name_lookup_proxy_res {
187                warn!("failed to connect to fidl_fuchsia_net_name::LookupMarker: {:?}", e);
188                return (infra_if_idx, Ip6Prefix::new(Ip6Address::new(0, 0, 0, 0, 0, 0, 0, 0), 0));
189            }
190
191            let lookup_result;
192            match name_lookup_proxy_res
193                .unwrap()
194                .lookup_ip(
195                    "ipv4only.arpa",
196                    &fidl_fuchsia_net_name::LookupIpOptions {
197                        ipv6_lookup: Some(true),
198                        ..Default::default()
199                    },
200                )
201                .await
202            {
203                Ok(res) => {
204                    lookup_result = res;
205                }
206                Err(e) => {
207                    warn!("failed to do dns lookup_ip: {:?}", e);
208                    return (
209                        infra_if_idx,
210                        Ip6Prefix::new(Ip6Address::new(0, 0, 0, 0, 0, 0, 0, 0), 0),
211                    );
212                }
213            };
214
215            let prefix;
216            match lookup_result {
217                Ok(fidl_fuchsia_net_name::LookupResult { addresses: Some(ip_vec), .. }) => {
218                    info!("processed dns response, result: {:?}", &ip_vec);
219                    match process_ail_dns_lookup_result(ip_vec) {
220                        Ok(prefix_output) => {
221                            prefix = prefix_output;
222                        }
223                        Err(_) => {
224                            warn!("malformed DNS response, dropping the prefix");
225                            prefix = Ip6Prefix::new(Ip6Address::new(0, 0, 0, 0, 0, 0, 0, 0), 0);
226                        }
227                    }
228                }
229                Ok(fidl_fuchsia_net_name::LookupResult { addresses: None, .. }) => {
230                    info!("failed to process dns lookup result: empty result");
231                    prefix = Ip6Prefix::new(Ip6Address::new(0, 0, 0, 0, 0, 0, 0, 0), 0);
232                }
233                Err(e) => {
234                    warn!("failed to process dns lookup result: {:?}", e);
235                    prefix = Ip6Prefix::new(Ip6Address::new(0, 0, 0, 0, 0, 0, 0, 0), 0);
236                }
237            }
238
239            (infra_if_idx, prefix)
240        };
241
242        self.nat64
243            .nat64_prefix_req_sender
244            .lock()
245            .unbounded_send(Mutex::new(Some(fut.boxed())))
246            .expect("on_net64_prefix_request");
247    }
248}
249
250impl Platform {
251    pub fn process_poll_nat64(&mut self, instance: &ot::Instance, cx: &mut Context<'_>) {
252        while let Poll::Ready(Some(mtx)) =
253            self.nat64_platform_instance.nat64_prefix_req_receiver.poll_next_unpin(cx)
254        {
255            self.nat64_platform_instance.nat64_pending_fut = mtx;
256        }
257
258        let mut borrowed = self.nat64_platform_instance.nat64_pending_fut.lock();
259        if let Some(future) = borrowed.as_mut() {
260            match future.poll_unpin(cx) {
261                Poll::Ready((infra_if_idx, ip6_prefix)) => {
262                    instance.plat_infra_if_discover_nat64_prefix_done(infra_if_idx, ip6_prefix);
263                    *borrowed = None;
264                }
265                Poll::Pending => {}
266            }
267        }
268    }
269}
270
271#[cfg(test)]
272mod test {
273    use super::*;
274
275    #[test]
276    fn ail_dns_lookup_process_test() {
277        // empty input
278        assert!(process_ail_dns_lookup_result(vec![]).is_err());
279        // no ipv6 response
280        assert!(
281            process_ail_dns_lookup_result(vec![fidl_fuchsia_net::IpAddress::Ipv4(
282                fidl_fuchsia_net::Ipv4Address { addr: [192, 0, 0, 170] }
283            )])
284            .is_err()
285        );
286        // unknown address
287        assert!(
288            process_ail_dns_lookup_result(vec![fidl_fuchsia_net::IpAddress::Ipv6(
289                fidl_fuchsia_net::Ipv6Address {
290                    addr: [
291                        0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
292                        0x00, 0x00, 0x00, 0x00
293                    ]
294                }
295            )])
296            .is_err()
297        );
298        // valid ipv6 address in response (32 bit prefix, found addr 1)
299        assert_eq!(
300            process_ail_dns_lookup_result(vec![
301                fidl_fuchsia_net::IpAddress::Ipv6(fidl_fuchsia_net::Ipv6Address {
302                    addr: [
303                        0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
304                        0x00, 0x00, 0x00, 0x00
305                    ]
306                }),
307                fidl_fuchsia_net::IpAddress::Ipv6(fidl_fuchsia_net::Ipv6Address {
308                    addr: [
309                        0xfc, 0x00, 0x00, 0x01, 0xc0, 0x00, 0x00, 0xaa, 0x00, 0x01, 0x00, 0x02,
310                        0x00, 0x03, 0x00, 0x04
311                    ]
312                })
313            ])
314            .unwrap(),
315            Ip6Prefix::new(Ip6Address::new(0xfc00, 0x0001, 0, 0, 0, 0, 0, 0), 32)
316        );
317        // valid ipv6 address in response (32 bit prefix, found addr 2)
318        assert_eq!(
319            process_ail_dns_lookup_result(vec![
320                fidl_fuchsia_net::IpAddress::Ipv6(fidl_fuchsia_net::Ipv6Address {
321                    addr: [
322                        0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
323                        0x00, 0x00, 0x00, 0x00
324                    ]
325                }),
326                fidl_fuchsia_net::IpAddress::Ipv6(fidl_fuchsia_net::Ipv6Address {
327                    addr: [
328                        0xfc, 0x00, 0x00, 0x01, 0xc0, 0x00, 0x00, 0xab, 0x00, 0x01, 0x00, 0x02,
329                        0x00, 0x03, 0x00, 0x04
330                    ]
331                })
332            ])
333            .unwrap(),
334            Ip6Prefix::new(Ip6Address::new(0xfc00, 0x0001, 0, 0, 0, 0, 0, 0), 32)
335        );
336        // valid ipv6 address in response (40 bit prefix, found addr 1)
337        assert_eq!(
338            process_ail_dns_lookup_result(vec![
339                fidl_fuchsia_net::IpAddress::Ipv6(fidl_fuchsia_net::Ipv6Address {
340                    addr: [
341                        0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
342                        0x00, 0x00, 0x00, 0x00
343                    ]
344                }),
345                fidl_fuchsia_net::IpAddress::Ipv6(fidl_fuchsia_net::Ipv6Address {
346                    addr: [
347                        0xfc, 0x00, 0x00, 0x00, 0x01, 0xc0, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x02,
348                        0x00, 0x03, 0x00, 0x04
349                    ]
350                })
351            ])
352            .unwrap(),
353            Ip6Prefix::new(Ip6Address::new(0xfc00, 0x0000, 0x0100, 0, 0, 0, 0, 0), 40)
354        );
355        // valid ipv6 address in response (48 bit prefix, found addr 1)
356        assert_eq!(
357            process_ail_dns_lookup_result(vec![
358                fidl_fuchsia_net::IpAddress::Ipv6(fidl_fuchsia_net::Ipv6Address {
359                    addr: [
360                        0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
361                        0x00, 0x00, 0x00, 0x00
362                    ]
363                }),
364                fidl_fuchsia_net::IpAddress::Ipv6(fidl_fuchsia_net::Ipv6Address {
365                    addr: [
366                        0xfc, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0, 0x00, 0x00, 0x00, 0xaa, 0x02,
367                        0x00, 0x03, 0x00, 0x04
368                    ]
369                })
370            ])
371            .unwrap(),
372            Ip6Prefix::new(Ip6Address::new(0xfc00, 0x0000, 0x0001, 0, 0, 0, 0, 0), 48)
373        );
374        // valid ipv6 address in response (56 bit prefix, found addr 1)
375        assert_eq!(
376            process_ail_dns_lookup_result(vec![
377                fidl_fuchsia_net::IpAddress::Ipv6(fidl_fuchsia_net::Ipv6Address {
378                    addr: [
379                        0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
380                        0x00, 0x00, 0x00, 0x00
381                    ]
382                }),
383                fidl_fuchsia_net::IpAddress::Ipv6(fidl_fuchsia_net::Ipv6Address {
384                    addr: [
385                        0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0, 0x00, 0x00, 0x00, 0xaa,
386                        0x00, 0x03, 0x00, 0x04
387                    ]
388                })
389            ])
390            .unwrap(),
391            Ip6Prefix::new(Ip6Address::new(0xfc00, 0x0000, 0x0000, 0x0100, 0, 0, 0, 0), 56)
392        );
393        // valid ipv6 address in response (64 bit prefix, found addr 1)
394        assert_eq!(
395            process_ail_dns_lookup_result(vec![
396                fidl_fuchsia_net::IpAddress::Ipv6(fidl_fuchsia_net::Ipv6Address {
397                    addr: [
398                        0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
399                        0x00, 0x00, 0x00, 0x00
400                    ]
401                }),
402                fidl_fuchsia_net::IpAddress::Ipv6(fidl_fuchsia_net::Ipv6Address {
403                    addr: [
404                        0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xc0, 0x00, 0x00,
405                        0xaa, 0x03, 0x00, 0x04
406                    ]
407                })
408            ])
409            .unwrap(),
410            Ip6Prefix::new(Ip6Address::new(0xfc00, 0x0000, 0x0000, 0x0001, 0, 0, 0, 0), 64)
411        );
412        // valid ipv6 address in response (96 bit prefix, found addr 1)
413        assert_eq!(
414            process_ail_dns_lookup_result(vec![
415                fidl_fuchsia_net::IpAddress::Ipv6(fidl_fuchsia_net::Ipv6Address {
416                    addr: [
417                        0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
418                        0x00, 0x00, 0x00, 0x00
419                    ]
420                }),
421                fidl_fuchsia_net::IpAddress::Ipv6(fidl_fuchsia_net::Ipv6Address {
422                    addr: [
423                        0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
424                        0xc0, 0x00, 0x00, 0xaa
425                    ]
426                })
427            ])
428            .unwrap(),
429            Ip6Prefix::new(
430                Ip6Address::new(0xfc00, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0, 0),
431                96
432            )
433        );
434        // no valid address in response: dup (32 bit and 72 bit)
435        assert!(
436            process_ail_dns_lookup_result(vec![
437                fidl_fuchsia_net::IpAddress::Ipv6(fidl_fuchsia_net::Ipv6Address {
438                    addr: [
439                        0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
440                        0x00, 0x00, 0x00, 0x00
441                    ]
442                }),
443                fidl_fuchsia_net::IpAddress::Ipv6(fidl_fuchsia_net::Ipv6Address {
444                    addr: [
445                        0xfc, 0x00, 0x00, 0x01, 0xc0, 0x00, 0x00, 0xaa, 0x00, 0xc0, 0x00, 0x00,
446                        0xaa, 0x03, 0x00, 0x04
447                    ]
448                })
449            ])
450            .is_err()
451        );
452        // no valid address in response: dup (32 bit and 96 bit)
453        assert!(
454            process_ail_dns_lookup_result(vec![
455                fidl_fuchsia_net::IpAddress::Ipv6(fidl_fuchsia_net::Ipv6Address {
456                    addr: [
457                        0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
458                        0x00, 0x00, 0x00, 0x00
459                    ]
460                }),
461                fidl_fuchsia_net::IpAddress::Ipv6(fidl_fuchsia_net::Ipv6Address {
462                    addr: [
463                        0xfc, 0x00, 0x00, 0x01, 0xc0, 0x00, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x00,
464                        0xc0, 0x00, 0x00, 0xaa
465                    ]
466                })
467            ])
468            .is_err()
469        );
470        // no valid address in response: dup (40 bit and 96 bit)
471        assert!(
472            process_ail_dns_lookup_result(vec![
473                fidl_fuchsia_net::IpAddress::Ipv6(fidl_fuchsia_net::Ipv6Address {
474                    addr: [
475                        0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
476                        0x00, 0x00, 0x00, 0x00
477                    ]
478                }),
479                fidl_fuchsia_net::IpAddress::Ipv6(fidl_fuchsia_net::Ipv6Address {
480                    addr: [
481                        0xfc, 0x00, 0x00, 0x00, 0x01, 0xc0, 0x00, 0x00, 0x00, 0xaa, 0x00, 0x00,
482                        0xc0, 0x00, 0x00, 0xaa
483                    ]
484                })
485            ])
486            .is_err()
487        );
488        // no valid address in response: dup (56 bit and 96 bit)
489        assert!(
490            process_ail_dns_lookup_result(vec![
491                fidl_fuchsia_net::IpAddress::Ipv6(fidl_fuchsia_net::Ipv6Address {
492                    addr: [
493                        0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
494                        0x00, 0x00, 0x00, 0x00
495                    ]
496                }),
497                fidl_fuchsia_net::IpAddress::Ipv6(fidl_fuchsia_net::Ipv6Address {
498                    addr: [
499                        0xfc, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0, 0x00, 0x00, 0x00, 0xaa, 0x00,
500                        0xc0, 0x00, 0x00, 0xaa
501                    ]
502                })
503            ])
504            .is_err()
505        );
506    }
507
508    #[test]
509    fn test_ipv4_nat64_ops() {
510        assert_eq!(
511            extract_ipv4_addr_from_ipv6(
512                Ip6Address::new(0xffff, 0xffff, 0xf1f2, 0xf3f4, 0xffff, 0xffff, 0xffff, 0xffff),
513                32
514            ),
515            Ip4Address::new(0xf1, 0xf2, 0xf3, 0xf4)
516        );
517        assert_eq!(
518            extract_ipv4_addr_from_ipv6(
519                Ip6Address::new(0xffff, 0xffff, 0xfff1, 0xf2f3, 0xfff4, 0xffff, 0xffff, 0xffff),
520                40
521            ),
522            Ip4Address::new(0xf1, 0xf2, 0xf3, 0xf4)
523        );
524        assert_eq!(
525            extract_ipv4_addr_from_ipv6(
526                Ip6Address::new(0xffff, 0xffff, 0xffff, 0xf1f2, 0xfff3, 0xf4ff, 0xffff, 0xffff),
527                48
528            ),
529            Ip4Address::new(0xf1, 0xf2, 0xf3, 0xf4)
530        );
531        assert_eq!(
532            extract_ipv4_addr_from_ipv6(
533                Ip6Address::new(0xffff, 0xffff, 0xffff, 0xfff1, 0xfff2, 0xf3f4, 0xffff, 0xffff),
534                56
535            ),
536            Ip4Address::new(0xf1, 0xf2, 0xf3, 0xf4)
537        );
538        assert_eq!(
539            extract_ipv4_addr_from_ipv6(
540                Ip6Address::new(0xffff, 0xffff, 0xffff, 0xffff, 0xfff1, 0xf2f3, 0xf4ff, 0xffff),
541                64
542            ),
543            Ip4Address::new(0xf1, 0xf2, 0xf3, 0xf4)
544        );
545        assert_eq!(
546            extract_ipv4_addr_from_ipv6(
547                Ip6Address::new(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xf1f2, 0xf3f4),
548                96
549            ),
550            Ip4Address::new(0xf1, 0xf2, 0xf3, 0xf4)
551        );
552
553        // Invalid input
554        assert_eq!(
555            extract_ipv4_addr_from_ipv6(
556                Ip6Address::new(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xf1f2, 0xf3f4),
557                44
558            ),
559            Ip4Address::new(0, 0, 0, 0)
560        );
561    }
562
563    #[test]
564    fn test_ipv6_nat64_ops() {
565        assert_eq!(
566            get_ipv6_prefix(
567                Ip6Address::new(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),
568                0
569            ),
570            Some(Ip6Address::new(0, 0, 0, 0, 0, 0, 0, 0))
571        );
572        assert_eq!(
573            get_ipv6_prefix(
574                Ip6Address::new(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),
575                16
576            ),
577            Some(Ip6Address::new(0xffff, 0, 0, 0, 0, 0, 0, 0))
578        );
579        assert_eq!(
580            get_ipv6_prefix(
581                Ip6Address::new(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),
582                32
583            ),
584            Some(Ip6Address::new(0xffff, 0xffff, 0, 0, 0, 0, 0, 0))
585        );
586        assert_eq!(
587            get_ipv6_prefix(
588                Ip6Address::new(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),
589                63
590            ),
591            Some(Ip6Address::new(0xffff, 0xffff, 0xffff, 0xfffe, 0, 0, 0, 0))
592        );
593        assert_eq!(
594            get_ipv6_prefix(
595                Ip6Address::new(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),
596                64
597            ),
598            Some(Ip6Address::new(0xffff, 0xffff, 0xffff, 0xffff, 0, 0, 0, 0))
599        );
600        assert_eq!(
601            get_ipv6_prefix(
602                Ip6Address::new(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),
603                65
604            ),
605            Some(Ip6Address::new(0xffff, 0xffff, 0xffff, 0xffff, 0x8000, 0, 0, 0))
606        );
607        assert_eq!(
608            get_ipv6_prefix(
609                Ip6Address::new(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),
610                128
611            ),
612            Some(Ip6Address::new(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff))
613        );
614        assert_eq!(
615            get_ipv6_prefix(
616                Ip6Address::new(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff),
617                129
618            ),
619            None
620        );
621    }
622}