1use dyn_clone::DynClone;
2use std::convert::TryInto;
3use std::net::Ipv4Addr;
4
5use log::debug;
6
7pub trait IPV4AddrSource: DynClone + Send {
9 fn get_best_ipv4(&self) -> Option<Ipv4Addr>;
16
17 fn add_vote(&mut self, their_addr: Ipv4Addr, proposed_addr: Ipv4Addr);
27
28 fn decay(&mut self);
31}
32
33dyn_clone::clone_trait_object!(IPV4AddrSource);
34
35#[derive(Clone)]
37pub struct StaticIPV4AddrSource {
38 ip: Ipv4Addr,
39}
40
41impl IPV4AddrSource for StaticIPV4AddrSource {
42 fn get_best_ipv4(&self) -> Option<Ipv4Addr> {
43 Some(self.ip)
44 }
45 fn add_vote(&mut self, _: Ipv4Addr, _: Ipv4Addr) {}
46 fn decay(&mut self) {}
47}
48
49impl StaticIPV4AddrSource {
50 pub fn new(addr: Ipv4Addr) -> StaticIPV4AddrSource {
51 StaticIPV4AddrSource { ip: addr }
52 }
53}
54
55#[derive(Clone)]
56struct IPV4Vote {
57 ip: Ipv4Addr,
58 votes: i32,
59}
60
61#[derive(Clone)]
63pub struct IPV4Consensus {
64 min_votes: usize,
65 max_votes: usize,
66 votes: Vec<IPV4Vote>,
67}
68
69impl IPV4Consensus {
70 pub fn new(min_votes: usize, max_votes: usize) -> IPV4Consensus {
71 IPV4Consensus {
72 min_votes,
73 max_votes,
74 votes: Vec::new(),
75 }
76 }
77}
78
79impl IPV4AddrSource for IPV4Consensus {
80 fn get_best_ipv4(&self) -> Option<Ipv4Addr> {
81 let first = self.votes.first();
82 match first {
83 Some(vote_info) => {
84 debug!(target: "rustydht_lib::IPV4AddrSource", "Best IPv4 address {:?} has {} votes", vote_info.ip, vote_info.votes);
85 if vote_info.votes >= self.min_votes.try_into().unwrap() {
86 Some(vote_info.ip)
87 } else {
88 None
89 }
90 }
91
92 None => None,
93 }
94 }
95
96 fn add_vote(&mut self, _: Ipv4Addr, proposed_addr: Ipv4Addr) {
97 let mut do_sort = false;
98 for vote in self.votes.iter_mut() {
99 if vote.ip == proposed_addr {
100 vote.votes = std::cmp::min(self.max_votes.try_into().unwrap(), vote.votes + 1);
101 do_sort = true;
102 break;
103 }
104 }
105
106 if do_sort {
107 self.votes.sort_by(|a, b| b.votes.cmp(&a.votes));
108 } else {
109 self.votes.push(IPV4Vote {
110 ip: proposed_addr,
111 votes: 1,
112 });
113 }
114 }
115
116 fn decay(&mut self) {
117 for vote in self.votes.iter_mut() {
118 vote.votes = std::cmp::max(0, vote.votes - 1);
119 }
120
121 self.votes.retain(|a| a.votes > 0)
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 #[test]
131 fn test_static_src() {
132 let ip = Ipv4Addr::new(10, 0, 0, 107);
133 let mut src = StaticIPV4AddrSource::new(ip);
134 assert_eq!(Some(ip), src.get_best_ipv4());
135
136 src.add_vote(Ipv4Addr::new(0, 0, 0, 0), Ipv4Addr::new(1, 1, 1, 1));
138 src.decay();
139
140 assert_eq!(Some(ip), src.get_best_ipv4());
142 }
143
144 #[test]
145 fn test_consensus_src() {
146 let mut src = IPV4Consensus::new(2, 4);
147 assert_eq!(None, src.get_best_ipv4());
149
150 src.add_vote(Ipv4Addr::new(0, 0, 0, 0), Ipv4Addr::new(1, 1, 1, 1));
152 assert_eq!(None, src.get_best_ipv4());
153
154 src.add_vote(Ipv4Addr::new(0, 0, 0, 0), Ipv4Addr::new(2, 2, 2, 2));
156 assert_eq!(None, src.get_best_ipv4());
157
158 src.add_vote(Ipv4Addr::new(0, 0, 0, 0), Ipv4Addr::new(1, 1, 1, 1));
160 assert_eq!(Some(Ipv4Addr::new(1, 1, 1, 1)), src.get_best_ipv4());
161
162 src.add_vote(Ipv4Addr::new(0, 0, 0, 0), Ipv4Addr::new(2, 2, 2, 2));
164 assert_eq!(Some(Ipv4Addr::new(1, 1, 1, 1)), src.get_best_ipv4());
165
166 src.add_vote(Ipv4Addr::new(0, 0, 0, 0), Ipv4Addr::new(2, 2, 2, 2));
168 assert_eq!(Some(Ipv4Addr::new(2, 2, 2, 2)), src.get_best_ipv4());
169
170 src.decay();
172
173 assert_eq!(Some(Ipv4Addr::new(2, 2, 2, 2)), src.get_best_ipv4());
175
176 src.decay();
178
179 assert_eq!(None, src.get_best_ipv4());
181 }
182}