diff --git a/base/hosts.c b/base/hosts.c index 4d40167bf..3ec5d17fd 100644 --- a/base/hosts.c +++ b/base/hosts.c @@ -466,8 +466,8 @@ is_hostname (const char *str) * * @return 1 if valid IPv6 CIDR-expressed block, 0 otherwise. */ -static int -is_cidr6_block (const char *str) +int +gvm_is_cidr6_block (const char *str) { long block; char *addr6_str, *block_str, *p; @@ -512,8 +512,8 @@ is_cidr6_block (const char *str) * * @return -1 if error, 0 otherwise. */ -static int -cidr6_get_block (const char *str, unsigned int *block) +int +gvm_cidr6_get_block (const char *str, unsigned int *block) { if (str == NULL || block == NULL) return -1; @@ -533,8 +533,8 @@ cidr6_get_block (const char *str, unsigned int *block) * * @return -1 if error, 0 otherwise. */ -static int -cidr6_get_ip (const char *str, struct in6_addr *addr6) +int +gvm_cidr6_get_ip (const char *str, struct in6_addr *addr6) { gchar *addr6_str, *tmp; @@ -568,8 +568,9 @@ cidr6_get_ip (const char *str, struct in6_addr *addr6) * * @return -1 if error, 0 else. */ -static int -cidr6_block_ips (const char *str, struct in6_addr *first, struct in6_addr *last) +int +gvm_cidr6_block_ips (const char *str, struct in6_addr *first, + struct in6_addr *last) { unsigned int block; int i, j; @@ -578,9 +579,9 @@ cidr6_block_ips (const char *str, struct in6_addr *first, struct in6_addr *last) return -1; /* Get IP and block values. */ - if (cidr6_get_block (str, &block) == -1) + if (gvm_cidr6_get_block (str, &block) == -1) return -1; - if (cidr6_get_ip (str, first) == -1) + if (gvm_cidr6_get_ip (str, first) == -1) return -1; memcpy (&last->s6_addr, &first->s6_addr, 16); @@ -844,7 +845,7 @@ gvm_get_host_type (const gchar *str_stripped) return HOST_TYPE_RANGE_LONG; /* Check for regular IPv6 CIDR-expressed block like "2620:0:2d0:200::7/120" */ - if (is_cidr6_block (str_stripped)) + if (gvm_is_cidr6_block (str_stripped)) return HOST_TYPE_CIDR6_BLOCK; /* Check for short range-expressed networks "::1-ef12" */ @@ -1216,7 +1217,7 @@ gvm_hosts_new_with_max (const gchar *hosts_str, unsigned int max_hosts) struct in6_addr *); if (host_type == HOST_TYPE_CIDR6_BLOCK) - ips_func = cidr6_block_ips; + ips_func = gvm_cidr6_block_ips; else if (host_type == HOST_TYPE_RANGE6_SHORT) ips_func = short_range6_network_ips; else diff --git a/base/hosts.h b/base/hosts.h index 1c34e6bd4..95a8d1b1e 100644 --- a/base/hosts.h +++ b/base/hosts.h @@ -208,4 +208,17 @@ gvm_vhost_new (char *, char *); int gvm_get_host_type (const gchar *); +int +gvm_is_cidr6_block (const char *); + +int +gvm_cidr6_get_block (const char *, unsigned int *); + +int +gvm_cidr6_get_ip (const char *, struct in6_addr *); + +int +gvm_cidr6_block_ips (const char *str, struct in6_addr *first, + struct in6_addr *last); + #endif /* not _GVM_BASE_HOSTS_H */ diff --git a/boreas/alivedetection.h b/boreas/alivedetection.h index 1441ec316..baf56f50b 100644 --- a/boreas/alivedetection.h +++ b/boreas/alivedetection.h @@ -36,6 +36,7 @@ start_alive_detection (void *); typedef struct hosts_data hosts_data_t; typedef struct scan_restrictions scan_restrictions_t; +typedef struct ipv6_net_data ipv6_net_data_t; /** * @brief The scanner struct holds data which is used frequently by the alive @@ -62,13 +63,21 @@ struct scanner /* pcap handle */ pcap_t *pcap_handle; hosts_data_t *hosts_data; + ipv6_net_data_t *ipv6_net; scan_restrictions_t *scan_restrictions; /* 0 do not print in stdout, 1 print in stdout used for cmd line cli. */ int print_results; + int host_discovery; }; typedef struct scanner scanner_t; +struct ipv6_net_data +{ + char *net; + struct in6_addr src; +}; + /** * @brief The hosts_data struct holds the alive hosts and target hosts in * separate hashtables. @@ -108,7 +117,8 @@ typedef enum ALIVE_TEST_ICMP = 2, ALIVE_TEST_ARP = 4, ALIVE_TEST_CONSIDER_ALIVE = 8, - ALIVE_TEST_TCP_SYN_SERVICE = 16 + ALIVE_TEST_TCP_SYN_SERVICE = 16, + ALIVE_TEST_IPV6_HOST_DISCOVERY = 32 } alive_test_t; /** diff --git a/boreas/boreas_error.c b/boreas/boreas_error.c index 83b55772d..0fc1c6eb1 100644 --- a/boreas/boreas_error.c +++ b/boreas/boreas_error.c @@ -45,6 +45,9 @@ str_boreas_error (boreas_error_t boreas_error) msg = "Boreas was not able to determine a source address for the given " "destination."; break; + case BOREAS_INVALID_IPV6_NETWORK: + msg = "Invalid IPv6 Network. Not possible to run an Host Discovery"; + break; case NO_ERROR: msg = "No error was encountered by Boreas"; break; diff --git a/boreas/boreas_error.h b/boreas/boreas_error.h index ffbcd5386..ff82d644b 100644 --- a/boreas/boreas_error.h +++ b/boreas/boreas_error.h @@ -13,9 +13,11 @@ typedef enum { BOREAS_OPENING_SOCKET_FAILED = -100, BOREAS_SETTING_SOCKET_OPTION_FAILED, + BOREAS_BIND_SOCKET_FAILED, BOREAS_NO_VALID_ALIVE_TEST_SPECIFIED, BOREAS_CLEANUP_ERROR, BOREAS_NO_SRC_ADDR_FOUND, + BOREAS_INVALID_IPV6_NETWORK, NO_ERROR = 0, } boreas_error_t; diff --git a/boreas/cli.c b/boreas/cli.c index 5f746c959..f563c741a 100644 --- a/boreas/cli.c +++ b/boreas/cli.c @@ -8,6 +8,7 @@ #include "../base/networking.h" #include "../base/prefs.h" #include "alivedetection.h" +#include "boreas_error.h" #include "boreas_io.h" #include "ping.h" #include "sniffer.h" @@ -15,6 +16,8 @@ #include #include +#include +#include #include #undef G_LOG_DOMAIN @@ -38,6 +41,7 @@ init_cli (scanner_t *scanner, gvm_hosts_t *hosts, alive_test_t alive_test, /* No kb used for cli mode.*/ scanner->main_kb = NULL; scanner->print_results = print_results; + scanner->host_discovery = 0; /* hosts_data */ scanner->hosts_data = g_malloc0 (sizeof (hosts_data_t)); @@ -71,6 +75,46 @@ init_cli (scanner_t *scanner, gvm_hosts_t *hosts, alive_test_t alive_test, return error; } +static boreas_error_t +init_cli_for_host_discovery (scanner_t *scanner, const char *net, + const int print_results) +{ + int error; + + /* No kb used for cli mode.*/ + scanner->main_kb = NULL; + scanner->print_results = print_results; + scanner->host_discovery = 1; + + /* hosts_data */ + scanner->hosts_data = g_malloc0 (sizeof (hosts_data_t)); + scanner->hosts_data->alivehosts = + g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + scanner->hosts_data->targethosts = + g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + + // Create socket for getting the src address + error = set_udp6_socket (scanner); + if (error != 0) + { + printf ("%s: %s", __func__, str_boreas_error (error)); + return error; + } + + error = init_ipv6_net_data (scanner, net); + if (error != 0) + { + printf ("%s: Not possible to initialize data: %s", __func__, + str_boreas_error (error)); + return error; + } + + /* No scan restrictions. */ + init_scan_restrictions (scanner, 0); + + return error; +} + static boreas_error_t free_cli (scanner_t *scanner, alive_test_t alive_test) { @@ -86,6 +130,12 @@ free_cli (scanner_t *scanner, alive_test_t alive_test) g_hash_table_destroy (scanner->hosts_data->targethosts); g_free (scanner->hosts_data); + if (alive_test & ALIVE_TEST_IPV6_HOST_DISCOVERY) + { + g_free (scanner->ipv6_net->net); + g_free (scanner->ipv6_net); + } + return close_err; } @@ -101,46 +151,57 @@ run_cli_scan (scanner_t *scanner, alive_test_t alive_test) gettimeofday (&start_time, NULL); number_of_targets = g_hash_table_size (scanner->hosts_data->targethosts); - if (scanner->print_results == 1) + if (scanner->print_results == 1 && scanner->host_discovery) + printf ("Host discovery started for network %s.\n", scanner->ipv6_net->net); + else printf ("Alive scan started: Target has %d hosts.\n", number_of_targets); error = start_sniffer_thread (scanner, &sniffer_thread_id); if (error) return error; - if (alive_test & (ALIVE_TEST_ICMP)) + if (alive_test & (ALIVE_TEST_IPV6_HOST_DISCOVERY)) { - g_hash_table_foreach (scanner->hosts_data->targethosts, send_icmp, - scanner); - wait_until_so_sndbuf_empty (scanner->icmpv4soc, 10); + send_icmp_v6_multicast (scanner); wait_until_so_sndbuf_empty (scanner->icmpv6soc, 10); usleep (500000); } - if (alive_test & (ALIVE_TEST_TCP_SYN_SERVICE)) - { - scanner->tcp_flag = 0x02; /* SYN */ - g_hash_table_foreach (scanner->hosts_data->targethosts, send_tcp, - scanner); - wait_until_so_sndbuf_empty (scanner->tcpv4soc, 10); - wait_until_so_sndbuf_empty (scanner->tcpv6soc, 10); - usleep (500000); - } - if (alive_test & (ALIVE_TEST_TCP_ACK_SERVICE)) - { - scanner->tcp_flag = 0x10; /* ACK */ - g_hash_table_foreach (scanner->hosts_data->targethosts, send_tcp, - scanner); - wait_until_so_sndbuf_empty (scanner->tcpv4soc, 10); - wait_until_so_sndbuf_empty (scanner->tcpv6soc, 10); - usleep (500000); - } - if (alive_test & (ALIVE_TEST_ARP)) + else { - g_hash_table_foreach (scanner->hosts_data->targethosts, send_arp, - scanner); - wait_until_so_sndbuf_empty (scanner->arpv4soc, 10); - wait_until_so_sndbuf_empty (scanner->arpv6soc, 10); - usleep (500000); + if (alive_test & (ALIVE_TEST_ICMP)) + { + g_hash_table_foreach (scanner->hosts_data->targethosts, send_icmp, + scanner); + wait_until_so_sndbuf_empty (scanner->icmpv4soc, 10); + wait_until_so_sndbuf_empty (scanner->icmpv6soc, 10); + usleep (500000); + } + if (alive_test & (ALIVE_TEST_TCP_SYN_SERVICE)) + { + scanner->tcp_flag = 0x02; /* SYN */ + g_hash_table_foreach (scanner->hosts_data->targethosts, send_tcp, + scanner); + wait_until_so_sndbuf_empty (scanner->tcpv4soc, 10); + wait_until_so_sndbuf_empty (scanner->tcpv6soc, 10); + usleep (500000); + } + if (alive_test & (ALIVE_TEST_TCP_ACK_SERVICE)) + { + scanner->tcp_flag = 0x10; /* ACK */ + g_hash_table_foreach (scanner->hosts_data->targethosts, send_tcp, + scanner); + wait_until_so_sndbuf_empty (scanner->tcpv4soc, 10); + wait_until_so_sndbuf_empty (scanner->tcpv6soc, 10); + usleep (500000); + } + if (alive_test & (ALIVE_TEST_ARP)) + { + g_hash_table_foreach (scanner->hosts_data->targethosts, send_arp, + scanner); + wait_until_so_sndbuf_empty (scanner->arpv4soc, 10); + wait_until_so_sndbuf_empty (scanner->arpv6soc, 10); + usleep (500000); + } } if (wait_timeout > 0 && wait_timeout <= 20) @@ -150,8 +211,12 @@ run_cli_scan (scanner_t *scanner, alive_test_t alive_test) stop_sniffer_thread (scanner, sniffer_thread_id); + if (scanner->host_discovery) + number_of_targets = g_hash_table_size (scanner->hosts_data->targethosts); + number_of_dead_hosts = count_difference (scanner->hosts_data->targethosts, scanner->hosts_data->alivehosts); + gettimeofday (&end_time, NULL); if (scanner->print_results == 1) printf ("Alive scan finished in %ld seconds: %d alive hosts of %d.\n", @@ -203,6 +268,83 @@ run_cli_extended (gvm_hosts_t *hosts, alive_test_t alive_test, return NO_ERROR; } +/** + * @brief GHFunc helper function to create a comma separated list of host + * + * @param[in] key host + * @param [in] value Not used + * @param[in/out] userdata comma separated list where new keys are appended to. + */ +static void +create_host_list (gpointer key, gpointer value, gpointer *userdata) +{ + (void) value; + GString *host_str = (GString *) *userdata; + g_string_append (host_str, key); + g_string_append (host_str, ","); +} + +/** + * @brief Runs a host discovery for large ipv6 network + * + * @param[in] net IPv6 network + * @param[out] hosts_found Discovered alive hosts in comma separated list + * + * @return NO_ERROR (0) on success, boreas_error_t on error. + */ +boreas_error_t +run_cli_for_ipv6_network (const char *net, char **hosts_found, + int print_results) +{ + unsigned int block; + struct in6_addr target; + scanner_t scanner = {0}; + boreas_error_t init_err; + boreas_error_t run_err; + boreas_error_t free_err; + + if (net == NULL || gvm_get_host_type (net) != HOST_TYPE_CIDR6_BLOCK) + return BOREAS_INVALID_IPV6_NETWORK; + + if (gvm_cidr6_get_block (net, &block) < 0 + || gvm_cidr6_get_ip (net, &target) < 0) + return BOREAS_INVALID_IPV6_NETWORK; + + init_err = init_cli_for_host_discovery (&scanner, net, print_results); + if (init_err) + { + printf ("Error initializing scanner.\n"); + return init_err; + } + + run_err = run_cli_scan (&scanner, ALIVE_TEST_IPV6_HOST_DISCOVERY); + if (run_err) + { + printf ("Error while running the scan.\n"); + return run_err; + } + + if (hosts_found != NULL) + { + GString *host_str = g_string_new (""); + g_hash_table_foreach (scanner.hosts_data->alivehosts, + (GHFunc) create_host_list, (gpointer) &host_str); + + *hosts_found = g_string_free (host_str, FALSE); + } + + free_err = free_cli (&scanner, ALIVE_TEST_IPV6_HOST_DISCOVERY); + if (free_err) + { + printf ("Error freeing scan data.\n"); + if (hosts_found) + g_free (*hosts_found); + return free_err; + } + + return NO_ERROR; +} + /** * @brief Scan all specified hosts in ip_str list. * diff --git a/boreas/cli.h b/boreas/cli.h index a129c8ba1..ea1d34ab6 100644 --- a/boreas/cli.h +++ b/boreas/cli.h @@ -9,6 +9,8 @@ #include "alivedetection.h" #include "boreas_error.h" +#define FEATURE_HOST_DISCOVERY_IPV6 + boreas_error_t run_cli_extended (gvm_hosts_t *, alive_test_t, const gchar *, const unsigned int); @@ -16,6 +18,9 @@ run_cli_extended (gvm_hosts_t *, alive_test_t, const gchar *, boreas_error_t run_cli (gvm_hosts_t *, alive_test_t, const gchar *); +boreas_error_t +run_cli_for_ipv6_network (const char *, char **, int); + boreas_error_t is_host_alive (const char *, int *); diff --git a/boreas/ping.c b/boreas/ping.c index eb69b9fe3..fc907b737 100644 --- a/boreas/ping.c +++ b/boreas/ping.c @@ -5,6 +5,7 @@ #include "ping.h" +#include "../base/networking.h" #include "../base/prefs.h" /* for prefs_get() */ #include "arp.h" #include "util.h" @@ -32,6 +33,18 @@ */ #define G_LOG_DOMAIN "libgvm boreas" +struct v6pseudo_icmp_hdr +{ + struct in6_addr s6addr; + struct in6_addr d6addr; + u_short length; + u_char zero1; + u_char zero2; + u_char zero3; + u_char protocol; + struct icmp6_hdr icmpheader; +}; + struct v6pseudohdr { struct in6_addr s6addr; @@ -293,6 +306,81 @@ send_icmp (gpointer key, gpointer value, gpointer scanner_p) } } +/** + * @brief Send icmp ping to multicast address. + * + * @param soc Socket to use for sending. + * @param dst Destination network address to explore + */ +void +send_icmp_v6_multicast (gpointer scanner_p) +{ + struct sockaddr_in6 soca; + scanner_t *scanner = scanner_p; + /* Throttling related variables */ + static int so_sndbuf = -1; // socket send buffer + static int init = -1; + u_char packet[sizeof (struct ip6_hdr) + sizeof (struct icmp6_hdr)]; + struct ip6_hdr *ip = (struct ip6_hdr *) packet; + int soc = scanner->icmpv6soc; + struct icmp6_hdr *icmp = + (struct icmp6_hdr *) (packet + sizeof (struct ip6_hdr)); + + memset (packet, 0, sizeof (packet)); + /* IPv6 */ + ip->ip6_flow = htonl ((6 << 28) | (0 << 20) | 0); + ip->ip6_plen = htons (8); // ICMP6_HDRLEN + ip->ip6_nxt = IPPROTO_ICMPV6; + ip->ip6_hops = 255; // max value + ip->ip6_src = scanner->ipv6_net->src; + inet_pton (AF_INET6, "FF02::1", &ip->ip6_dst); + + /* ICMP */ + icmp->icmp6_type = ICMP6_ECHO_REQUEST; + icmp->icmp6_code = 0; + icmp->icmp6_id = get_echo_id (); + icmp->icmp6_seq = 0x0100; + + /* CKsum */ + { + struct v6pseudo_icmp_hdr pseudoheader; + + memset (&pseudoheader, 0, sizeof (packet)); + memcpy (&pseudoheader.s6addr, &ip->ip6_src, sizeof (struct in6_addr)); + memcpy (&pseudoheader.d6addr, &ip->ip6_dst, sizeof (struct in6_addr)); + + pseudoheader.protocol = IPPROTO_ICMPV6; + pseudoheader.length = htons (8); + memcpy ((char *) &pseudoheader.icmpheader, (char *) icmp, + sizeof (struct icmp6_hdr)); + icmp->icmp6_cksum = + in_cksum ((unsigned short *) &pseudoheader, sizeof (packet)); + } + + /* Get size of empty SO_SNDBUF */ + if (init == -1) + { + if (get_so_sndbuf (soc, &so_sndbuf) == 0) + init = 1; + } + + /* Throttle speed if needed */ + throttle (soc, so_sndbuf); + + // destination socket + memset (&soca, 0, sizeof (soca)); + soca.sin6_family = AF_INET6; + soca.sin6_addr = ip->ip6_dst; + + /* ICMP6_HDRLEN(8) IP6_HDRLEN(40) */ + if (sendto (soc, (const void *) ip, 40 + 8, 0, (struct sockaddr *) &soca, + sizeof (struct sockaddr_in6)) + < 0) + { + g_debug ("%s: sendto(): %s", __func__, strerror (errno)); + } +} + /** * @brief Send tcp ping. * diff --git a/boreas/ping.h b/boreas/ping.h index be3a66f09..1ce1e850f 100644 --- a/boreas/ping.h +++ b/boreas/ping.h @@ -14,4 +14,6 @@ void send_tcp (gpointer, gpointer, gpointer); void send_arp (gpointer, gpointer, gpointer); +void send_icmp_v6_multicast (gpointer); + #endif /* not _GVM_BOREAS_PING_H */ diff --git a/boreas/sniffer.c b/boreas/sniffer.c index 1f274f9dc..f657f79c8 100644 --- a/boreas/sniffer.c +++ b/boreas/sniffer.c @@ -5,14 +5,17 @@ #include "sniffer.h" +#include "../base/networking.h" /* for range_t */ #include "alivedetection.h" #include "boreas_io.h" +#include "util.h" #include #include #include #include #include +#include #include #include #include @@ -170,11 +173,17 @@ got_packet (u_char *user_data, g_debug ("%s: Failed to transform IP into string representation: %s", __func__, strerror (errno)); } + /* Only put unique hosts on queue and in hash table. Use short circuit * evaluation to not add hosts to the hash table which are not in our * target list.*/ - if ((g_hash_table_contains (hosts_data->targethosts, addr_str) == TRUE) - && (g_hash_table_add (hosts_data->alivehosts, g_strdup (addr_str)))) + if (((scanner->host_discovery == 0) + && (g_hash_table_contains (hosts_data->targethosts, addr_str) == TRUE) + && (g_hash_table_add (hosts_data->alivehosts, g_strdup (addr_str)))) + || ((scanner->host_discovery == 1) + && (cidr6block_contains (scanner->ipv6_net->net, addr_str) > 0) + && (g_hash_table_add (hosts_data->alivehosts, g_strdup (addr_str))) + && (g_hash_table_add (hosts_data->targethosts, g_strdup (addr_str))))) { /* handle max_scan_hosts related restrictions. */ handle_scan_restrictions (scanner, addr_str); @@ -283,7 +292,20 @@ start_sniffer_thread (scanner_t *scanner, pthread_t *sniffer_thread_id) { int err; - scanner->pcap_handle = open_live (NULL, FILTER_STR); + if (scanner->host_discovery) + { + char filter[256]; + char *addr = addr6_as_str (&(scanner->ipv6_net->src)); + snprintf (filter, sizeof (filter), "ip6 and ip6[40]=129 and dst %s", + addr); + g_free (addr); + scanner->pcap_handle = open_live (NULL, filter); + } + else + { + scanner->pcap_handle = open_live (NULL, FILTER_STR); + } + if (scanner->pcap_handle == NULL) { g_warning ("%s: Unable to open valid pcap handle.", __func__); diff --git a/boreas/util.c b/boreas/util.c index 504d3195f..edc511f3b 100644 --- a/boreas/util.c +++ b/boreas/util.c @@ -6,6 +6,8 @@ #include "util.h" #include "../base/networking.h" /* for range_t */ +#include "alivedetection.h" +#include "boreas_error.h" #include #include @@ -534,6 +536,84 @@ set_socket (socket_type_t socket_type, int *scanner_socket) return error; } +boreas_error_t +set_udp6_socket (scanner_t *scanner) +{ + boreas_error_t error = NO_ERROR; + error = set_socket (UDPV6, &(scanner->udpv6soc)); + return error; +} + +boreas_error_t +init_ipv6_net_data (scanner_t *scanner, const char *net) +{ + struct in6_addr first, last; + struct sockaddr_in6 socs; + int soc; + boreas_error_t error = NO_ERROR; + // IPv6 net data for host discovery + scanner->ipv6_net = g_malloc0 (sizeof (ipv6_net_data_t)); + scanner->ipv6_net->net = g_strdup (net); + + /* Get source address for IPv6 header. */ + gvm_cidr6_block_ips (scanner->ipv6_net->net, &first, &last); + + error = get_source_addr_v6 (&(scanner->udpv6soc), &first, + &(scanner->ipv6_net->src)); + if (error) + { + char destination_str[INET_ADDRSTRLEN]; + inet_ntop (AF_INET6, (const void *) &first, destination_str, + INET_ADDRSTRLEN); + printf ("%s: Destination: %s. %s", __func__, destination_str, + str_boreas_error (error)); + return BOREAS_INVALID_IPV6_NETWORK; + } + + int opt_on = 1; + soc = socket (AF_INET6, SOCK_RAW, IPPROTO_RAW); + if (soc < 0) + { + g_warning ("%s: failed to open ICPMV6 socket: %s", __func__, + strerror (errno)); + return BOREAS_OPENING_SOCKET_FAILED; + } + + if (setsockopt (soc, IPPROTO_IPV6, IP_HDRINCL, (char *) &opt_on, + sizeof (opt_on)) + < 0) + { + g_warning ("%s: failed to set socket option IP_HDRINCL: %s", __func__, + strerror (errno)); + close (soc); + return BOREAS_SETTING_SOCKET_OPTION_FAILED; + } + + // Enable multicast + error = set_broadcast (soc); + if (error != 0) + { + close (soc); + return error; + } + + // Create source address + memset (&socs, 0, sizeof (socs)); + socs.sin6_family = AF_INET6; + socs.sin6_addr = scanner->ipv6_net->src; + // and set the source address to the socket + if (bind (soc, (struct sockaddr *) &socs, sizeof (socs)) < 0) + { + g_warning ("%s: failed to bind socket to source address: %s", __func__, + strerror (errno)); + close (soc); + return BOREAS_BIND_SOCKET_FAILED; + } + scanner->icmpv6soc = soc; + + return error; +} + /** * @brief Set all sockets needed for the chosen detection methods. * @@ -657,3 +737,51 @@ wait_until_so_sndbuf_empty (int soc, int timeout) usleep (100000); } } + +/** + * @brief Check if the address is contained in the CIDR block + * + * @param cidr a valid IPv6 CIDR-expressed block + * @param address IPv6 address to check if it is contained. + * + * @return 1 if it is contained, 0 if not. Boreas error type on error; + */ +int +cidr6block_contains (const char *cidr, const char *address) +{ + struct in6_addr net, ip; + unsigned int prefix_len = 0; + + if (gvm_cidr6_get_ip (cidr, &net) || gvm_cidr6_get_block (cidr, &prefix_len)) + + g_debug ("Checking if %s belongs to the CIDR block %s", address, cidr); + + if (inet_pton (AF_INET6, address, &ip) != 1) + return -1; + + const uint8_t *addr_bytes = (const uint8_t *) &ip; + const uint8_t *net_bytes = (const uint8_t *) &net; + + int bytes_to_compare = prefix_len / 8; + int bits_to_compare = prefix_len % 8; + + if (bytes_to_compare > 0) + { + if (memcmp (addr_bytes, net_bytes, bytes_to_compare) != 0) + { + return 0; + } + } + + if (bits_to_compare > 0) + { + uint8_t mask = 0xFF << (8 - bits_to_compare); + if ((addr_bytes[bytes_to_compare] & mask) + != (net_bytes[bytes_to_compare] & mask)) + { + return 0; + } + } + + return 1; // All network bits match +} diff --git a/boreas/util.h b/boreas/util.h index d0963c92c..108542378 100644 --- a/boreas/util.h +++ b/boreas/util.h @@ -23,11 +23,20 @@ get_source_addr_v6 (int *, struct in6_addr *, struct in6_addr *); boreas_error_t get_source_addr_v4 (int *, struct in_addr *, struct in_addr *); +int +cidr6block_contains (const char *, const char *); + void fill_ports_array (gpointer, gpointer); boreas_error_t set_all_needed_sockets (scanner_t *, alive_test_t); +boreas_error_t +set_udp6_socket (scanner_t *); + +boreas_error_t +init_ipv6_net_data (scanner_t *, const char *); + boreas_error_t close_all_needed_sockets (scanner_t *, alive_test_t); diff --git a/boreas/util_tests.c b/boreas/util_tests.c index 5a6bff78d..b099896dd 100644 --- a/boreas/util_tests.c +++ b/boreas/util_tests.c @@ -3,6 +3,7 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ +#include "boreas_error.c" #include "util.c" #include @@ -232,6 +233,15 @@ Ensure (util, fill_ports_array) g_array_free (ports_garray, TRUE); } +Ensure (util, cidr6_contains_ip) +{ + assert_that (cidr6block_contains ("5858::/64", "5858::1"), is_equal_to (1)); + assert_that (cidr6block_contains ("5858::/64", "5858::ff"), is_equal_to (1)); + assert_that (cidr6block_contains ("5858::/120", "5858::ff"), is_equal_to (1)); + assert_that (cidr6block_contains ("5858::/120", "5858::252"), + is_equal_to (0)); +} + int main (int argc, char **argv) { @@ -247,6 +257,7 @@ main (int argc, char **argv) add_test_with_context (suite, util, set_socket); add_test_with_context (suite, util, get_source_addr_v4); add_test_with_context (suite, util, get_source_addr_v6); + add_test_with_context (suite, util, cidr6_contains_ip); if (argc > 1) ret = run_single_test (suite, argv[1], create_text_reporter ());