You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

449 lines
14 KiB
Rust

use std::{fmt::{Display, Formatter}, fs::OpenOptions, io::{Read, Write}};
use rand::Rng;
static NETWORK_FILE: &str = "/home/rocker/network";
fn get_gateway_addr(addr: &str) -> String {
let prefix = &addr[..addr.len() - 1];
format!("{}0", prefix)
}
fn create_bridge_dev(bridge_name: &str) -> bool {
// brctl addbr br0
let args = ["addbr", bridge_name];
let out = std::process::Command::new("brctl")
.args(args)
.output()
.unwrap();
if !out.status.success() {
println!("create_bridge_dev: {args:?}");
println!("{:?}", out);
return false
}
true
}
fn set_bridge_ip(bridge_name: &str, addr: &str) -> bool {
// 设置容器内从ip: ip addr add 192.168.124.1/24 dev bridge_name
let args = ["addr", "add", &format!("{addr}/24"), "dev", bridge_name];
let out = std::process::Command::new("ip")
.args(args)
.output()
.unwrap();
if !out.status.success() {
println!("set_bridge_ip: {args:?}");
println!("{:?}", out);
return false;
}
true
}
fn set_slave_ip(slave_veth_name: &str, addr: &str, pid: &str) -> bool {
// nsenter -t 10050 -n -- ip addr add <ip_address>/<subnet_mask> dev veth1
let args = ["-t", pid, "-n", "--", "ip", "addr", "add", &format!("{addr}/24"), "dev", slave_veth_name];
let out = std::process::Command::new("nsenter")
.args(args)
.output()
.unwrap();
if !out.status.success() {
println!("set_slave_ip: {args:?}");
println!("{:?}", out);
return false;
}
true
}
fn set_snat(gateway_addr: &str, bridge_name: &str, action: &str) -> bool {
// sudo iptables -t nat -A POSTROUTING -s 172.18.0.0/24 ! -o br0 -j MASQUERADE
let args = ["-t", "nat", action, "POSTROUTING", "-s", &format!("{gateway_addr}/24"), "!", "-o", bridge_name, "-j", "MASQUERADE"];
let out = std::process::Command::new("iptables")
.args(args)
.output()
.unwrap();
if !out.status.success() {
println!("set_snat: {args:?}");
println!("{:?}", out);
return false;
}
true
}
fn set_iptables_forward() -> bool {
// sudo sysctl net.ipv4.conf.all.forwarding=1
let args = ["net.ipv4.conf.all.forwarding=1"];
let out = std::process::Command::new("sysctl")
.args(args)
.output()
.unwrap();
if !out.status.success() {
println!("set_iptables_forward: {args:?}");
println!("{:?}", out);
return false;
}
// sudo iptables -t filter -P FORWARD ACCEPT
let args = ["-t", "filter", "-P", "FORWARD", "ACCEPT"];
let out = std::process::Command::new("iptables")
.args(args)
.output()
.unwrap();
if !out.status.success() {
println!("set_iptables_forward: {args:?}");
println!("{:?}", out);
return false;
}
true
}
fn set_slave_route(slave_veth_name: &str, bridge_addr: &str, pid: &str) -> bool {
// sudo nsenter -t 1851 -n -- ip route add default via 172.18.0.1 dev veth3
let args = ["-t", pid, "-n", "--", "ip", "route", "add", "default", "via", bridge_addr, "dev", slave_veth_name];
let out = std::process::Command::new("nsenter")
.args(args)
.output()
.unwrap();
if !out.status.success() {
println!("set_slave_route: {args:?}");
println!("{:?}", out);
return false;
}
true
}
fn set_net_up(bridge_name: &str, master_veth_name: &str, pid: &str, slave_veth_name: &str) -> bool {
// sudo ip link set bridge_name up
let args = ["link", "set", bridge_name, "up"];
let out = std::process::Command::new("ip")
.args(args)
.output()
.unwrap();
if !out.status.success() {
println!("set_up: {args:?}");
println!("{:?}", out);
return false;
}
// sudo ip link set master_veth_name up
let args = ["link", "set", master_veth_name, "up"];
let out = std::process::Command::new("ip")
.args(args)
.output()
.unwrap();
if !out.status.success() {
println!("set_up: {args:?}");
println!("{:?}", out);
return false;
}
// nsenter -t 1970 -n -- ip link set dev slave_veth_name up
let args = ["-t", pid, "-n", "--", "ip", "link", "set", "dev", slave_veth_name, "up"];
let out = std::process::Command::new("nsenter")
.args(args)
.output()
.unwrap();
if !out.status.success() {
println!("set_up: {args:?}");
println!("{:?}", out);
return false;
}
// ip link set lo up
let args = ["-t", pid, "-n", "--", "ip", "link", "set", "lo", "up"];
let out = std::process::Command::new("nsenter")
.args(args)
.output()
.unwrap();
if !out.status.success() {
println!("set_up: {args:?}");
println!("{:?}", out);
return false;
}
true
}
fn set_up(dev_name: &str) {
// sudo ip link set bridge_name up
let args = ["link", "set", dev_name, "up"];
println!("set_up: {args:?}");
let out = std::process::Command::new("ip")
.args(args)
.output()
.unwrap();
if !out.status.success() {
println!("{:?}", out);
}
}
fn set_route(bridge_name: &str, addr: &str) {
// ip route add 192.168.100.0/24 via 192.168.100.1 dev bridge_name
// sudo ip netns exec ns1 ip route add default via 172.18.0.1 dev veth0
// 把addr最后的1位替换为0
let gateway_addr = get_gateway_addr(addr);
// 删掉系统自动添加的 ip route del 172.18.0.0/24 dev br0
let args = ["route", "del", &format!("{gateway_addr}/24"), "dev", bridge_name];
println!("set_route: {args:?}");
let out = std::process::Command::new("ip")
.args(args)
.output()
.unwrap();
if !out.status.success() {
println!("{:?}", out);
}
let args = ["route", "add", &format!("{gateway_addr}/24"), "via", addr, "dev", bridge_name];
println!("set_route: {args:?}");
let out = std::process::Command::new("ip")
.args(args)
.output()
.unwrap();
if !out.status.success() {
println!("{:?}", out);
}
}
fn set_net(bridge_name: &str, addr: &str){
// sudo iptables -t nat -A POSTROUTING -s 172.18.0.0/24 ! -o bridge_name -j MASQUERADE
let gateway_addr = get_gateway_addr(addr);
let args = ["-t", "nat", "-A", "POSTROUTING", "-s", &format!("{gateway_addr}/24"), "!", "-o", bridge_name, "-j", "MASQUERADE"];
println!("set_net: {args:?}");
let out = std::process::Command::new("iptables")
.args(args)
.output()
.unwrap();
if !out.status.success() {
println!("{:?}", out);
}
}
fn del_bridge(bridge_name: &str) {
// sudo brctl delbr br0
let args = ["delbr", bridge_name];
println!("del_bridge: {args:?}");
let out = std::process::Command::new("brctl")
.args(args)
.output()
.unwrap();
if !out.status.success() {
println!("{:?}", out);
}
}
fn create_veth_pair(master_veth_name: &str, slave_veth_name: &str) -> bool {
// sudo ip link add master_veth_name type veth peer name slave_veth_name
let args = ["link", "add", master_veth_name, "type", "veth", "peer", "name", slave_veth_name];
let out = std::process::Command::new("ip")
.args(args)
.output()
.unwrap();
if !out.status.success() {
println!("create_veth_pair: {args:?}");
println!("{:?}", out);
return false;
}
return true;
}
#[derive(Debug, Clone)]
struct NetWrok {
gateway_addr: String, // 192.168.124.0
bridge_addr: String, // 192.168.124.1
slave_addr: String,
bridge_name: String, // ro_uuid[:8]_1 // ip 就是 192.168.124.1
master_veth_name: String, // ro_uuid[:8]_2 // 不分配ip 直接插到桥
slave_veth_name: String, // ro_uuid[:8]_3 // ip 就是 192.168.124.3, 插在容器中
}
impl Display for NetWrok {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{},{},{},{},{},{}", self.gateway_addr, self.bridge_addr, self.slave_addr, self.bridge_name, self.master_veth_name, self.slave_veth_name)
}
}
fn get_all_network() -> Vec<NetWrok> {
// 打开 NETWORK_FILE 所有已经配置的信息, 如果没有则创建
let mut f = OpenOptions::new()
.write(true)
.create(true)
.read(true)
.open(NETWORK_FILE)
.unwrap();
let mut text = String::new();
f.read_to_string(&mut text).unwrap();
let mut all_network = vec![];
for line in text.lines() {
let network_info_vec = line.split(",").collect::<Vec<&str>>();
all_network.push(NetWrok {
gateway_addr: network_info_vec[0].to_string(),
bridge_addr: network_info_vec[1].to_string(),
slave_addr: network_info_vec[2].to_string(),
bridge_name: network_info_vec[3].to_string(),
master_veth_name: network_info_vec[4].to_string(),
slave_veth_name: network_info_vec[5].to_string(),
})
}
all_network
}
fn master_join_bridge(bridge_name: &str, master_veth_name: &str) -> bool {
// 将主端链接到桥
//sudo brctl addif bridge_name master_veth_name
let args = ["addif", bridge_name, master_veth_name];
let out = std::process::Command::new("brctl")
.args(args)
.output()
.unwrap();
if !out.status.success() {
println!("master_join_bridge: {args:?}");
println!("{:?}", out);
return false;
}
true
}
fn write_network_info(all_network: Vec<NetWrok>) {
let mut f = OpenOptions::new()
.write(true)
.create(true)
.open(NETWORK_FILE)
.unwrap();
for n in all_network {
writeln!(f, "{}", n).unwrap();
}
}
fn slave_join_bridge(slave_veth_name: &str, pid: &str) -> bool {
// 将从端 链接到容器
// sudo ip link set slave_veth_name netns 1234
let args = ["link", "set", slave_veth_name, "netns", pid];
let out = std::process::Command::new("ip")
.args(args)
.output()
.unwrap();
if !out.status.success() {
println!("slave_join_bridge: {args:?}");
println!("{:?}", out);
return false;
}
true
}
pub fn create_network(uuid_name: &str, pid: i32) -> bool {
// 创建一个随机地址段的 没分配过的ip
let mut all_network = get_all_network();
let mut rg = rand::thread_rng();
let network = loop {
// 生成一个随机桥ip
let base_addr = format!("10.{}.{}", rg.gen_range(0..255), rg.gen_range(0..255));
let gateway_addr = format!("{base_addr}.0");
let bridge_addr = format!("{base_addr}.1");
let slave_addr = format!("{base_addr}.3");
if all_network.iter().any(|n|n.bridge_addr == bridge_addr) == false {
let bridge_name = format!("ro_{uuid_name}_1");
let master_veth_name = format!("ro_{uuid_name}_2");
let slave_veth_name = format!("ro_{uuid_name}_3");
let net_work = NetWrok {
gateway_addr,
bridge_addr,
slave_addr,
bridge_name,
master_veth_name,
slave_veth_name,
};
break net_work;
}
};
println!("{network}");
all_network.push(network.clone());
// 写入到文件中
write_network_info(all_network);
// 系统中创建桥
if create_bridge_dev(&network.bridge_name) &&
// 给桥分配ip
set_bridge_ip(&network.bridge_name, &network.bridge_addr) &&
// 创建veth pair
create_veth_pair(&network.master_veth_name, &network.slave_veth_name) &&
// 宿主机 主pair 连接桥
master_join_bridge(&network.bridge_name, &network.master_veth_name) &&
// 把从 pair 添加到容器内
slave_join_bridge(&network.slave_veth_name, pid.to_string().as_str()) &&
// 给容器内 pair 分配ip
set_slave_ip(&network.slave_veth_name, &network.slave_addr, pid.to_string().as_str()) &&
// 激活 上线
set_net_up(&network.bridge_name, &network.master_veth_name, pid.to_string().as_str(), &&network.slave_veth_name) &&
// 访问公网设置
// 配置路由
set_slave_route(&network.slave_veth_name, &network.bridge_addr, pid.to_string().as_str()) &&
// 设置转发规则
set_iptables_forward() &&
// 设置snat
set_snat(&network.gateway_addr, &network.bridge_name, "-A") {
println!("create_network success");
true
} else {
println!("create_network failed");
remove_network(&uuid_name);
false
}
}
fn remove_network(uuid_name: &str) {
let mut all_network = get_all_network();
let mut network = None;
all_network = all_network.into_iter()
.filter_map(|n| {
if n.bridge_name.contains(uuid_name) {
network = Some(n);
None
} else {
Some(n)
}
}).collect::<Vec<NetWrok>>();
if let Some(network) = network {
}
// todo 回写到文件中
// write_network_info(all_network);
}
fn main(){
let container_id = uuid::Uuid::new_v4().to_string()[0..8].to_string();
// create_network(&container_id, 17543);
remove_network("ac1aca8d");
return;
let addr = "192.168.124.1";
let bridge_name = &format!("rocker_{}", addr[8..].replace(".", "_"));
println!("{bridge_name}");
println!("{addr}");
create_bridge_dev(&bridge_name);
set_bridge_ip(&bridge_name, &addr);
set_up(&bridge_name);
// 我们在 ip addr add 172.18.0.1/24 dev br0 的时候补全了网段信息 /24, 所以在 ip link set br0 up的时候,
// 会自动配置路由表配置路由表 192.168.0.0/24 转发到这 bridge_name 的网络接口上
// set_route(bridge_name, addr);
set_net(bridge_name, addr);
let veth_name = "yanguang";
// connect(bridge_name, veth_name);
}