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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
extern crate crypto;
extern crate ipnetwork;
use std::net::Ipv6Addr;
use std::str::FromStr;
use crypto::blake2b::Blake2b;
use crypto::digest::Digest;
use ipnetwork::Ipv6Network;
pub fn ip(name: &str, cidr: &str) -> Result<Ipv6Addr, String> {
let net = match Ipv6Network::from_str(cidr).map_err(|err| format!("{:?}", err)) {
Ok(net) => {
if net.prefix() == 128 {
return Err(format!("{}/{} is already a full IPv6 address",
net.ip(),
net.prefix()));
} else {
net
}
}
Err(msg) => return Err(msg),
};
let network_len = net.prefix() as usize / 4;
let ip = net.ip().segments();
let ip_parts: Vec<String> = ip.iter()
.map(|b| format!("{:04x}", b))
.collect();
let ip_hash = ip_parts.join("");
let ip_hash = ip_hash.as_str();
let network_hash = &ip_hash[0..network_len];
let address_len = 32 - network_len;
let hash_is_bigger = address_len % 2 != 0;
let mut blake_len = address_len / 2;
if hash_is_bigger {
blake_len += 1;
};
let hash = hash(name, blake_len);
let address_hash = if hash_is_bigger {
&hash[..hash.len()]
} else {
hash.as_str()
};
let ip_hash = format!("{}{}", network_hash, address_hash);
let ip = format!("{}:{}:{}:{}:{}:{}:{}:{}",
&ip_hash[0..4],
&ip_hash[4..8],
&ip_hash[8..12],
&ip_hash[12..16],
&ip_hash[16..20],
&ip_hash[20..24],
&ip_hash[24..28],
&ip_hash[28..32]);
Ipv6Addr::from_str(ip.as_str())
.map_err(|err| format!("generated IPv6 address ({}) has {}", ip, err))
}
pub fn subnet(name: &str) -> String {
hash(name, 2)
}
fn hash(name: &str, len: usize) -> String {
let mut hash = Blake2b::new(len);
hash.input_str(name);
hash.result_str()
}
#[cfg(test)]
mod test {
#[test]
fn ip_is_valid() {
match super::ip("c0a010fb-2632-40cb-a105-90297cba567a",
"fd52:f6b0:3162::/48") {
Ok(_) => {
}
Err(err) => panic!(err),
};
}
}