nzrdhcp: make it actually work
* Check the DHCP options for the requested IPv4 address * Update yiaddr, not siaddr or ciaddr * Read the RFC a tenth time. I think I've got it now
This commit is contained in:
parent
9ca1b0c821
commit
3d0ea1f2ef
|
@ -3,6 +3,7 @@ use diesel::{
|
|||
SqliteConnection,
|
||||
};
|
||||
use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
|
||||
use log::trace;
|
||||
use nzr_virt::{vol, Connection};
|
||||
use std::ops::Deref;
|
||||
use thiserror::Error;
|
||||
|
@ -64,6 +65,7 @@ impl InnerCtx {
|
|||
baseimg: conn.get_pool(&config.storage.base_image_pool).await?,
|
||||
};
|
||||
|
||||
trace!("Connecting to database");
|
||||
let db_uri = config.db_uri.clone();
|
||||
let sqldb = tokio::task::spawn_blocking(|| {
|
||||
let manager = ConnectionManager::<SqliteConnection>::new(db_uri);
|
||||
|
@ -74,6 +76,7 @@ impl InnerCtx {
|
|||
.unwrap()?;
|
||||
|
||||
{
|
||||
trace!("Running pending migrations");
|
||||
let mut conn = sqldb.get()?;
|
||||
tokio::task::spawn_blocking(move || {
|
||||
conn.run_pending_migrations(MIGRATIONS)
|
||||
|
|
|
@ -5,7 +5,7 @@ use std::{net::Ipv4Addr, process::ExitCode};
|
|||
use ctx::Context;
|
||||
use dhcproto::{
|
||||
v4::{DhcpOption, Message, MessageType, Opcode, OptionCode},
|
||||
Decodable, Decoder,
|
||||
Decodable, Decoder, Encodable, Encoder,
|
||||
};
|
||||
use nzr_api::{config::Config, net::mac::MacAddr};
|
||||
use std::net::SocketAddr;
|
||||
|
@ -16,9 +16,9 @@ const DEFAULT_LEASE: u32 = 86400;
|
|||
|
||||
fn make_reply(msg: &Message, msg_type: MessageType, lease_addr: Option<Ipv4Addr>) -> Message {
|
||||
let mut resp = Message::new(
|
||||
EMPTY_V4,
|
||||
EMPTY_V4,
|
||||
lease_addr.unwrap_or(EMPTY_V4),
|
||||
EMPTY_V4,
|
||||
msg.giaddr(),
|
||||
msg.chaddr(),
|
||||
);
|
||||
|
@ -46,6 +46,7 @@ async fn handle_message(ctx: &Context, from: SocketAddr, msg: &Message) {
|
|||
tracing::info!("Received DHCP payload with invalid addr (different media type?)");
|
||||
return;
|
||||
};
|
||||
tracing::debug!("Client MAC is {client_mac}!!!");
|
||||
|
||||
let instance = match ctx.instance_by_mac(client_mac).await {
|
||||
Ok(Some(i)) => i,
|
||||
|
@ -59,6 +60,11 @@ async fn handle_message(ctx: &Context, from: SocketAddr, msg: &Message) {
|
|||
}
|
||||
};
|
||||
|
||||
tracing::info!(
|
||||
"Recieved {msg_type:?} from {client_mac} (assuming {})",
|
||||
&instance.name
|
||||
);
|
||||
|
||||
let mut lease_time = None;
|
||||
let mut nak = false;
|
||||
|
||||
|
@ -68,12 +74,18 @@ async fn handle_message(ctx: &Context, from: SocketAddr, msg: &Message) {
|
|||
make_reply(msg, MessageType::Offer, Some(instance.lease.addr.addr))
|
||||
}
|
||||
MessageType::Request => {
|
||||
if msg.ciaddr() != instance.lease.addr.addr {
|
||||
if let Some(DhcpOption::RequestedIpAddress(addr)) =
|
||||
msg.opts().get(OptionCode::RequestedIpAddress)
|
||||
{
|
||||
if *addr == instance.lease.addr.addr {
|
||||
make_reply(msg, MessageType::Ack, Some(instance.lease.addr.addr))
|
||||
} else {
|
||||
nak = true;
|
||||
make_reply(msg, MessageType::Nak, None)
|
||||
}
|
||||
} else {
|
||||
nak = true;
|
||||
make_reply(msg, MessageType::Nak, None)
|
||||
} else {
|
||||
lease_time = Some(DEFAULT_LEASE);
|
||||
make_reply(msg, MessageType::Ack, Some(instance.lease.addr.addr))
|
||||
}
|
||||
}
|
||||
MessageType::Decline => {
|
||||
|
@ -86,57 +98,80 @@ async fn handle_message(ctx: &Context, from: SocketAddr, msg: &Message) {
|
|||
}
|
||||
MessageType::Release => {
|
||||
// We only provide static leases
|
||||
tracing::trace!("Ignoring DHCPRELEASE");
|
||||
tracing::debug!("Ignoring DHCPRELEASE");
|
||||
return;
|
||||
}
|
||||
MessageType::Inform => make_reply(msg, MessageType::Ack, None),
|
||||
other => {
|
||||
tracing::trace!("Received unhandled message {other:?}");
|
||||
tracing::info!("Received unhandled message {other:?}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let opts = response.opts_mut();
|
||||
let giaddr = if msg.giaddr().is_unspecified() {
|
||||
todo!("no relay??")
|
||||
} else {
|
||||
msg.giaddr()
|
||||
};
|
||||
|
||||
opts.insert(DhcpOption::ServerIdentifier(giaddr));
|
||||
if let Some(time) = lease_time {
|
||||
opts.insert(DhcpOption::AddressLeaseTime(time));
|
||||
}
|
||||
|
||||
if !nak {
|
||||
// Get general networking info
|
||||
let subnet = match ctx.get_subnet(&instance.lease.subnet).await {
|
||||
Ok(Some(net)) => net,
|
||||
Ok(None) => {
|
||||
tracing::error!("nzrd says '{}' isn't a subnet", &instance.lease.subnet);
|
||||
return;
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::error!("Error getting subnet: {err}");
|
||||
return;
|
||||
}
|
||||
{
|
||||
let opts = response.opts_mut();
|
||||
let giaddr = if msg.giaddr().is_unspecified() {
|
||||
todo!("no relay??")
|
||||
} else {
|
||||
msg.giaddr()
|
||||
};
|
||||
|
||||
opts.insert(DhcpOption::Hostname(instance.name.clone()));
|
||||
|
||||
if !subnet.dns.is_empty() {
|
||||
opts.insert(DhcpOption::DomainNameServer(subnet.dns));
|
||||
opts.insert(DhcpOption::ServerIdentifier(giaddr));
|
||||
if let Some(time) = lease_time {
|
||||
opts.insert(DhcpOption::AddressLeaseTime(time));
|
||||
}
|
||||
|
||||
if let Some(name) = subnet.domain_name {
|
||||
opts.insert(DhcpOption::DomainName(name.to_utf8()));
|
||||
}
|
||||
if !nak {
|
||||
// Get general networking info
|
||||
let subnet = match ctx.get_subnet(&instance.lease.subnet).await {
|
||||
Ok(Some(net)) => net,
|
||||
Ok(None) => {
|
||||
tracing::error!("nzrd says '{}' isn't a subnet", &instance.lease.subnet);
|
||||
return;
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::error!("Error getting subnet: {err}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(gw) = subnet.gateway4 {
|
||||
opts.insert(DhcpOption::Router(Vec::from(&[gw])));
|
||||
}
|
||||
opts.insert(DhcpOption::Hostname(instance.name.clone()));
|
||||
|
||||
opts.insert(DhcpOption::SubnetMask(instance.lease.addr.netmask()));
|
||||
if !subnet.dns.is_empty() {
|
||||
opts.insert(DhcpOption::DomainNameServer(subnet.dns));
|
||||
}
|
||||
|
||||
if let Some(name) = subnet.domain_name {
|
||||
opts.insert(DhcpOption::DomainName(name.to_utf8()));
|
||||
}
|
||||
|
||||
if let Some(gw) = subnet.gateway4 {
|
||||
opts.insert(DhcpOption::Router(Vec::from(&[gw])));
|
||||
}
|
||||
|
||||
opts.insert(DhcpOption::SubnetMask(instance.lease.addr.netmask()));
|
||||
}
|
||||
}
|
||||
|
||||
tracing::info!(
|
||||
"Sending message {:?} with yiaddr {}",
|
||||
response
|
||||
.opts()
|
||||
.get(OptionCode::MessageType)
|
||||
.unwrap_or(&DhcpOption::End),
|
||||
response.yiaddr()
|
||||
);
|
||||
|
||||
// unicast it back
|
||||
let mut resp_buf = Vec::new();
|
||||
let mut enc = Encoder::new(&mut resp_buf);
|
||||
if let Err(err) = response.encode(&mut enc) {
|
||||
tracing::error!("Couldn't encode response: {err}");
|
||||
return;
|
||||
}
|
||||
|
||||
if let Err(err) = ctx.sock().send_to(&resp_buf, from).await {
|
||||
tracing::error!("Couldn't send response: {err}");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue