diff --git a/net-guardia-ebpf/src/action/blocking.rs b/net-guardia-ebpf/src/action/blocking.rs index 618307d..d190e84 100644 --- a/net-guardia-ebpf/src/action/blocking.rs +++ b/net-guardia-ebpf/src/action/blocking.rs @@ -5,30 +5,46 @@ use net_guardia_common::model::ip_address::{IPv4, IPv6, Port}; use net_guardia_common::{MAX_RULES, MAX_RULES_PORT}; #[map] -static IPV4_BLACKLIST: HashMap = +static IPV4_SRC_BLACKLIST: HashMap = HashMap::with_max_entries(MAX_RULES, 0); #[map] -static IPV6_BLACKLIST: HashMap = +static IPV6_SRC_BLACKLIST: HashMap = + HashMap::with_max_entries(MAX_RULES, 0); +#[map] +static IPV4_DST_BLACKLIST: HashMap = + HashMap::with_max_entries(MAX_RULES, 0); +#[map] +static IPV6_DST_BLACKLIST: HashMap = HashMap::with_max_entries(MAX_RULES, 0); pub fn ipv4_should_block(event: &IPv4Event) -> bool { unsafe { - if let Some(ports) = IPV4_BLACKLIST.get(&event.source_ip) { + if let Some(ports) = IPV4_SRC_BLACKLIST.get(&event.source_ip) { if is_port_blocked(ports, event.source_port) { return true; } } + if let Some(ports) = IPV4_DST_BLACKLIST.get(&event.destination_ip) { + if is_port_blocked(ports, event.destination_port) { + return true; + } + } } false } pub fn ipv6_should_block(event: &IPv6Event) -> bool { unsafe { - if let Some(ports) = IPV6_BLACKLIST.get(&event.source_ip) { + if let Some(ports) = IPV6_SRC_BLACKLIST.get(&event.source_ip) { if is_port_blocked(ports, event.source_port) { return true; } } + if let Some(ports) = IPV6_DST_BLACKLIST.get(&event.destination_ip) { + if is_port_blocked(ports, event.destination_port) { + return true; + } + } } false } diff --git a/net-guardia-ebpf/src/main.rs b/net-guardia-ebpf/src/main.rs index 3219fc6..d403f97 100644 --- a/net-guardia-ebpf/src/main.rs +++ b/net-guardia-ebpf/src/main.rs @@ -32,7 +32,7 @@ fn try_net_guardia(ctx: XdpContext) -> Result { if service::ipv4_service_rule_violation(start, end, &event) { return Ok(xdp_action::XDP_DROP); } - // if defence::is_attack_ipv4(&event) { + // if defence::ipv4_is_attack(&event) { // return Ok(xdp_action::XDP_DROP); // } monitor::ipv4_update_stats(&event); @@ -45,7 +45,7 @@ fn try_net_guardia(ctx: XdpContext) -> Result { if service::ipv6_service_rule_violation(start, end, &event) { return Ok(xdp_action::XDP_DROP); } - // if defence::is_attack_ipv6(&event) { + // if defence::ipv6_is_attack(&event) { // return Ok(xdp_action::XDP_DROP); // } monitor::ipv6_update_stats(&event); diff --git a/net-guardia/src/core/control.rs b/net-guardia/src/core/control.rs index aa98fb4..5e88354 100644 --- a/net-guardia/src/core/control.rs +++ b/net-guardia/src/core/control.rs @@ -17,8 +17,10 @@ use tracing::{error, info}; static CONTROL: OnceLock> = OnceLock::new(); pub struct Control { - ipv4_black_list: AyaHashMap, - ipv6_black_list: AyaHashMap, + ipv4_src_black_list: AyaHashMap, + ipv6_src_black_list: AyaHashMap, + ipv4_dst_black_list: AyaHashMap, + ipv6_dst_black_list: AyaHashMap, ipv4_http_service: AyaHashMap, ipv6_http_service: AyaHashMap, ssh_white_list_only: AyaArray, @@ -38,8 +40,10 @@ impl Control { let mut system = System::instance_mut().await; let ebpf = &mut system.ebpf; let mut control = Control { - ipv4_black_list: AyaHashMap::try_from(ebpf.take_map("IPV4_BLACKLIST").unwrap())?, - ipv6_black_list: AyaHashMap::try_from(ebpf.take_map("IPV6_BLACKLIST").unwrap())?, + ipv4_src_black_list: AyaHashMap::try_from(ebpf.take_map("IPV4_SRC_BLACKLIST").unwrap())?, + ipv6_src_black_list: AyaHashMap::try_from(ebpf.take_map("IPV6_SRC_BLACKLIST").unwrap())?, + ipv4_dst_black_list: AyaHashMap::try_from(ebpf.take_map("IPV4_DST_BLACKLIST").unwrap())?, + ipv6_dst_black_list: AyaHashMap::try_from(ebpf.take_map("IPV6_DST_BLACKLIST").unwrap())?, ipv4_http_service: AyaHashMap::try_from(ebpf.take_map("IPV4_HTTP_SERVICE").unwrap())?, ipv6_http_service: AyaHashMap::try_from(ebpf.take_map("IPV6_HTTP_SERVICE").unwrap())?, ssh_white_list_only: AyaArray::try_from(ebpf.take_map("SSH_WHITE_LIST_ONLY").unwrap())?, @@ -84,34 +88,34 @@ impl Control { once_lock.write().await } - pub async fn get_ipv4_black_list() -> StdHashMap> { + pub async fn get_ipv4_src_black_list() -> StdHashMap> { let control = Control::instance().await; control - .ipv4_black_list + .ipv4_src_black_list .iter() .filter_map(Result::ok) .map(|(key, value)| (Ipv4Addr::from(key), convert_ports_to_vec(value))) .collect() } - pub async fn get_ipv6_black_list() -> StdHashMap> { + pub async fn get_ipv6_src_black_list() -> StdHashMap> { let control = Control::instance().await; control - .ipv6_black_list + .ipv6_src_black_list .iter() .filter_map(Result::ok) .map(|(key, value)| (Ipv6Addr::from(key), convert_ports_to_vec(value))) .collect() } - pub async fn add_ipv4_black_list(address: SocketAddrV4) -> anyhow::Result<()> { + pub async fn add_ipv4_src_black_list(address: SocketAddrV4) -> anyhow::Result<()> { let ip: u32 = (*address.ip()).into(); let port = address.port(); let mut control = Control::instance_mut().await; let mut new_ports = [0_u16; MAX_RULES_PORT]; if port == 0 { new_ports[0] = 0; - } else if let Ok(ports) = control.ipv4_black_list.get(&ip, 0) { + } else if let Ok(ports) = control.ipv4_src_black_list.get(&ip, 0) { if ports[0] == 0 { return Ok(()); } @@ -133,20 +137,20 @@ impl Control { new_ports[0] = port; } control - .ipv4_black_list + .ipv4_src_black_list .insert(ip, new_ports, 0) .map_err(|_| EbpfEntry::MapOperationError)?; Ok(()) } - pub async fn add_ipv6_black_list(address: SocketAddrV6) -> anyhow::Result<()> { + pub async fn add_ipv6_src_black_list(address: SocketAddrV6) -> anyhow::Result<()> { let ip: u128 = (*address.ip()).into(); let port = address.port(); let mut control = Control::instance_mut().await; let mut new_ports = [0_u16; MAX_RULES_PORT]; if port == 0 { new_ports[0] = 0; - } else if let Ok(ports) = control.ipv6_black_list.get(&ip, 0) { + } else if let Ok(ports) = control.ipv6_src_black_list.get(&ip, 0) { if ports[0] == 0 { return Ok(()); } @@ -168,20 +172,20 @@ impl Control { new_ports[0] = port; } control - .ipv6_black_list + .ipv6_src_black_list .insert(ip, new_ports, 0) .map_err(|_| EbpfEntry::MapOperationError)?; Ok(()) } - pub async fn remove_ipv4_black_list(address: SocketAddrV4) -> anyhow::Result<()> { + pub async fn remove_ipv4_src_black_list(address: SocketAddrV4) -> anyhow::Result<()> { let ip: u32 = (*address.ip()).into(); let port = address.port(); let mut control = Control::instance_mut().await; - if let Ok(mut ports) = control.ipv4_black_list.get(&ip, 0) { + if let Ok(mut ports) = control.ipv4_src_black_list.get(&ip, 0) { if port == 0 { control - .ipv4_black_list + .ipv4_src_black_list .remove(&ip) .map_err(|_| EbpfEntry::MapOperationError)?; return Ok(()); @@ -193,12 +197,12 @@ impl Control { ports[MAX_RULES_PORT - 1] = 0; if ports[0] == 0 { control - .ipv4_black_list + .ipv4_src_black_list .remove(&ip) .map_err(|_| EbpfEntry::MapOperationError)?; } else { control - .ipv4_black_list + .ipv4_src_black_list .insert(ip, ports, 0) .map_err(|_| EbpfEntry::MapOperationError)?; } @@ -209,14 +213,14 @@ impl Control { } } - pub async fn remove_ipv6_black_list(address: SocketAddrV6) -> anyhow::Result<()> { + pub async fn remove_ipv6_src_black_list(address: SocketAddrV6) -> anyhow::Result<()> { let ip: u128 = (*address.ip()).into(); let port = address.port(); let mut control = Control::instance_mut().await; - if let Ok(mut ports) = control.ipv6_black_list.get(&ip, 0) { + if let Ok(mut ports) = control.ipv6_src_black_list.get(&ip, 0) { if port == 0 { control - .ipv6_black_list + .ipv6_src_black_list .remove(&ip) .map_err(|_| EbpfEntry::MapOperationError)?; return Ok(()); @@ -228,12 +232,172 @@ impl Control { ports[MAX_RULES_PORT - 1] = 0; if ports[0] == 0 { control - .ipv6_black_list + .ipv6_src_black_list .remove(&ip) .map_err(|_| EbpfEntry::MapOperationError)?; } else { control - .ipv6_black_list + .ipv6_src_black_list + .insert(ip, ports, 0) + .map_err(|_| EbpfEntry::MapOperationError)?; + } + } + Ok(()) + } else { + Err(EbpfEntry::IpDoesNotExist)? + } + } + + pub async fn get_ipv4_dst_black_list() -> StdHashMap> { + let control = Control::instance().await; + control + .ipv4_dst_black_list + .iter() + .filter_map(Result::ok) + .map(|(key, value)| (Ipv4Addr::from(key), convert_ports_to_vec(value))) + .collect() + } + + pub async fn get_ipv6_dst_black_list() -> StdHashMap> { + let control = Control::instance().await; + control + .ipv6_dst_black_list + .iter() + .filter_map(Result::ok) + .map(|(key, value)| (Ipv6Addr::from(key), convert_ports_to_vec(value))) + .collect() + } + + pub async fn add_ipv4_dst_black_list(address: SocketAddrV4) -> anyhow::Result<()> { + let ip: u32 = (*address.ip()).into(); + let port = address.port(); + let mut control = Control::instance_mut().await; + let mut new_ports = [0_u16; MAX_RULES_PORT]; + if port == 0 { + new_ports[0] = 0; + } else if let Ok(ports) = control.ipv4_dst_black_list.get(&ip, 0) { + if ports[0] == 0 { + return Ok(()); + } + let mut index = None; + for (i, &value) in ports.iter().enumerate() { + if value == port { + return Ok(()); + } + if index.is_none() && value == 0 { + index = Some(i); + } + } + if index.is_none() { + return Err(EbpfEntry::RuleReachLimit.into()); + } + new_ports.copy_from_slice(&ports); + new_ports[index.unwrap()] = port; + } else { + new_ports[0] = port; + } + control + .ipv4_dst_black_list + .insert(ip, new_ports, 0) + .map_err(|_| EbpfEntry::MapOperationError)?; + Ok(()) + } + + pub async fn add_ipv6_dst_black_list(address: SocketAddrV6) -> anyhow::Result<()> { + let ip: u128 = (*address.ip()).into(); + let port = address.port(); + let mut control = Control::instance_mut().await; + let mut new_ports = [0_u16; MAX_RULES_PORT]; + if port == 0 { + new_ports[0] = 0; + } else if let Ok(ports) = control.ipv6_dst_black_list.get(&ip, 0) { + if ports[0] == 0 { + return Ok(()); + } + let mut index = None; + for (i, &value) in ports.iter().enumerate() { + if value == port { + return Ok(()); + } + if index.is_none() && value == 0 { + index = Some(i); + } + } + if index.is_none() { + return Err(EbpfEntry::RuleReachLimit.into()); + } + new_ports.copy_from_slice(&ports); + new_ports[index.unwrap()] = port; + } else { + new_ports[0] = port; + } + control + .ipv6_dst_black_list + .insert(ip, new_ports, 0) + .map_err(|_| EbpfEntry::MapOperationError)?; + Ok(()) + } + + pub async fn remove_ipv4_dst_black_list(address: SocketAddrV4) -> anyhow::Result<()> { + let ip: u32 = (*address.ip()).into(); + let port = address.port(); + let mut control = Control::instance_mut().await; + if let Ok(mut ports) = control.ipv4_dst_black_list.get(&ip, 0) { + if port == 0 { + control + .ipv4_dst_black_list + .remove(&ip) + .map_err(|_| EbpfEntry::MapOperationError)?; + return Ok(()); + } + if let Some(index) = ports.iter().position(|&x| x == port) { + for i in index..(MAX_RULES_PORT - 1) { + ports[i] = ports[i + 1]; + } + ports[MAX_RULES_PORT - 1] = 0; + if ports[0] == 0 { + control + .ipv4_dst_black_list + .remove(&ip) + .map_err(|_| EbpfEntry::MapOperationError)?; + } else { + control + .ipv4_dst_black_list + .insert(ip, ports, 0) + .map_err(|_| EbpfEntry::MapOperationError)?; + } + } + Ok(()) + } else { + Err(EbpfEntry::IpDoesNotExist)? + } + } + + pub async fn remove_ipv6_dst_black_list(address: SocketAddrV6) -> anyhow::Result<()> { + let ip: u128 = (*address.ip()).into(); + let port = address.port(); + let mut control = Control::instance_mut().await; + if let Ok(mut ports) = control.ipv6_dst_black_list.get(&ip, 0) { + if port == 0 { + control + .ipv6_dst_black_list + .remove(&ip) + .map_err(|_| EbpfEntry::MapOperationError)?; + return Ok(()); + } + if let Some(index) = ports.iter().position(|&x| x == port) { + for i in index..(MAX_RULES_PORT - 1) { + ports[i] = ports[i + 1]; + } + ports[MAX_RULES_PORT - 1] = 0; + if ports[0] == 0 { + control + .ipv6_dst_black_list + .remove(&ip) + .map_err(|_| EbpfEntry::MapOperationError)?; + } else { + control + .ipv6_dst_black_list .insert(ip, ports, 0) .map_err(|_| EbpfEntry::MapOperationError)?; } diff --git a/net-guardia/src/web/api/control.rs b/net-guardia/src/web/api/control.rs index 4c2e9ef..491388f 100644 --- a/net-guardia/src/web/api/control.rs +++ b/net-guardia/src/web/api/control.rs @@ -5,12 +5,18 @@ use std::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6}; pub fn initialize() -> Scope { web::scope("/control") - .service(get_ipv4_black_list) - .service(get_ipv6_black_list) - .service(add_ipv4_black_list) - .service(add_ipv6_black_list) - .service(remove_ipv4_black_list) - .service(remove_ipv6_black_list) + .service(get_ipv4_src_black_list) + .service(get_ipv6_src_black_list) + .service(add_ipv4_src_black_list) + .service(add_ipv6_src_black_list) + .service(remove_ipv4_src_black_list) + .service(remove_ipv6_src_black_list) + .service(get_ipv4_dst_black_list) + .service(get_ipv6_dst_black_list) + .service(add_ipv4_dst_black_list) + .service(add_ipv6_dst_black_list) + .service(remove_ipv4_dst_black_list) + .service(remove_ipv6_dst_black_list) .service(get_ipv4_http_service) .service(get_ipv6_http_service) .service(add_ipv4_http_service) @@ -44,45 +50,89 @@ pub fn initialize() -> Scope { .service(remove_ipv6_scanner_list) } -#[get("/ipv4/black_list")] -async fn get_ipv4_black_list() -> impl Responder { - let list = Control::get_ipv4_black_list().await; +#[get("/ipv4/src_black_list")] +async fn get_ipv4_src_black_list() -> impl Responder { + let list = Control::get_ipv4_src_black_list().await; HttpResponse::Ok().json(web::Json(list)) } -#[get("/ipv6/black_list")] -async fn get_ipv6_black_list() -> impl Responder { - let list = Control::get_ipv6_black_list().await; +#[get("/ipv6/src_black_list")] +async fn get_ipv6_src_black_list() -> impl Responder { + let list = Control::get_ipv6_src_black_list().await; HttpResponse::Ok().json(web::Json(list)) } -#[put("/ipv4/black_list")] -async fn add_ipv4_black_list(ip_addr: web::Json) -> impl Responder { - match Control::add_ipv4_black_list(ip_addr.into_inner()).await { +#[put("/ipv4/src_black_list")] +async fn add_ipv4_src_black_list(ip_addr: web::Json) -> impl Responder { + match Control::add_ipv4_src_black_list(ip_addr.into_inner()).await { Ok(_) => HttpResponse::Ok().finish(), Err(e) => HttpResponse::InternalServerError().body(e.to_string()), } } -#[put("/ipv6/black_list")] -async fn add_ipv6_black_list(ip_addr: web::Json) -> impl Responder { - match Control::add_ipv6_black_list(ip_addr.into_inner()).await { +#[put("/ipv6/src_black_list")] +async fn add_ipv6_src_black_list(ip_addr: web::Json) -> impl Responder { + match Control::add_ipv6_src_black_list(ip_addr.into_inner()).await { Ok(_) => HttpResponse::Ok().finish(), Err(e) => HttpResponse::InternalServerError().body(e.to_string()), } } -#[delete("/ipv4/black_list")] -async fn remove_ipv4_black_list(ip_addr: web::Json) -> impl Responder { - match Control::remove_ipv4_black_list(ip_addr.into_inner()).await { +#[delete("/ipv4/src_black_list")] +async fn remove_ipv4_src_black_list(ip_addr: web::Json) -> impl Responder { + match Control::remove_ipv4_src_black_list(ip_addr.into_inner()).await { Ok(_) => HttpResponse::Ok().finish(), Err(e) => HttpResponse::InternalServerError().body(e.to_string()), } } -#[delete("/ipv6/black_list")] -async fn remove_ipv6_black_list(ip_addr: web::Json) -> impl Responder { - match Control::remove_ipv6_black_list(ip_addr.into_inner()).await { +#[delete("/ipv6/src_black_list")] +async fn remove_ipv6_src_black_list(ip_addr: web::Json) -> impl Responder { + match Control::remove_ipv6_src_black_list(ip_addr.into_inner()).await { + Ok(_) => HttpResponse::Ok().finish(), + Err(e) => HttpResponse::InternalServerError().body(e.to_string()), + } +} + +#[get("/ipv4/dst_black_list")] +async fn get_ipv4_dst_black_list() -> impl Responder { + let list = Control::get_ipv4_dst_black_list().await; + HttpResponse::Ok().json(web::Json(list)) +} + +#[get("/ipv6/dst_black_list")] +async fn get_ipv6_dst_black_list() -> impl Responder { + let list = Control::get_ipv6_dst_black_list().await; + HttpResponse::Ok().json(web::Json(list)) +} + +#[put("/ipv4/dst_black_list")] +async fn add_ipv4_dst_black_list(ip_addr: web::Json) -> impl Responder { + match Control::add_ipv4_dst_black_list(ip_addr.into_inner()).await { + Ok(_) => HttpResponse::Ok().finish(), + Err(e) => HttpResponse::InternalServerError().body(e.to_string()), + } +} + +#[put("/ipv6/dst_black_list")] +async fn add_ipv6_dst_black_list(ip_addr: web::Json) -> impl Responder { + match Control::add_ipv6_dst_black_list(ip_addr.into_inner()).await { + Ok(_) => HttpResponse::Ok().finish(), + Err(e) => HttpResponse::InternalServerError().body(e.to_string()), + } +} + +#[delete("/ipv4/dst_black_list")] +async fn remove_ipv4_dst_black_list(ip_addr: web::Json) -> impl Responder { + match Control::remove_ipv4_dst_black_list(ip_addr.into_inner()).await { + Ok(_) => HttpResponse::Ok().finish(), + Err(e) => HttpResponse::InternalServerError().body(e.to_string()), + } +} + +#[delete("/ipv6/dst_black_list")] +async fn remove_ipv6_dst_black_list(ip_addr: web::Json) -> impl Responder { + match Control::remove_ipv6_dst_black_list(ip_addr.into_inner()).await { Ok(_) => HttpResponse::Ok().finish(), Err(e) => HttpResponse::InternalServerError().body(e.to_string()), }