diff --git a/kclvm/runtime/src/_kclvm.h b/kclvm/runtime/src/_kclvm.h index cff0e8c81..ad154bfcb 100644 --- a/kclvm/runtime/src/_kclvm.h +++ b/kclvm/runtime/src/_kclvm.h @@ -423,6 +423,14 @@ kclvm_value_ref_t* kclvm_net_is_IPv4(kclvm_context_t* ctx, kclvm_value_ref_t* ar kclvm_value_ref_t* kclvm_net_is_global_unicast_IP(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); +kclvm_value_ref_t* kclvm_net_parse_CIDR (kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_net_hosts_in_CIDR (kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_net_subnets_from_CIDR (kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + +kclvm_value_ref_t* kclvm_net_is_IP_in_CIDR (kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); + kclvm_value_ref_t* kclvm_net_is_interface_local_multicast_IP(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); kclvm_value_ref_t* kclvm_net_is_link_local_multicast_IP(kclvm_context_t* ctx, kclvm_value_ref_t* args, kclvm_value_ref_t* _kwargs); diff --git a/kclvm/runtime/src/_kclvm.rs b/kclvm/runtime/src/_kclvm.rs index 0e45fb162..6a900a4f2 100644 --- a/kclvm/runtime/src/_kclvm.rs +++ b/kclvm/runtime/src/_kclvm.rs @@ -204,6 +204,10 @@ pub enum ApiFunc { kclvm_net_is_IP, kclvm_net_is_IPv4, kclvm_net_is_global_unicast_IP, + kclvm_net_parse_CIDR, + kclvm_net_hosts_in_CIDR, + kclvm_net_subnets_from_CIDR, + kclvm_net_is_IP_in_CIDR, kclvm_net_is_interface_local_multicast_IP, kclvm_net_is_link_local_multicast_IP, kclvm_net_is_link_local_unicast_IP, diff --git a/kclvm/runtime/src/_kclvm_addr.rs b/kclvm/runtime/src/_kclvm_addr.rs index 25012b473..4d0a84abf 100644 --- a/kclvm/runtime/src/_kclvm_addr.rs +++ b/kclvm/runtime/src/_kclvm_addr.rs @@ -223,6 +223,10 @@ pub fn _kclvm_get_fn_ptr_by_name(name: &str) -> u64 { "kclvm_net_is_loopback_IP" => crate::kclvm_net_is_loopback_IP as *const () as u64, "kclvm_net_is_multicast_IP" => crate::kclvm_net_is_multicast_IP as *const () as u64, "kclvm_net_is_unspecified_IP" => crate::kclvm_net_is_unspecified_IP as *const () as u64, + "kclvm_net_parse_CIDR" => crate::kclvm_net_parse_CIDR as *const () as u64, + "kclvm_net_hosts_in_CIDR" => crate::kclvm_net_hosts_in_CIDR as *const () as u64, + "kclvm_net_subnets_from_CIDR" => crate::kclvm_net_subnets_from_CIDR as *const () as u64, + "kclvm_net_is_IP_in_CIDR" => crate::kclvm_net_is_IP_in_CIDR as *const () as u64, "kclvm_net_join_host_port" => crate::kclvm_net_join_host_port as *const () as u64, "kclvm_net_parse_IP" => crate::kclvm_net_parse_IP as *const () as u64, "kclvm_net_split_host_port" => crate::kclvm_net_split_host_port as *const () as u64, diff --git a/kclvm/runtime/src/net/mod.rs b/kclvm/runtime/src/net/mod.rs index 5446d1bab..168fbec97 100644 --- a/kclvm/runtime/src/net/mod.rs +++ b/kclvm/runtime/src/net/mod.rs @@ -396,6 +396,141 @@ pub extern "C" fn kclvm_net_is_global_unicast_IP( panic!("is_global_unicast_IP() missing 1 required positional argument: 'ip'"); } +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_net_parse_CIDR( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + let kwargs = ptr_as_ref(kwargs); + let ctx = mut_ptr_as_ref(ctx); + + if let Some(cidr) = get_call_arg_str(args, kwargs, 0, Some("cidr")) { + let parts: Vec<&str> = cidr.split('/').collect(); + if parts.len() == 2 { + let ip = parts[0]; + let mask = parts[1]; + if let Ok(ip) = Ipv4Addr::from_str(ip) { + if let Ok(mask) = mask.parse::() { + let ip_value = ValueRef::str(ip.to_string().as_str()); + let mask_value = ValueRef::int(mask as i64); + return ValueRef::dict(Some(&[("ip", &ip_value), ("mask", &mask_value)])) + .into_raw(ctx); + } + } + } + return ValueRef::dict(None).into_raw(ctx); + } + + panic!("parse_CIDR() missing 1 required positional argument: 'cidr'"); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_net_hosts_in_CIDR( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + let kwargs = ptr_as_ref(kwargs); + let ctx = mut_ptr_as_ref(ctx); + + if let Some(cidr) = get_call_arg_str(args, kwargs, 0, Some("cidr")) { + let parts: Vec<&str> = cidr.split('/').collect(); + if parts.len() == 2 { + let ip = parts[0]; + let mask = parts[1]; + if let Ok(ip) = Ipv4Addr::from_str(ip) { + if let Ok(mask) = mask.parse::() { + let mask = u32::from_be_bytes(ip.octets()) & !((1 << (32 - mask)) - 1); + let mut hosts = vec![]; + for i in 1..(1 << (32 - mask)) - 1 { + let ip = u32::from_be_bytes(ip.octets()) + i; + hosts.push(ValueRef::str(Ipv4Addr::from(ip).to_string().as_str())); + } + let hosts_refs: Vec<&ValueRef> = hosts.iter().collect(); + return ValueRef::list(Some(&hosts_refs[..])).into_raw(ctx); + } + } + } + return ValueRef::list(None).into_raw(ctx); + } + + panic!("hosts_in_CIDR() missing 1 required positional argument: 'cidr'"); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_net_subnets_from_CIDR( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + let kwargs = ptr_as_ref(kwargs); + let ctx = mut_ptr_as_ref(ctx); + + if let Some(cidr) = get_call_arg_str(args, kwargs, 0, Some("cidr")) { + let parts: Vec<&str> = cidr.split('/').collect(); + if parts.len() == 2 { + let ip = parts[0]; + let mask = parts[1]; + if let Ok(ip) = Ipv4Addr::from_str(ip) { + if let Ok(mask) = mask.parse::() { + let mask = u32::from_be_bytes(ip.octets()) & !((1 << (32 - mask)) - 1); + let mut subnets = vec![]; + for i in 1..(1 << (32 - mask)) - 1 { + let ip = u32::from_be_bytes(ip.octets()) + i; + subnets.push(ValueRef::str( + format!("{}/{}", Ipv4Addr::from(ip), mask).as_str(), + )); + } + let subnets_refs: Vec<&ValueRef> = subnets.iter().collect(); + return ValueRef::list(Some(&subnets_refs)).into_raw(ctx); + } + } + } + return ValueRef::list(None).into_raw(ctx); + } + + panic!("subnets_from_CIDR() missing 1 required positional argument: 'cidr'"); +} + +#[no_mangle] +#[runtime_fn] +pub extern "C" fn kclvm_net_is_IP_in_CIDR( + ctx: *mut kclvm_context_t, + args: *const kclvm_value_ref_t, + kwargs: *const kclvm_value_ref_t, +) -> *const kclvm_value_ref_t { + let args = ptr_as_ref(args); + let kwargs = ptr_as_ref(kwargs); + + if let Some(ip) = get_call_arg_str(args, kwargs, 0, Some("ip")) { + if let Some(cidr) = get_call_arg_str(args, kwargs, 1, Some("cidr")) { + let parts: Vec<&str> = cidr.split('/').collect(); + if parts.len() == 2 { + let ip = parts[0]; + let mask = parts[1]; + if let Ok(ip) = Ipv4Addr::from_str(ip) { + if let Ok(mask) = mask.parse::() { + let mask = u32::from_be_bytes(ip.octets()) & !((1 << (32 - mask)) - 1); + let ip = u32::from_be_bytes(ip.octets()); + let x = (ip & mask) == mask; + return kclvm_value_Bool(ctx, x as i8); + } + } + } + } + return kclvm_value_False(ctx); + } + + panic!("is_IP_in_CIDR() missing 2 required positional arguments: 'ip' and 'cidr'"); +} + #[allow(non_camel_case_types, non_snake_case)] fn Ipv4Addr_is_global(_self: &std::net::Ipv4Addr) -> bool { // check if this address is 192.0.0.9 or 192.0.0.10. These addresses are the only two diff --git a/kclvm/sema/src/builtin/system_module.rs b/kclvm/sema/src/builtin/system_module.rs index af2e3bc62..ceda2b74a 100644 --- a/kclvm/sema/src/builtin/system_module.rs +++ b/kclvm/sema/src/builtin/system_module.rs @@ -345,6 +345,84 @@ register_net_member! { false, None, ) + parse_CIDR => Type::function( + None, + Type::dict_ref(Type::str_ref(), Type::str_ref()), + &[ + Parameter { + name: "cidr".to_string(), + ty: Type::str_ref(), + has_default: false, + default_value: None, + range: dummy_range(), + }, + ], + r#"Parse a CIDR block into network, prefix, minHost, and maxHost."#, + false, + None, + ) + hosts_in_CIDR => Type::function( + None, + Type::list_ref(Type::str_ref()), + &[ + Parameter { + name: "cidr".to_string(), + ty: Type::str_ref(), + has_default: false, + default_value: None, + range: dummy_range(), + }, + ], + r#"Generate a list of all IP addresses in a CIDR block."#, + false, + None, + ) + subnets_from_CIDR => Type::function( + None, + Type::list_ref(Type::str_ref()), + &[ + Parameter { + name: "cidr".to_string(), + ty: Type::str_ref(), + has_default: false, + default_value: None, + range: dummy_range(), + }, + Parameter { + name: "new_prefix".to_string(), + ty: Type::int_ref(), + has_default: false, + default_value: None, + range: dummy_range(), + }, + ], + r#"Split a CIDR block into smaller subnets with a given prefix."#, + false, + None, + ) + is_IP_in_CIDR => Type::function( + None, + Type::bool_ref(), + &[ + Parameter { + name: "ip".to_string(), + ty: Type::str_ref(), + has_default: false, + default_value: None, + range: dummy_range(), + }, + Parameter { + name: "cidr".to_string(), + ty: Type::str_ref(), + has_default: false, + default_value: None, + range: dummy_range(), + }, + ], + r#"Check if an IP address is within a given CIDR block."#, + false, + None, + ) } // ------------------------------ diff --git a/test/grammar/builtins/net/is_ip_2/main.k b/test/grammar/builtins/net/is_ip_2/main.k index d215848d3..bed34c0c5 100644 --- a/test/grammar/builtins/net/is_ip_2/main.k +++ b/test/grammar/builtins/net/is_ip_2/main.k @@ -6,3 +6,8 @@ isip2 = net.is_link_local_multicast_IP("224.0.0.0") isip3 = net.is_link_local_unicast_IP("fe80::2012:1") isip4 = net.is_global_unicast_IP("220.181.108.89") isip5 = net.is_unspecified_IP("0.0.0.0") +isip6 = net.parse_CIDR("192.168.1.0/24") +isip7 = net.hosts_in_CIDR("192.168.1.0/24") +isip8 = net.subnets_from_CIDR("192.168.1.0/24") +isip9 = net.is_IP_in_CIDR("192.168.1.1", "192.168.1.0/24") +