Skip to content

Commit

Permalink
chore(node): remove setupos/hostos network bonding and clean up docs (#…
Browse files Browse the repository at this point in the history
…2579)

NODE-1527

Remove bonding. It's added complexity that we don't need.

I successfully testing on bare metal and networking still works 👍 

zh2-dll01 on master:
![Pasted Graphic
19](https://github.com/user-attachments/assets/af3b9972-ba91-48e8-8bb5-db89c23666cf)

On branch:
<img width="1209" alt="image"
src="https://github.com/user-attachments/assets/fae924ee-8c32-4b1f-8c6a-a5bf566bbc35">

----

GuestOS `$ ip a` output is unchanged
  • Loading branch information
andrewbattat authored Nov 20, 2024
1 parent dab4842 commit f96dec1
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 112 deletions.
65 changes: 2 additions & 63 deletions ic-os/docs/Network-Configuration.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -13,70 +13,9 @@ Network configuration details for each IC-OS:

== Deterministic MAC Address

Each IC-OS node must have a unique but deterministic MAC address. To solve this, a schema has been devised.
Each IC-OS node must have a unique but deterministic MAC address derived from its BMC MAC address, deployment type (mainnet vs testnet), and variant type (SetupOS, HostOS, GuestOS, BoundaryGuestOS). This MAC address is then utilized to generate the node's network configuration.

=== Schema

* *The first 8-bits:*
** IPv4 interfaces: 4a
** IPv6 interfaces: 6a

* *The second 8-bits:*
** We reserve the following hexadecimal numbers for each IC-OS:
*** SetupOS: 0f
*** HostOS: 00
*** GuestOS: 01
*** Boundary-GuestOS: 02

** Note: any additional virtual machine on the same physical machine gets the next higher hexadecimal number.

* *The remaining 32-bits:*
** Deterministically generated

=== Example MAC addresses

* SetupOS: `{4a,6a}:0f:<deterministically-generated-part>`
* HostOS: `{4a,6a}:00:<deterministically-generated-part>`
* GuestOS: `{4a,6a}:01:<deterministically-generated-part>`
* BoundaryOS: `{4a,6a}:02:<deterministically-generated-part>`
* Next Virtual Machine: `{4a,6a}:03:<deterministically-generated-part>`

Note that the MAC address is expected to be lower-case and to contain colons between the octets.

=== Deterministically Generated Part

The deterministically generated part is generated using the following inputs:

1. IPMI MAC address (the MAC address of the BMC)
a. Obtained via `$ ipmitool lan print | grep 'MAC Address'``
2. Deployment name
a. Ex: `mainnet`

The concatenation of the IPMI MAC address and deployment name is hashed:

$ sha256sum "<IPMI MAC ADDRESS><DEPLOYMENT NAME>"
# Example:
$ sha256sum "3c:ec:ef:6b:37:99mainnet"

The first 32-bits of the sha256 checksum are then used as the deterministically generated part of the MAC address.

# Checksum
f409d72aa8c98ea40a82ea5a0a437798a67d36e587b2cc49f9dabf2de1cedeeb

# Deterministically Generated Part
f409d72a

==== Deployment name

The deployment name is added to the MAC address generation to further increase its uniqueness. The deployment name *mainnet* is reserved for production. Testnets must use other names to avoid any chance of a MAC address collision in the same data center.

The deployment name is retrieved from the +deployment.json+ configuration file, generated as part of the SetupOS:

{
"deployment": {
"name": "mainnet"
}
}
For details, see the link:../../rs/ic_os/network/mac_address/README.md[mac_address README].

== Hostname

Expand Down
70 changes: 70 additions & 0 deletions rs/ic_os/network/mac_address/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# MAC Address

Each IC-OS node must have a unique but deterministic MAC address derived from its BMC MAC address, deployment type (mainnet vs testnet), and variant type (SetupOS, HostOS, GuestOS, BoundaryGuestOS). This MAC address is then utilized to generate the node’s network configuration. To solve this, a schema has been devised.

## Schema

- **The first 8-bits:**
- IPv4 interfaces: 4a
- IPv6 interfaces: 6a

- **The second 8-bits:**
- Reserved hexadecimal numbers for each IC-OS:
- SetupOS: `0f`
- HostOS: `00`
- GuestOS: `01`
- Boundary-GuestOS: `02`

- **The remaining 32-bits:**
- Deterministically generated.

## Example MAC Addresses

- SetupOS: `6a:0f:<deterministically-generated-part>`
- HostOS: `6a:00:<deterministically-generated-part>`
- GuestOS: `6a:01:<deterministically-generated-part>`
- BoundaryOS: `6a:02:<deterministically-generated-part>`

## Deterministically Generated Part

The deterministically generated part is generated using the following inputs:

1. **IPMI MAC address** (the MAC address of the BMC):
- Obtained via:
```bash
ipmitool lan print | grep 'MAC Address'
```

2. **Deployment name**:
- Example: `mainnet`

The concatenation of the IPMI MAC address and deployment name is hashed:

```bash
sha256sum "<IPMI MAC ADDRESS><DEPLOYMENT NAME>"
# Example:
sha256sum "3c:ec:ef:6b:37:99mainnet"
```

The first 32 bits of the SHA-256 checksum are then used as the deterministically generated part of the MAC address:

```bash
Checksum:
f409d72aa8c98ea40a82ea5a0a437798a67d36e587b2cc49f9dabf2de1cedeeb
Deterministically Generated Part:
f409d72a
```

## Deployment Name
The deployment name is added to the MAC address generation to further increase its uniqueness. The deployment name mainnet is reserved for production. Testnets must use other names to avoid any chance of a MAC address collision in the same data center.

The deployment name is retrieved from the deployment.json configuration file, generated as part of the SetupOS:

json
Copy code
{
"deployment": {
"name": "mainnet"
}
}
62 changes: 13 additions & 49 deletions rs/ic_os/network/src/systemd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ DNS=2001:4860:4860::8888
DNS=2001:4860:4860::8844
"#;

fn generate_network_interface_content(interface_name: &str) -> String {
fn generate_network_interface_content(interface_name: &str, mac_line: &str) -> String {
format!(
"
[Match]
Expand All @@ -27,39 +27,16 @@ Name={interface_name}
[Link]
RequiredForOnline=no
MTUBytes=1500
{mac_line}
[Network]
LLDP=true
EmitLLDP=true
Bond=bond6
Bridge=br6
"
)
}

// `mac_line` - Must be in format: "MACAddress=ff:ff:ff:ff:ff:ff". Potentially unnecessary.
fn generate_bond6_netdev_content(mac_line: &str) -> String {
format!(
"
[NetDev]
Name=bond6
Kind=bond
{mac_line}
[Bond]
Mode=active-backup
MIIMonitorSec=5
UpDelaySec=10
DownDelaySec=10"
)
}

static BOND6_NETWORK_CONTENT: &str = "
[Match]
Name=bond6
[Network]
Bridge=br6";

static BRIDGE6_NETDEV_CONTENT: &str = "
[NetDev]
Name=br6
Expand Down Expand Up @@ -107,26 +84,16 @@ fn generate_and_write_systemd_files(
eprintln!("Creating directory: {}", output_directory.to_string_lossy());
create_dir_all(output_directory)?;

let interface_filename = format!("20-{}.network", interface.name);
let interface_path = output_directory.join(interface_filename);
let interface_content = generate_network_interface_content(&interface.name);
eprintln!("Writing {}", interface_path.to_string_lossy());
write(interface_path, interface_content)?;

let bond6_filename = "20-bond6.network";
let bond6_path = output_directory.join(bond6_filename);
eprintln!("Writing {}", bond6_path.to_string_lossy());
write(bond6_path, BOND6_NETWORK_CONTENT)?;

let bond6_netdev_filename = "20-bond6.netdev";
let bond6_netdev_path = output_directory.join(bond6_netdev_filename);
let mac_line = match generated_mac {
Some(mac) => format!("MACAddress={}", mac.get()),
None => String::new(),
};
let bond6_netdev_content = generate_bond6_netdev_content(&mac_line);
eprintln!("Writing {}", bond6_netdev_path.to_string_lossy());
write(bond6_netdev_path, bond6_netdev_content)?;

let interface_filename = format!("20-{}.network", interface.name);
let interface_path = output_directory.join(interface_filename);
let interface_content = generate_network_interface_content(&interface.name, &mac_line);
eprintln!("Writing {}", interface_path.to_string_lossy());
write(interface_path, interface_content)?;

let bridge6_netdev_filename = "20-br6.netdev";
let bridge6_netdev_path = output_directory.join(bridge6_netdev_filename);
Expand All @@ -135,7 +102,6 @@ fn generate_and_write_systemd_files(

let bridge6_filename = "20-br6.network";
let bridge6_path = output_directory.join(bridge6_filename);

let bridge6_content = generate_bridge6_network_content(
ipv6_address,
ipv6_gateway,
Expand All @@ -158,8 +124,8 @@ pub fn generate_systemd_config_files(
eprintln!("Interfaces sorted by speed: {:?}", interfaces);

let ping_target = network_info.ipv6_gateway.to_string();
// old nodes are still configured with a local IPv4 interface connection
// local IPv4 interfaces must be filtered out
// Old nodes are still configured with a local IPv4 interface connection
// Local IPv4 interfaces must be filtered out
let ipv6_interfaces: Vec<&Interface> = interfaces
.iter()
.filter(|i| {
Expand All @@ -173,9 +139,7 @@ pub fn generate_systemd_config_files(
})
.collect();

// For now only assign the fastest interface to ipv6.
// TODO - probe to make sure the interfaces are on the same network before doing active-backup bonding.
// TODO - Ensure ipv6 connectivity exists
// Only assign the fastest interface to ipv6.
let fastest_interface = ipv6_interfaces
.first()
.context("Could not find any network interfaces")?;
Expand All @@ -192,7 +156,7 @@ pub fn generate_systemd_config_files(
&network_info.ipv6_gateway.to_string(),
)?;

print!("Restarting systemd networkd");
println!("Restarting systemd networkd");
restart_systemd_networkd();

Ok(())
Expand Down

0 comments on commit f96dec1

Please sign in to comment.