1use crate::common::{gen_value_in_range, parse_arg};
2use crate::error::cidr_prefix_length_out_of_bounds;
3use rand::{rng, Rng};
4use std::collections::HashMap;
5use std::net::{Ipv4Addr, Ipv6Addr};
6use tera::{to_value, Result, Value};
7
8pub fn random_ipv4(args: &HashMap<String, Value>) -> Result<Value> {
47 let start_opt: Option<u32> = parse_arg(args, "start")?.map(|start: Ipv4Addr| start.into());
48
49 let end_opt: Option<u32> = parse_arg(args, "end")?.map(|end: Ipv4Addr| end.into());
50
51 let random_ipv4: u32 = gen_value_in_range(start_opt, end_opt, u32::MIN, u32::MAX);
52 let random_ipv4: Ipv4Addr = random_ipv4.into();
53
54 let json_value: Value = to_value(random_ipv4)?;
55 Ok(json_value)
56}
57
58pub fn random_ipv6(args: &HashMap<String, Value>) -> Result<Value> {
98 let start_opt: Option<u128> =
99 parse_arg(args, "start")?.map(|start_ipv6: Ipv6Addr| start_ipv6.into());
100
101 let end_opt: Option<u128> = parse_arg(args, "end")?.map(|end_ipv6: Ipv6Addr| end_ipv6.into());
102
103 let random_ipv6: u128 = gen_value_in_range(start_opt, end_opt, u128::MIN, u128::MAX);
104 let random_ipv6: Ipv6Addr = random_ipv6.into();
105
106 let json_value: Value = to_value(random_ipv6)?;
107 Ok(json_value)
108}
109
110pub fn random_ipv4_cidr(args: &HashMap<String, Value>) -> Result<Value> {
185 let addr_start_opt: Option<u32> =
186 parse_arg(args, "addr_start")?.map(|addr_start: Ipv4Addr| addr_start.into());
187 let addr_end_opt: Option<u32> =
188 parse_arg(args, "addr_end")?.map(|addr_end: Ipv4Addr| addr_end.into());
189
190 let random_addr: u32 = gen_value_in_range(addr_start_opt, addr_end_opt, u32::MIN, u32::MAX);
191
192 let length_start: u32 =
193 parse_cidr_prefix_length_and_check_bounds(args, "length_start", 0u32, u32::BITS)?
194 .unwrap_or(0u32);
195 let length_end: u32 =
196 parse_cidr_prefix_length_and_check_bounds(args, "length_end", 0u32, u32::BITS)?
197 .unwrap_or(u32::BITS);
198
199 let random_prefix_length: u32 = rng().random_range(length_start..=length_end);
200 let bits_to_shift: u32 = u32::BITS - random_prefix_length;
201
202 let random_prefix: u32 = match bits_to_shift {
203 u32::BITS => 0u32,
204 bits_to_shift => random_addr >> bits_to_shift << bits_to_shift,
205 };
206 let random_prefix: Ipv4Addr = random_prefix.into();
207
208 let random_cidr: String = format!("{}/{}", random_prefix.to_string(), random_prefix_length);
209 let json_value: Value = to_value(random_cidr)?;
210 Ok(json_value)
211}
212
213pub fn random_ipv6_cidr(args: &HashMap<String, Value>) -> Result<Value> {
288 let addr_start_opt: Option<u128> =
289 parse_arg(args, "addr_start")?.map(|addr_start: Ipv6Addr| addr_start.into());
290 let addr_end_opt: Option<u128> =
291 parse_arg(args, "addr_end")?.map(|addr_end: Ipv6Addr| addr_end.into());
292
293 let random_addr: u128 = gen_value_in_range(addr_start_opt, addr_end_opt, u128::MIN, u128::MAX);
294
295 let length_start: u32 =
296 parse_cidr_prefix_length_and_check_bounds(args, "length_start", 0u32, u128::BITS)?
297 .unwrap_or(0u32);
298 let length_end: u32 =
299 parse_cidr_prefix_length_and_check_bounds(args, "length_end", 0u32, u128::BITS)?
300 .unwrap_or(u128::BITS);
301
302 let random_prefix_length: u32 = rng().random_range(length_start..=length_end);
303 let bits_to_shift: u32 = u128::BITS - random_prefix_length;
304
305 let random_prefix: u128 = match bits_to_shift {
306 u128::BITS => 0u128,
307 bits_to_shift => random_addr >> bits_to_shift << bits_to_shift,
308 };
309 let random_prefix: Ipv6Addr = random_prefix.into();
310
311 let random_cidr: String = format!("{}/{}", random_prefix.to_string(), random_prefix_length);
312 let json_value: Value = to_value(random_cidr)?;
313 Ok(json_value)
314}
315
316fn parse_cidr_prefix_length_and_check_bounds(
317 args: &HashMap<String, Value>,
318 parameter: &'static str,
319 start_bound: u32,
320 end_bound: u32,
321) -> tera::Result<Option<u32>> {
322 parse_arg(args, parameter)?
323 .map(|length: u32| {
324 if length < start_bound || length > end_bound {
325 Err(cidr_prefix_length_out_of_bounds(
326 length,
327 start_bound,
328 end_bound,
329 ))
330 } else {
331 Ok(length)
332 }
333 })
334 .transpose()
335}
336
337#[cfg(test)]
338mod tests {
339 use crate::common::tests::{test_tera_rand_function, test_tera_rand_function_returns_error};
340 use crate::net::*;
341 use tracing_test::traced_test;
342
343 #[test]
345 #[traced_test]
346 fn test_random_ipv4() {
347 test_tera_rand_function(
348 random_ipv4,
349 "random_ipv4",
350 r#"{ "some_field": "{{ random_ipv4() }}" }"#,
351 r#"\{ "some_field": "\d+\.\d+\.\d+\.\d+" }"#,
352 );
353 }
354
355 #[test]
356 #[traced_test]
357 fn test_random_ipv4_with_both_start_and_end() {
358 test_tera_rand_function(
359 random_ipv4,
360 "random_ipv4",
361 r#"{ "some_field": "{{ random_ipv4(start="127.0.0.1", end="127.0.0.3") }}" }"#,
362 r#"\{ "some_field": "(127\.0\.0\.1|127\.0\.0\.2|127\.0\.0\.3)" }"#,
363 );
364 }
365
366 #[test]
367 #[traced_test]
368 fn test_random_ipv4_near_max() {
369 test_tera_rand_function(
370 random_ipv4,
371 "random_ipv4",
372 r#"{ "some_field": "{{ random_ipv4(start="255.255.255.253", end="255.255.255.255") }}" }"#,
373 r#"\{ "some_field": "(255\.255\.255\.253|255\.255\.255\.254|255\.255\.255\.255)" }"#,
374 );
375 }
376
377 #[test]
378 #[traced_test]
379 fn test_random_ipv4_near_min() {
380 test_tera_rand_function(
381 random_ipv4,
382 "random_ipv4",
383 r#"{ "some_field": "{{ random_ipv4(start="0.0.0.0", end="0.0.0.2") }}" }"#,
384 r#"\{ "some_field": "(0\.0\.0\.0|0\.0\.0\.1|0\.0\.0\.2)" }"#,
385 );
386 }
387
388 #[test]
389 #[traced_test]
390 fn test_random_ipv4_with_start_only() {
391 test_tera_rand_function(
392 random_ipv4,
393 "random_ipv4",
394 r#"{ "some_field": "{{ random_ipv4(start="255.255.255.253") }}" }"#,
395 r#"\{ "some_field": "(255\.255\.255\.253|255\.255\.255\.254|255\.255\.255\.255)" }"#,
396 );
397 }
398
399 #[test]
400 #[traced_test]
401 fn test_random_ipv4_with_end_only() {
402 test_tera_rand_function(
403 random_ipv4,
404 "random_ipv4",
405 r#"{ "some_field": "{{ random_ipv4(end="0.0.0.2") }}" }"#,
406 r#"\{ "some_field": "(0\.0\.0\.0|0\.0\.0\.1|0\.0\.0\.2)" }"#,
407 );
408 }
409
410 #[test]
412 #[traced_test]
413 fn test_random_ipv6() {
414 test_tera_rand_function(
415 random_ipv6,
416 "random_ipv6",
417 r#"{ "some_field": "{{ random_ipv6() }}" }"#,
418 r#"\{ "some_field": "([\da-f]{0,4}:){1,7}[\da-f]{0,4}" }"#,
419 );
420 }
421
422 #[test]
423 #[traced_test]
424 fn test_random_ipv6_with_both_start_and_end() {
425 test_tera_rand_function(
426 random_ipv6,
427 "random_ipv6",
428 r#"{ "some_field": "{{ random_ipv6(start="fe80::", end="fe80::2") }}" }"#,
429 r#"\{ "some_field": "(fe80::|fe80::1|fe80::2)" }"#,
430 );
431 }
432
433 #[test]
434 #[traced_test]
435 fn test_random_ipv6_near_max() {
436 test_tera_rand_function(
437 random_ipv6,
438 "random_ipv6",
439 r#"{ "some_field": "{{ random_ipv6(start="ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffd", end="ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") }}" }"#,
440 r#"\{ "some_field": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff(d|e|f)" }"#,
441 );
442 }
443
444 #[test]
445 #[traced_test]
446 fn test_random_ipv6_near_min() {
447 test_tera_rand_function(
448 random_ipv6,
449 "random_ipv6",
450 r#"{ "some_field": "{{ random_ipv6(start="::", end="::2") }}" }"#,
451 r#"\{ "some_field": "(::|::1|::2)" }"#,
452 );
453 }
454
455 #[test]
456 #[traced_test]
457 fn test_random_ipv6_with_start_only() {
458 test_tera_rand_function(
459 random_ipv6,
460 "random_ipv6",
461 r#"{ "some_field": "{{ random_ipv6(start="ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffd") }}" }"#,
462 r#"\{ "some_field": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff(d|e|f)" }"#,
463 );
464 }
465
466 #[test]
467 #[traced_test]
468 fn test_random_ipv6_with_end_only() {
469 test_tera_rand_function(
470 random_ipv6,
471 "random_ipv6",
472 r#"{ "some_field": "{{ random_ipv6(end="::2") }}" }"#,
473 r#"\{ "some_field": "(::|::1|::2)" }"#,
474 );
475 }
476
477 #[test]
479 #[traced_test]
480 fn test_random_ipv4_cidr() {
481 test_tera_rand_function(
482 random_ipv4_cidr,
483 "random_ipv4_cidr",
484 r#"{ "some_field": "{{ random_ipv4_cidr() }}" }"#,
485 r#"\{ "some_field": "\d+\.\d+\.\d+\.\d+/\d+" }"#,
486 );
487 }
488
489 #[test]
490 #[traced_test]
491 fn test_random_ipv4_cidr_with_prefix_length_start_and_end() {
492 test_tera_rand_function(
493 random_ipv4_cidr,
494 "random_ipv4_cidr",
495 r#"{ "some_field": "{{ random_ipv4_cidr(length_start=28, length_end=30) }}" }"#,
496 r#"\{ "some_field": "\d+\.\d+\.\d+\.\d+/(28|29|30)" }"#,
497 );
498 }
499
500 #[test]
501 #[traced_test]
502 fn test_random_ipv4_cidr_with_prefix_length_start() {
503 test_tera_rand_function(
504 random_ipv4_cidr,
505 "random_ipv4_cidr",
506 r#"{ "some_field": "{{ random_ipv4_cidr(length_start=30) }}" }"#,
507 r#"\{ "some_field": "\d+\.\d+\.\d+\.\d+/(30|31|32)" }"#,
508 );
509 }
510
511 #[test]
512 #[traced_test]
513 fn test_random_ipv4_cidr_with_prefix_length_end() {
514 test_tera_rand_function(
515 random_ipv4_cidr,
516 "random_ipv4_cidr",
517 r#"{ "some_field": "{{ random_ipv4_cidr(length_end=2) }}" }"#,
518 r#"\{ "some_field": "\d+\.\d+\.\d+\.\d+/(0|1|2)" }"#,
519 );
520 }
521
522 #[test]
523 #[traced_test]
524 fn test_random_ipv4_cidr_with_32_bit_prefix() {
525 test_tera_rand_function(
526 random_ipv4_cidr,
527 "random_ipv4_cidr",
528 r#"{ "some_field": "{{ random_ipv4_cidr(length_start=32, length_end=32) }}" }"#,
529 r#"\{ "some_field": "\d+\.\d+\.\d+\.\d+/32" }"#,
530 );
531 }
532
533 #[test]
534 #[traced_test]
535 fn test_random_ipv4_cidr_with_0_bit_prefix() {
536 test_tera_rand_function(
537 random_ipv4_cidr,
538 "random_ipv4_cidr",
539 r#"{ "some_field": "{{ random_ipv4_cidr(length_start=0, length_end=0) }}" }"#,
540 r#"\{ "some_field": "0\.0\.0\.0/0" }"#,
541 );
542 }
543
544 #[test]
545 #[traced_test]
546 fn test_random_ipv4_cidr_with_too_large_prefix_length_returns_error() {
547 test_tera_rand_function_returns_error(
548 random_ipv4_cidr,
549 "random_ipv4_cidr",
550 r#"{ "some_field": "{{ random_ipv4_cidr(length_start=0, length_end=33) }}" }"#,
551 );
552 }
553
554 #[test]
555 #[traced_test]
556 fn test_random_ipv4_cidr_with_too_small_prefix_length_returns_error() {
557 test_tera_rand_function_returns_error(
558 random_ipv4_cidr,
559 "random_ipv4_cidr",
560 r#"{ "some_field": "{{ random_ipv4_cidr(length_start=-1, length_end=16) }}" }"#,
561 );
562 }
563
564 #[test]
566 #[traced_test]
567 fn test_random_ipv6_cidr() {
568 test_tera_rand_function(
569 random_ipv6_cidr,
570 "random_ipv6_cidr",
571 r#"{ "some_field": "{{ random_ipv6_cidr() }}" }"#,
572 r#"\{ "some_field": "([\da-f]{0,4}:){1,7}[\da-f]{0,4}/\d+" }"#,
573 );
574 }
575
576 #[test]
577 #[traced_test]
578 fn test_random_ipv6_cidr_with_prefix_length_start_and_end() {
579 test_tera_rand_function(
580 random_ipv6_cidr,
581 "random_ipv6_cidr",
582 r#"{ "some_field": "{{ random_ipv6_cidr(length_start=86, length_end=88) }}" }"#,
583 r#"\{ "some_field": "([\da-f]{0,4}:){1,7}[\da-f]{0,4}/(86|87|88)" }"#,
584 );
585 }
586
587 #[test]
588 #[traced_test]
589 fn test_random_ipv6_cidr_with_prefix_length_start() {
590 test_tera_rand_function(
591 random_ipv6_cidr,
592 "random_ipv6_cidr",
593 r#"{ "some_field": "{{ random_ipv6_cidr(length_start=126) }}" }"#,
594 r#"\{ "some_field": "([\da-f]{0,4}:){1,7}[\da-f]{0,4}/(126|127|128)" }"#,
595 );
596 }
597
598 #[test]
599 #[traced_test]
600 fn test_random_ipv6_cidr_with_prefix_length_end() {
601 test_tera_rand_function(
602 random_ipv6_cidr,
603 "random_ipv6_cidr",
604 r#"{ "some_field": "{{ random_ipv6_cidr(length_end=2) }}" }"#,
605 r#"\{ "some_field": "([\da-f]{0,4}:){1,7}[\da-f]{0,4}/(0|1|2)" }"#,
606 );
607 }
608
609 #[test]
610 #[traced_test]
611 fn test_random_ipv6_cidr_with_128_bit_prefix() {
612 test_tera_rand_function(
613 random_ipv6_cidr,
614 "random_ipv6_cidr",
615 r#"{ "some_field": "{{ random_ipv6_cidr(length_start=128, length_end=128) }}" }"#,
616 r#"\{ "some_field": "([\da-f]{0,4}:){1,7}[\da-f]{0,4}/128" }"#,
617 );
618 }
619
620 #[test]
621 #[traced_test]
622 fn test_random_ipv6_cidr_with_0_bit_prefix() {
623 test_tera_rand_function(
624 random_ipv6_cidr,
625 "random_ipv6_cidr",
626 r#"{ "some_field": "{{ random_ipv6_cidr(length_start=0, length_end=0) }}" }"#,
627 r#"\{ "some_field": "::/0" }"#,
628 );
629 }
630
631 #[test]
632 #[traced_test]
633 fn test_random_ipv6_cidr_with_too_large_prefix_length_returns_error() {
634 test_tera_rand_function_returns_error(
635 random_ipv6_cidr,
636 "random_ipv6_cidr",
637 r#"{ "some_field": "{{ random_ipv6_cidr(length_start=0, length_end=129) }}" }"#,
638 );
639 }
640
641 #[test]
642 #[traced_test]
643 fn test_random_ipv6_cidr_with_too_small_prefix_length_returns_error() {
644 test_tera_rand_function_returns_error(
645 random_ipv6_cidr,
646 "random_ipv6_cidr",
647 r#"{ "some_field": "{{ random_ipv6_cidr(length_start=-1, length_end=16) }}" }"#,
648 );
649 }
650}