diff --git a/Cargo.lock b/Cargo.lock index c26463f..cfd4736 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -654,7 +654,7 @@ dependencies = [ [[package]] name = "dhcproto" version = "0.9.0-alpha" -source = "git+https://github.com/bluecatengineering/dhcproto#320262dfcf0d48b2deafa64bf73dddf8b6a0c3a5" +source = "git+https://github.com/bluecatengineering/dhcproto#195f6f41d2721e46fefad07644e825f936f6d9d8" dependencies = [ "hex", "ipnet", diff --git a/README.md b/README.md index c2b607f..6e7ac37 100644 --- a/README.md +++ b/README.md @@ -143,10 +143,10 @@ We _could_ go much faster by keeping leases in memory and appending to the db li [perfdhcp](https://kea.readthedocs.io/en/kea-2.0.1/man/perfdhcp.8.html) can be used to test dora, include `giaddr`, the subnet select option or the relay agent link selection opt, you can use this as a starting point: ``` -sudo perfdhcp -4 -N 9901 -L 9903 -r 1 -xi -t 1 -o 118,C0A80001 -R 100 127.0.0.1 +sudo perfdhcp -4 -N 9900 -L 9903 -r 1 -xi -t 1 -o 118,C0A80001 -R 100 127.0.0.1 ``` -This will start perfdhcp using dhcpv4, send messages to `127.0.0.1:9901`, listen on port `9903` at a rate of 1/sec, and using 100 different devices. It includes the subnet select opt (118) with `C0A80001` as a hex encoded value of the integer of `192.168.0.1`. `dora` must be listening on `9901` and have a config with ranges to allocate on the `192.168.0.1` network. +This will start perfdhcp using dhcpv4, send messages to `127.0.0.1:9900`, listen on port `9903` at a rate of 1/sec, and using 100 different devices. It includes the subnet select opt (118) with `C0A80001` as a hex encoded value of the integer of `192.168.0.1`. `dora` must be listening on `9900` and have a config with ranges to allocate on the `192.168.0.1` network. ### Setting up dora on the PI @@ -156,4 +156,4 @@ See [PI setup](./docs/pi_setup.md) If you find a bug, or see something that doesn't look right, please open an issue and let us know. We welcome any and all constructive feedback. -We're still actively working on this. Some of the things we'd like to add in the future include: DDNS updates, stateful DHCPv6, HA & Client classification. +We're still actively working on this. Some of the things we'd like to add in the future include: DDNS updates, stateful DHCPv6, HA & Client classification. diff --git a/basic.yaml b/basic.yaml deleted file mode 100644 index 4bba2c5..0000000 --- a/basic.yaml +++ /dev/null @@ -1,118 +0,0 @@ -chaddr_only: false -interfaces: - - srv -networks: - 192.168.1.100/30: - probation_period: 86400 - ranges: - - - start: 192.168.1.100 - end: 192.168.1.103 - config: - lease_time: - default: 3600 - min: 1200 - max: 4800 - options: - values: - 1: - type: ip - value: 192.168.1.1 - 3: - type: ip_list - value: - - 192.168.1.1 - - 192.168.2.0/24: - probation_period: 86400 - ranges: - - - start: 192.168.2.100 - end: 192.168.2.150 - config: - lease_time: - default: 3600 - min: 1200 - max: 4800 - options: - values: - 1: - type: ip - value: 192.168.2.1 - 3: - type: ip_list - value: - - 192.168.2.1 - 40: - type: str - value: testdomain.com - 253: - type: hex - value: 123ABC - except: - - 192.168.2.123 - - 192.168.2.124 - - reservations: - - - ip: 192.168.2.160 - config: - lease_time: - default: 3600 - min: 1200 - max: 4800 - options: - values: - 1: - type: ip - value: 192.168.2.1 - 3: - type: ip_list - value: - - 192.168.2.1 - match: - options: - values: - 61: - type: hex - value: 001122334455 - - - ip: 192.168.2.170 - config: - lease_time: - default: 3600 - min: 1200 - max: 4800 - options: - values: - 1: - type: ip - value: 10.10.0.1 - 3: - type: ip_list - value: - - 10.10.0.1 - match: - chaddr: aa:bb:cc:dd:ee:ff - 10.0.0.0/16: - ranges: - - - start: 10.0.0.10 - end: 10.0.6.254 - config: - lease_time: - default: 3600 - min: 1200 - max: 4800 - options: - values: - 1: - type: ip - value: 10.10.0.1 - 3: - type: ip_list - value: - - 10.10.0.1 - except: - - 10.0.0.123 - - 10.0.0.124 diff --git a/bin/README.md b/bin/README.md index a700865..edeeadf 100644 --- a/bin/README.md +++ b/bin/README.md @@ -62,13 +62,13 @@ OPTIONS: Run on non-standard ports: ``` -dora -c /path/to/config.yaml --v4-addr 0.0.0.0:9901 +dora -c /path/to/config.yaml --v4-addr 0.0.0.0:9900 ``` is equivalent to: ``` -V4_ADDR="0.0.0.0:9901" CONFIG_PATH="/path/to/config.yaml" dora +V4_ADDR="0.0.0.0:9900" CONFIG_PATH="/path/to/config.yaml" dora ``` Use `DORA_LOG` to control dora's log level. Takes same arguments as `RUST_LOG` diff --git a/bin/tests/basic.rs b/bin/tests/basic.rs index 3e9d85f..02e3c63 100644 --- a/bin/tests/basic.rs +++ b/bin/tests/basic.rs @@ -30,7 +30,7 @@ fn test_basic_dhcpv4_unicast() -> Result<()> { let settings = ClientSettingsBuilder::default() .iface_name("dhcpcli") .target("192.168.2.1".parse::().unwrap()) - .port(9901_u16) + .port(9900_u16) .build()?; // create a client that sends dhcpv4 messages let mut client = Client::::new(settings); @@ -79,7 +79,7 @@ fn static_chaddr_dora() -> Result<()> { let settings = ClientSettingsBuilder::default() .iface_name("dhcpcli") .target("192.168.2.1".parse::().unwrap()) - .port(9901_u16) + .port(9900_u16) .build()?; let chaddr = "aa:bb:cc:dd:ee:ff".parse::()?.octets(); @@ -126,7 +126,7 @@ fn static_opt_dora() -> Result<()> { let settings = ClientSettingsBuilder::default() .iface_name("dhcpcli") .target("192.168.2.1".parse::().unwrap()) - .port(9901_u16) + .port(9900_u16) .build()?; let mut client = Client::::new(settings); @@ -178,7 +178,7 @@ fn discover_req_addr() -> Result<()> { let settings = ClientSettingsBuilder::default() .iface_name("dhcpcli") .target("192.168.2.1".parse::().unwrap()) - .port(9901_u16) + .port(9900_u16) .build()?; // create a client that sends dhcpv4 messages @@ -226,7 +226,7 @@ fn request_nak() -> Result<()> { let settings = ClientSettingsBuilder::default() .iface_name("dhcpcli") .target("192.168.2.1".parse::().unwrap()) - .port(9901_u16) + .port(9900_u16) .build()?; // create a client that sends dhcpv4 messages @@ -270,7 +270,7 @@ fn requested_lease_time() -> Result<()> { let settings = ClientSettingsBuilder::default() .iface_name("dhcpcli") .target("192.168.2.1".parse::().unwrap()) - .port(9901_u16) + .port(9900_u16) .build()?; // create a client that sends dhcpv4 messages let mut client = Client::::new(settings); @@ -327,7 +327,7 @@ fn test_requested_opts() -> Result<()> { let settings = ClientSettingsBuilder::default() .iface_name("dhcpcli") .target("192.168.2.1".parse::().unwrap()) - .port(9901_u16) + .port(9900_u16) .build()?; // create a client that sends dhcpv4 messages let mut client = Client::::new(settings); @@ -363,7 +363,7 @@ fn test_exclusions() -> Result<()> { let settings = ClientSettingsBuilder::default() .iface_name("dhcpcli") .target("192.168.2.1".parse::().unwrap()) - .port(9901_u16) + .port(9900_u16) .build()?; // create a client that sends dhcpv4 messages let mut client = Client::::new(settings); @@ -403,7 +403,7 @@ fn test_decline() -> Result<()> { let settings = ClientSettingsBuilder::default() .iface_name("dhcpcli") .target("192.168.2.1".parse::().unwrap()) - .port(9901_u16) + .port(9900_u16) .build()?; // create a client that sends dhcpv4 messages let mut client = Client::::new(settings); diff --git a/bin/tests/common/env.rs b/bin/tests/common/env.rs index b61003c..7aa23e6 100644 --- a/bin/tests/common/env.rs +++ b/bin/tests/common/env.rs @@ -83,7 +83,7 @@ fn start_dhcp_server(config: &str, netns: &str, db: &str) -> Child { let workspace_root = env::var("WORKSPACE_ROOT").unwrap_or_else(|_| "..".to_owned()); let config_path = format!("{workspace_root}/bin/tests/test_configs/{config}"); let dora_debug = format!( - "./{workspace_root}/target/debug/dora -d={db} --config-path={config_path} --threads=2 --dora-log=debug --v4-addr=0.0.0.0:9901", + "./{workspace_root}/target/debug/dora -d={db} --config-path={config_path} --threads=2 --dora-log=debug --v4-addr=0.0.0.0:9900", ); let cmd = format!("ip netns exec {netns} {dora_debug}"); diff --git a/dora-core/src/server/context.rs b/dora-core/src/server/context.rs index 062359c..e8527bc 100644 --- a/dora-core/src/server/context.rs +++ b/dora-core/src/server/context.rs @@ -465,6 +465,8 @@ impl MsgContext { v4::MessageType::Nak => { let giaddr = resp.giaddr(); resp.clear_addrs(); + resp.clear_fname(); + resp.clear_sname(); resp.set_giaddr(giaddr); // remove all opts. in the future, we may need to remove exclusively // what was added in the param req list, for now we will just remove all diff --git a/example.yaml b/example.yaml index 6abd753..1c47597 100644 --- a/example.yaml +++ b/example.yaml @@ -42,6 +42,11 @@ networks: # OR IF IT IS NOT specified, dora will use the IP of the interface we recv'd the message on. # OR If bound to loopback, we will just use the first non-loopback interface IP server_id: 192.168.5.1 + # this will replace the `sname` field in the DHCP header + # server_name: "example.org" + # + # this will replace the `fname` field in the DHCP header + # file_name: "bootfile.efi" ranges: - # start of your range diff --git a/libs/config/src/v4.rs b/libs/config/src/v4.rs index 2ef4e18..ef210e3 100644 --- a/libs/config/src/v4.rs +++ b/libs/config/src/v4.rs @@ -124,6 +124,8 @@ impl Config { authoritative, server_id, ping_timeout_ms, + server_name, + file_name, } = net; let ranges = ranges.into_iter().map(|range| range.into()).collect(); @@ -160,6 +162,8 @@ impl Config { reserved_opts, authoritative, ping_timeout_ms: Duration::from_millis(ping_timeout_ms), + server_name, + file_name, }; // set total addr space for metrics dora_core::metrics::TOTAL_AVAILABLE_ADDRS.set(network.total_addrs() as i64); @@ -225,9 +229,17 @@ pub struct Network { /// with authoritative == true then dora will always try to respond /// to REQUEST/INFORM authoritative: bool, + server_name: Option, + file_name: Option, } impl Network { + pub fn server_name(&self) -> Option<&str> { + self.server_name.as_deref() + } + pub fn file_name(&self) -> Option<&str> { + self.file_name.as_deref() + } pub fn subnet(&self) -> Ipv4Addr { self.subnet.network() } diff --git a/libs/config/src/wire/v4.rs b/libs/config/src/wire/v4.rs index a86f15c..6df85a6 100644 --- a/libs/config/src/wire/v4.rs +++ b/libs/config/src/wire/v4.rs @@ -71,6 +71,8 @@ pub struct Net { /// Whether we are authoritative for this network (default: true) #[serde(default = "super::default_authoritative")] pub authoritative: bool, + pub server_name: Option, + pub file_name: Option, } #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] diff --git a/plugins/message-type/src/lib.rs b/plugins/message-type/src/lib.rs index 384d4e0..54cf6f1 100644 --- a/plugins/message-type/src/lib.rs +++ b/plugins/message-type/src/lib.rs @@ -66,9 +66,12 @@ impl Plugin for MsgType { .v4() .server_id(meta.ifindex, subnet) .context("cannot find server_id")?; - + // look up which network the message belongs to + let network = self.cfg.v4().get_network(subnet); + let sname = network.and_then(|net| net.server_name()); + let fname = network.and_then(|net| net.file_name()); // message that will be returned - let mut resp = util::new_msg(req, server_id); + let mut resp = util::new_msg(req, server_id, sname, fname); // if there is a server identifier it must match ours if matches!(req.opts().get(OptionCode::ServerIdentifier), Some(DhcpOption::ServerIdentifier(id)) if *id != server_id && !id.is_unspecified()) @@ -84,9 +87,6 @@ impl Plugin for MsgType { resp.opts_mut() .insert(DhcpOption::ServerIdentifier(server_id)); - // look up which network the message belongs to - let network = self.cfg.v4().get_network(subnet); - match msg_type.context("no option 53 (message type) found")? { MessageType::Discover => { resp.opts_mut() @@ -148,7 +148,12 @@ impl Plugin for MsgType { pub mod util { use super::*; - pub fn new_msg(req: &Message, siaddr: Ipv4Addr) -> Message { + pub fn new_msg( + req: &Message, + siaddr: Ipv4Addr, + sname: Option<&str>, + fname: Option<&str>, + ) -> Message { let mut msg = Message::new_with_id( req.xid(), Ipv4Addr::UNSPECIFIED, @@ -160,6 +165,13 @@ pub mod util { msg.set_opcode(Opcode::BootReply) .set_htype(req.htype()) .set_flags(req.flags()); + // set the sname & fname header fields + if let Some(sname) = sname { + msg.set_sname_str(sname); + } + if let Some(fname) = fname { + msg.set_fname_str(fname); + } msg } }