1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#![allow(dead_code)]
#![allow(unused_imports)]

use std::{
    collections::BTreeMap,
    sync::{Arc, Mutex},
    time::Duration,
};

use twizzler_async::{timeout_after, FlagBlock};
use twizzler_net::addr::Ipv4Addr;

use crate::link::ethernet::EthernetAddr;

const ARP_TIMEOUT: Duration = Duration::from_millis(8000);

#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)]
pub struct ArpInfo {
    eth_addr: EthernetAddr,
}

#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug)]
pub struct ArpKey {
    addr: Ipv4Addr,
}

struct ArpTableInner {
    entries: Mutex<BTreeMap<ArpKey, ArpInfo>>,
    flag: FlagBlock,
}

struct ArpTable {
    inner: Arc<ArpTableInner>,
}

lazy_static::lazy_static! {
    static ref ARP_TABLE: ArpTable = ArpTable::new();
}

impl ArpTable {
    fn new() -> Self {
        Self {
            inner: Arc::new(ArpTableInner {
                entries: Mutex::new(BTreeMap::new()),
                flag: FlagBlock::new(),
            }),
        }
    }

    async fn lookup(&self, dst: ArpKey) -> ArpInfo {
        loop {
            let entries = self.inner.entries.lock().unwrap();
            if let Some(res) = entries.get(&dst) {
                return *res;
            }

            println!("didn't find arp entry");
            // a gotcha: you have to get the future here before releasing the entries mutex, and
            // then you must await on it _after_ the lock has been released.
            let fut = self.inner.flag.wait();
            println!("future created");
            drop(entries);
            println!("firing request");
            self.fire_arp_request(dst);
            println!("awaiting completion");
            fut.await;
        }
    }

    fn fire_arp_request(&self, _dst: ArpKey) {
        println!("fire arp request");
    }

    fn add_entry(&self, key: ArpKey, info: ArpInfo) {
        self.inner.entries.lock().unwrap().insert(key, info);
        self.inner.flag.signal_all();
    }
}

pub async fn lookup_arp_info(key: ArpKey) -> Result<Option<ArpInfo>, ()> {
    Ok(twizzler_async::timeout_after(ARP_TABLE.lookup(key), ARP_TIMEOUT).await)
}