-
Notifications
You must be signed in to change notification settings - Fork 62
IPv6 Multicast over UDP to share addresses #404
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
66a9566
fffce49
efda1da
cba49e3
acb8793
d497b70
7dde7ef
26d6fa1
4f60a80
f55ed57
678ca39
0aa0a03
8817ce0
e7f6ba9
0156df2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| // This Source Code Form is subject to the terms of the Mozilla Public | ||
| // License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| // file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
|
|
||
| //! Sled announcement and discovery. | ||
|
|
||
| use super::multicast; | ||
| use slog::Logger; | ||
| use std::collections::HashSet; | ||
| use std::io; | ||
| use std::net::{Ipv6Addr, SocketAddr, SocketAddrV6}; | ||
| use std::sync::Arc; | ||
| use tokio::net::UdpSocket; | ||
| use tokio::sync::Mutex; | ||
| use tokio::task::JoinHandle; | ||
|
|
||
| /// Manages Sled Discovery - both our announcement to other Sleds, | ||
| /// as well as our discovery of those sleds. | ||
| pub struct PeerMonitor { | ||
| sleds: Arc<Mutex<HashSet<SocketAddr>>>, | ||
| _worker: JoinHandle<()>, | ||
| } | ||
|
|
||
| async fn monitor_worker( | ||
| log: Logger, | ||
| address: SocketAddrV6, | ||
| sender: UdpSocket, | ||
| listener: UdpSocket, | ||
| sleds: Arc<Mutex<HashSet<SocketAddr>>>, | ||
| ) { | ||
| // Let this message be a reminder that this content is *not* | ||
| // encrypted, authenticated, or otherwise verified. We're just using | ||
| // it as a starting point for swapping addresses. | ||
| let message = | ||
| b"We've been trying to reach you about your car's extended warranty"; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. lol |
||
| loop { | ||
| let mut buf = vec![0u8; 128]; | ||
| tokio::select! { | ||
| _ = tokio::time::sleep(tokio::time::Duration::from_millis(5000)) => { | ||
| info!(log, "Bootstrap Peer Monitor: Broadcasting our own address: {}", address); | ||
| if let Err(e) = sender.try_send_to(message, address.into()) { | ||
| warn!(log, "PeerMonitor failed to broadcast: {}", e); | ||
| } | ||
| } | ||
| result = listener.recv_from(&mut buf) => { | ||
| match result { | ||
| Ok((_, addr)) => { | ||
| info!(log, "Bootstrap Peer Monitor: Successfully received an address: {}", addr); | ||
| sleds.lock().await.insert(addr); | ||
|
Comment on lines
+47
to
+49
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would ideally be the address we use for subsequent communication with the sled. Admittedly the sled agent / bootstrap servers are using an SMF file configured to use IPv4 addresses, but this is an IPv6 address. That presents a bit of a challenge - presumably we'd want everyone to be communicating over IPv6 in the long-term, no?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently there is no plan to have any IPv4 on the underlay. I think it's still an open question how stable addresses will find their way onto servers. The approach I've been taking in my testing setups is the following.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I will make the change to use IPv6 for the bootstrap server explicitly within this change. I hear you on the "no IPv4 in the underlay" - we should trend in that direction - but that transition can be more gradual.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Filing #442 to track |
||
| }, | ||
| Err(e) => warn!(log, "PeerMonitor failed to receive: {}", e), | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl PeerMonitor { | ||
| /// Creates a new [`PeerMonitor`]. | ||
| // TODO: Address, port, interface, etc, probably should be | ||
| // configuration options. | ||
| pub fn new(log: &Logger) -> Result<Self, io::Error> { | ||
| let scope = multicast::Ipv6MulticastScope::LinkLocal.first_hextet(); | ||
| let address = SocketAddrV6::new( | ||
| Ipv6Addr::new(scope, 0, 0, 0, 0, 0, 0, 0x1), | ||
| 7645, | ||
| 0, | ||
| 0, | ||
| ); | ||
|
Comment on lines
+64
to
+69
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Other than the scope - which is intentional - the rest of this address is 100% arbitrary.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should use either Admin-Local or Site-Local scope here. It's not guaranteed that there will be an L2 domain across the rack. RFD 63 lays out two possible paths for bootstrapping the rack, one that has an L2 broadcast domain for starting up, and another that starts in L3. I'm personally leaning toward the latter so we do not have to change the shape of the network as a part of starting up. Admin-Local or Site-Local scopes should work for either alternative, but Link-Local will only work for the former. I'd suggest coming up with a set of constant/well-known multicast group addresses that correspond to particular communication domains. For example for Rift peering in Maghemite we use ff02::a1f7
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This comment definitely led me to some required reading. Thank you for the feedback @rcgoodfellow. I still have a few questions on this though.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks Ry
Yes, exactly. There is a site specific unicast address and a multicast scope, but only a multicast cope for admin, but no related unicast address.
|
||
| let loopback = false; | ||
| let interface = 0; | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd be happy to pick a more specific interface, if there was a good way to do so. Feedback welcome.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a tricky question. I have not given much thought to multicast routing yet. At a basic level there are two potentially viable interfaces for this software to chose from, and I think the right answer might be both, so not specifying the interface for now seems reasonable. This means that traffic will egress on both interfaces and also ingress on both interfaces for the receiving servers. |
||
| let (sender, listener) = | ||
| multicast::new_ipv6_udp_pair(&address, loopback, interface)?; | ||
|
|
||
| let sleds = Arc::new(Mutex::new(HashSet::new())); | ||
| let sleds_for_worker = sleds.clone(); | ||
| let log = log.clone(); | ||
|
|
||
| let worker = tokio::task::spawn(async move { | ||
| monitor_worker(log, address, sender, listener, sleds_for_worker) | ||
| .await | ||
| }); | ||
|
|
||
| Ok(PeerMonitor { sleds, _worker: worker }) | ||
| } | ||
|
|
||
| /// Returns the addresses of connected sleds. | ||
| /// | ||
| /// Note: These sleds have not yet been verified. | ||
| pub async fn addrs(&self) -> Vec<SocketAddr> { | ||
| self.sleds.lock().await.iter().map(|addr| *addr).collect() | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe there needs to be a determination about phases here. If a sled does not already know the group of existing sleds (saved in a file) where each sled id is a public key or key fingerprint, that sled is not capable of unlocking itself, or doing much of anything except listening on its multicast address for a request from the primary. ( Note that we use the public key as sled id, since we want to allow movement of sleds throughout the rack and for their IP addresses to change. )
The above scenario is going to be the case of all sleds except the primary during initial rack setup. The primary will start a
PeerMonitorand wait for (in the demo case) a predefined number of sleds to respond. The primary will then connect over TCP to a predetermined SPDM port at each of the sled-agents that responded, using the responded IP address. The primary will then become an SPDM requester and attempt to create a secure channel (and pretend to attest measurements), over that TCP connection. When all the sleds are connected via secure SPDM channels, a secret will be generated and distributed to each sled-agent along with its individual key-share and group information. This is Phase 1 of the protocol and only happens during rack initialization. We aren't yet considering what it means to add a sled to the group at runtime.Phase 2 is what I believe
establish_sled_quorumwas meant to encapsulate. This is where each sled already has a key share and knows the group members. In this case, when a sled restarts it will run aPeerMonitorto get the IPs of a threshold of sleds and then create a secure SPDM channel to each of those sled-agents. Both sides of the SPDM channel should ensure that any received certs or digests actually match the group information, although its unclear if we need to do this for the demo. This is because while SPDM supports mutual authentication, it's not yet implemented, and so we are going to pretend to setup a secure channel by running the protocol up to the implemented challenge authentication phase. Once our pseudo-SPDM secured channel is established the remote sled can send the requested key share. When a quorum is retrieved the rack secret can be reconstructed and the sled unlocked.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, this code - as implemented - does not have a phase explicitly for sharing keys.
I don't have a strong opinion about whether or not this is implemented in
establish_sled_quorumor not - but as long as it happens after thePeerMonitoris up and running, I'm happy.