Skip to content

Commit

Permalink
adding local_broadcast_ip
Browse files Browse the repository at this point in the history
  • Loading branch information
JustBobinAround committed Feb 20, 2024
1 parent e03c5b4 commit 165e3df
Show file tree
Hide file tree
Showing 2 changed files with 251 additions and 1 deletion.
13 changes: 12 additions & 1 deletion examples/show_ip_and_ifs.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
use local_ip_address::{list_afinet_netifas, local_ip, local_ipv6};
use local_ip_address::{
list_afinet_netifas,
local_ip,
local_ipv6,
local_broadcast_ip,
};

fn main() {
match local_ip() {
Expand All @@ -11,6 +16,11 @@ fn main() {
Err(err) => println!("Failed to get local IPv6: {}", err),
};

match local_broadcast_ip() {
Ok(ip) => println!("Local broadcast IPv4: {}", ip),
Err(err) => println!("Failed to get local broadcast IPv4: {}", err),
};

match list_afinet_netifas() {
Ok(netifs) => {
println!("Got {} interfaces", netifs.len());
Expand All @@ -21,3 +31,4 @@ fn main() {
Err(err) => println!("Failed to get list of network interfaces: {}", err),
};
}

239 changes: 239 additions & 0 deletions src/linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,244 @@ pub fn local_ipv6() -> Result<IpAddr, Error> {
local_ip_impl(Inet6)
}

/// Retrieves the local broadcast IPv4 address for this system
pub fn local_broadcast_ip() -> Result<IpAddr, Error> {
local_broadcast_impl(Inet)
}

fn local_broadcast_impl(family: RtAddrFamily) -> Result<IpAddr, Error> {
let mut netlink_socket = NlSocketHandle::connect(NlFamily::Route, None, &[])
.map_err(|err| Error::StrategyError(err.to_string()))?;

let route_attr = match family {
Inet => {
let dstip = Ipv4Addr::new(192, 0, 2, 0); // reserved external IP
let raw_dstip = u32::from(dstip).to_be();
Rtattr::new(None, Rta::Dst, raw_dstip)
}
Inet6 => {
let dstip = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0); // reserved external IP
let raw_dstip = u128::from(dstip).to_be();
Rtattr::new(None, Rta::Dst, raw_dstip)
}
_ => Err(Error::StrategyError(format!(
"Invalid address family given: {:#?}",
family
)))?,
};

let route_attr = route_attr.map_err(|err| Error::StrategyError(err.to_string()))?;
let mut route_payload = RtBuffer::new();
route_payload.push(route_attr);
let ifroutemsg = Rtmsg {
rtm_family: family,
rtm_dst_len: 0,
rtm_src_len: 0,
rtm_tos: 0,
rtm_table: RtTable::Unspec,
rtm_protocol: Rtprot::Unspec,
rtm_scope: RtScope::Universe,
rtm_type: Rtn::Unspec,
rtm_flags: RtmFFlags::new(RTM_FLAGS_LOOKUP),
rtattrs: route_payload,
};
let netlink_message = Nlmsghdr::new(
None,
Rtm::Getroute,
NlmFFlags::new(&[NlmF::Request]),
None,
None,
NlPayload::Payload(ifroutemsg),
);

netlink_socket
.send(netlink_message)
.map_err(|err| Error::StrategyError(err.to_string()))?;

let mut pref_ip = None;
for response in netlink_socket.iter(false) {
if pref_ip.is_some() {
break;
}
let header: Nlmsghdr<Rtm, Rtmsg> = response.map_err(|err| {
if let Nlmsgerr(ref err) = err {
if err.error == -libc::ENETUNREACH {
return Error::LocalIpAddressNotFound;
}
}
Error::StrategyError(format!(
"An error occurred retrieving Netlink's socket response: {err}",
))
})?;

if let NlPayload::Empty = header.nl_payload {
continue;
}

if header.nl_type != Rtm::Newroute {
return Err(Error::StrategyError(String::from(
"The Netlink header type is not the expected",
)));
}

let p = header.get_payload().map_err(|_| {
Error::StrategyError(String::from(
"An error occurred getting Netlink's header payload",
))
})?;

if p.rtm_scope != RtScope::Universe {
continue;
}

if p.rtm_family != family {
Err(Error::StrategyError(format!(
"Invalid address family in Netlink payload: {:?}",
p.rtm_family
)))?
}

for rtattr in p.rtattrs.iter() {
if rtattr.rta_type == Rta::Prefsrc {
if p.rtm_family == Inet {
let addr = Ipv4Addr::from(u32::from_be(
rtattr.get_payload_as::<u32>().map_err(|_| {
Error::StrategyError(String::from(
"An error occurred retrieving Netlink's route payload attribute",
))
})?,
));
pref_ip = Some(IpAddr::V4(addr));
break;
}
let addr = Ipv6Addr::from(u128::from_be(
rtattr.get_payload_as::<u128>().map_err(|_| {
Error::StrategyError(String::from(
"An error occurred retrieving Netlink's route payload attribute",
))
})?,
));
pref_ip = Some(IpAddr::V6(addr));
break;
}
}
}

let ifaddrmsg = Ifaddrmsg {
ifa_family: family,
ifa_prefixlen: 0,
ifa_flags: IfaFFlags::empty(),
ifa_scope: 0,
ifa_index: 0,
rtattrs: RtBuffer::new(),
};
let netlink_message = Nlmsghdr::new(
None,
Rtm::Getaddr,
NlmFFlags::new(&[NlmF::Request, NlmF::Root]),
None,
None,
NlPayload::Payload(ifaddrmsg),
);

netlink_socket
.send(netlink_message)
.map_err(|err| Error::StrategyError(err.to_string()))?;

let mut broadcast_ip = None;
for response in netlink_socket.iter(false) {
let header: Nlmsghdr<Rtm, Ifaddrmsg> = response.map_err(|_| {
Error::StrategyError(String::from(
"An error occurred retrieving Netlink's socket response",
))
})?;

if let NlPayload::Empty = header.nl_payload {
continue;
}

if header.nl_type != Rtm::Newaddr {
return Err(Error::StrategyError(String::from(
"The Netlink header type is not the expected",
)));
}

let p = header.get_payload().map_err(|_| {
Error::StrategyError(String::from(
"An error occurred getting Netlink's header payload",
))
})?;

if RtScope::from(p.ifa_scope) != RtScope::Universe {
continue;
}

if p.ifa_family != family {
Err(Error::StrategyError(format!(
"Invalid family in Netlink payload: {:?}",
p.ifa_family
)))?
}

let mut is_match = false;
for rtattr in p.rtattrs.iter() {
if rtattr.rta_type == Ifa::Local {
if p.ifa_family == Inet {
let addr = Ipv4Addr::from(u32::from_be(
rtattr.get_payload_as::<u32>().map_err(|_| {
Error::StrategyError(String::from(
"An error occurred retrieving Netlink's route payload attribute",
))
})?,
));
is_match = pref_ip == Some(IpAddr::V4(addr));
} else {
let addr = Ipv6Addr::from(u128::from_be(
rtattr.get_payload_as::<u128>().map_err(|_| {
Error::StrategyError(String::from(
"An error occurred retrieving Netlink's route payload attribute",
))
})?,
));
is_match = pref_ip == Some(IpAddr::V6(addr));
}
}
if is_match {
if rtattr.rta_type == Ifa::Broadcast {
if p.ifa_family == Inet {
let addr = Ipv4Addr::from(u32::from_be(
rtattr.get_payload_as::<u32>().map_err(|_| {
Error::StrategyError(String::from(
"An error occurred retrieving Netlink's route payload broadcast attribute",
))
})?,
));
return Ok(IpAddr::V4(addr));
}
}
}
if rtattr.rta_type == Ifa::Broadcast {
if p.ifa_family == Inet {
let addr = Ipv4Addr::from(u32::from_be(
rtattr.get_payload_as::<u32>().map_err(|_| {
Error::StrategyError(String::from(
"An error occurred retrieving Netlink's route payload attribute",
))
})?,
));
broadcast_ip = Some(IpAddr::V4(addr));
}
}
}
}

if let Some(broadcast_ip) = broadcast_ip {
return Ok(broadcast_ip);
}

Err(Error::LocalIpAddressNotFound)
}

fn local_ip_impl(family: RtAddrFamily) -> Result<IpAddr, Error> {
let mut netlink_socket = NlSocketHandle::connect(NlFamily::Route, None, &[])
.map_err(|err| Error::StrategyError(err.to_string()))?;
Expand Down Expand Up @@ -482,3 +720,4 @@ mod tests {
assert_eq!(res.unwrap(), expected);
}
}

0 comments on commit 165e3df

Please sign in to comment.