Skip to content

Commit

Permalink
fix: dns probe (#172)
Browse files Browse the repository at this point in the history
* fix: wrong port for src

* fix: missing dns_event

* refactor(ebpf): if else to match in SockAddr
  • Loading branch information
qjerome authored Jan 23, 2025
1 parent 7d2a7dd commit c88352c
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 59 deletions.
124 changes: 78 additions & 46 deletions kunai-common/src/net/bpf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,79 +8,111 @@ use crate::consts::*;
use super::{Error, SockAddr, SocketInfo};

impl SockAddr {
#[inline(always)]
pub unsafe fn from_sockaddr_user(sa: sockaddr) -> Result<Self, Error> {
let sa_family = sa.sa_family_user().ok_or(Error::SaFamilyMissing)?;

match sa_family {
AF_INET => {
let sa_in = sockaddr_in::from(sa);

let addr = sa_in.s_addr_user().ok_or(Error::SaInAddrMissing)?.to_be();
let port = sa_in.sin_port_user().ok_or(Error::SaInPortMissing)?.to_be();

Ok(SockAddr::new_v4_from_be(addr, port))
}
AF_INET6 => {
let sa_in6 = sockaddr_in6::from(sa);

let addr = sa_in6
.sin6_addr_user()
.and_then(|in6| in6.addr32())
.ok_or(Error::SaIn6AddrMissing)?;
let port = sa_in6
.sin6_port_user()
.ok_or(Error::SaIn6PortMissing)?
.to_be();

Ok(SockAddr::new_v6_from_be(addr, port))
}
_ => Err(Error::UnsupportedSaFamily),
}
}

#[inline(always)]
pub unsafe fn from_sockaddr(sa: sockaddr) -> Result<Self, Error> {
let sa_family = sa.sa_family().ok_or(Error::SaFamilyMissing)?;

if sa_family == AF_INET {
let sa_in = sockaddr_in::from(sa);
match sa_family {
AF_INET => {
let sa_in = sockaddr_in::from(sa);

let addr = sa_in.s_addr().ok_or(Error::SaInAddrMissing)?.to_be();
let port = sa_in.sin_port().ok_or(Error::SaInPortMissing)?.to_be();
let addr = sa_in.s_addr().ok_or(Error::SaInAddrMissing)?.to_be();
let port = sa_in.sin_port().ok_or(Error::SaInPortMissing)?.to_be();

return Ok(SockAddr::new_v4_from_be(addr, port));
} else if sa_family == AF_INET6 {
let sa_in6 = sockaddr_in6::from(sa);
Ok(SockAddr::new_v4_from_be(addr, port))
}
AF_INET6 => {
let sa_in6 = sockaddr_in6::from(sa);

let addr = sa_in6
.sin6_addr()
.and_then(|in6| in6.addr32())
.ok_or(Error::SaIn6AddrMissing)?;
let port = sa_in6.sin6_port().ok_or(Error::SaIn6PortMissing)?.to_be();
let addr = sa_in6
.sin6_addr()
.and_then(|in6| in6.addr32())
.ok_or(Error::SaIn6AddrMissing)?;
let port = sa_in6.sin6_port().ok_or(Error::SaIn6PortMissing)?.to_be();

return Ok(SockAddr::new_v6_from_be(addr, port));
Ok(SockAddr::new_v6_from_be(addr, port))
}
_ => Err(Error::UnsupportedSaFamily),
}

return Err(Error::UnsupportedSaFamily);
}

#[inline(always)]
pub unsafe fn dst_from_sock_common(sk: sock_common) -> Result<Self, Error> {
let sa_family = sk.skc_family().ok_or(Error::SkcFamilyMissing)?;
let sa_family = sk.skc_family().ok_or(Error::SkcFamilyMissing)? as u32;
let dport = sk.skc_dport().ok_or(Error::SkcPortPairMissing)?.to_be();

if sa_family == AF_INET as u16 {
return Ok(SockAddr::new_v4_from_be(
match sa_family {
AF_INET => Ok(SockAddr::new_v4_from_be(
sk.skc_daddr().ok_or(Error::SkcAddrPairMissing)?.to_be(),
dport,
));
} else if sa_family == AF_INET6 as u16 {
return Ok(SockAddr::new_v6_from_be(
)),
AF_INET6 => Ok(SockAddr::new_v6_from_be(
sk.skc_v6_daddr()
.and_then(|in6| in6.addr32())
.ok_or(Error::SkcV6daddrMissing)?,
dport,
));
)),
_ => Err(Error::UnsupportedSaFamily),
}

return Err(Error::UnsupportedSaFamily);
}

#[inline(always)]
pub unsafe fn src_from_sock_common(sk: sock_common) -> Result<Self, Error> {
let sa_family = sk.skc_family().ok_or(Error::SkcFamilyMissing)?;
let sport = sk
.skc_num()
.map(u16::to_be)
.ok_or(Error::SkcPortPairMissing)?;

if sa_family == AF_INET as u16 {
return Ok(SockAddr::new_v4_from_be(
sk.skc_rcv_saddr()
.map(u32::to_be)
.ok_or(Error::SkcAddrPairMissing)?,
sport,
));
} else if sa_family == AF_INET6 as u16 {
return Ok(SockAddr::new_v6_from_be(
sk.skc_v6_rcv_saddr()
.and_then(|in6| in6.addr32())
.ok_or(Error::SkcV6daddrMissing)?,
sport,
));
let sa_family = sk.skc_family().ok_or(Error::SkcFamilyMissing)? as u32;
// skc_num is already in the good endianess
// https://elixir.bootlin.com/linux/v6.12.6/source/include/net/sock.h#L167
let sport = sk.skc_num().ok_or(Error::SkcPortPairMissing)?;

match sa_family {
AF_INET => {
return Ok(SockAddr::new_v4_from_be(
sk.skc_rcv_saddr()
.map(u32::to_be)
.ok_or(Error::SkcAddrPairMissing)?,
sport,
));
}
AF_INET6 => {
return Ok(SockAddr::new_v6_from_be(
sk.skc_v6_rcv_saddr()
.and_then(|in6| in6.addr32())
.ok_or(Error::SkcV6daddrMissing)?,
sport,
));
}
_ => Err(Error::UnsupportedSaFamily),
}

return Err(Error::UnsupportedSaFamily);
}
}

Expand Down
34 changes: 21 additions & 13 deletions kunai-ebpf/src/probes/dns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use aya_ebpf::{
programs::{ProbeContext, RetProbeContext},
};

use co_re::sockaddr;
use kunai_common::{
co_re::task_struct,
kprobe::ProbeFn,
Expand Down Expand Up @@ -83,7 +84,8 @@ impl SockHelper {
}
}

unsafe fn is_dns_sock(sock: &co_re::sock) -> Result<bool, ProbeError> {
#[inline(always)]
unsafe fn is_dns_dst(sock: &co_re::sock) -> Result<bool, ProbeError> {
let si = SocketInfo::try_from(*sock)?;

// return if socket neither is INET nor INET6
Expand All @@ -92,10 +94,10 @@ unsafe fn is_dns_sock(sock: &co_re::sock) -> Result<bool, ProbeError> {
}

let sock_common = core_read_kernel!(sock, sk_common)?;
let ip_port = SockAddr::dst_from_sock_common(sock_common)?;
let dst = SockAddr::dst_from_sock_common(sock_common)?;

// filter on dst port
Ok(ip_port.port() == 53)
Ok(dst.port() == 53)
}

#[kprobe(function = "vfs_read")]
Expand Down Expand Up @@ -128,7 +130,7 @@ unsafe fn try_enter_vfs_read(ctx: &ProbeContext) -> ProbeResult<()> {
let sock = core_read_kernel!(socket, sk)?;

// check if it looks like a connection to a DNS server
if !is_dns_sock(&sock)? {
if !is_dns_dst(&sock)? {
return Ok(());
}

Expand Down Expand Up @@ -211,6 +213,7 @@ pub fn net_dns_enter_sys_recvfrom(ctx: ProbeContext) -> u32 {
unsafe fn try_enter_sys_recvfrom(ctx: &ProbeContext) -> ProbeResult<()> {
let fd: c_int = kprobe_arg!(ctx, 0)?;
let ubuf: *const u8 = kprobe_arg!(ctx, 1)?;
let from_addr = sockaddr::from_ptr(kprobe_arg!(ctx, 4)?);

let current = task_struct::current();
let file = current
Expand All @@ -228,7 +231,10 @@ unsafe fn try_enter_sys_recvfrom(ctx: &ProbeContext) -> ProbeResult<()> {
let socket = co_re::socket::from_ptr(core_read_kernel!(file, private_data)? as *const _);
let sock = core_read_kernel!(socket, sk)?;

if !is_dns_sock(&sock)? {
// if from_addr isn't null it means we expect the kernel to fill
// the remote address during the call. It is possible that socket
// does not contain information about the destination address
if from_addr.is_null() && !is_dns_dst(&sock)? {
return Ok(());
}

Expand Down Expand Up @@ -271,7 +277,13 @@ unsafe fn try_exit_sys_recvfrom(exit_ctx: &RetProbeContext) -> ProbeResult<()> {

let fd: c_int = kprobe_arg!(ent_probe_ctx, 0)?;
let ubuf: *const u8 = kprobe_arg!(ent_probe_ctx, 1)?;
let from_addr = sockaddr::from_ptr(kprobe_arg!(ent_probe_ctx, 4)?);

let mut opt_server = None;

if let Ok(from) = SockAddr::from_sockaddr_user(from_addr) {
opt_server.replace(from);
}
let file = task_struct::current()
.get_fd(fd as usize)
.ok_or(ProbeError::FileNotFound)?;
Expand All @@ -290,7 +302,7 @@ unsafe fn try_exit_sys_recvfrom(exit_ctx: &RetProbeContext) -> ProbeResult<()> {
rc as usize,
);

sh.dns_event(exit_ctx, None, false)?;
sh.dns_event(exit_ctx, opt_server, false)?;

Ok(())
}
Expand Down Expand Up @@ -332,7 +344,7 @@ unsafe fn try_enter_sys_recvmsg(ctx: &ProbeContext) -> ProbeResult<()> {
let socket = co_re::socket::from_ptr(core_read_kernel!(file, private_data)? as *const _);
let sock = core_read_kernel!(socket, sk)?;

if !is_dns_sock(&sock)? {
if !is_dns_dst(&sock)? {
return Ok(());
}

Expand Down Expand Up @@ -403,12 +415,8 @@ unsafe fn try_exit_recvmsg(exit_ctx: &RetProbeContext) -> ProbeResult<()> {

if !msg_name.is_null() {
let addr = co_re::sockaddr::from_ptr(msg_name as *const _);
let sa_family = core_read_user!(addr, sa_family)?;
if sa_family == AF_INET {
let in_addr: co_re::sockaddr_in = addr.into();
let ip = core_read_user!(in_addr, s_addr)?.to_be();
let port = core_read_user!(in_addr, sin_port)?.to_be();
server = Some(SockAddr::new_v4_from_be(ip, port));
if let Ok(sa) = SockAddr::from_sockaddr(addr) {
server = Some(sa)
}
}

Expand Down

0 comments on commit c88352c

Please sign in to comment.