Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get interface used for routing to given destination #339

Merged
merged 12 commits into from
May 28, 2020
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Add gmp_start_task_ext_c. [#327](https://github.com/greenbone/gvm-libs/pull/327)
- Make log mutex visible. [#328](https://github.com/greenbone/gvm-libs/pull/328)
- Add new scan status PENDING. [#336](https://github.com/greenbone/gvm-libs/pull/336)
- Add gvm_routethrough which is used by Boreas alive detection module. [#339](https://github.com/greenbone/gvm-libs/pull/339)

### Fixed
- Fix is_cidr_block(). [#322](https://github.com/greenbone/gvm-libs/pull/322)
Expand Down
5 changes: 4 additions & 1 deletion base/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,11 @@ if (BUILD_TESTS)
EXCLUDE_FROM_ALL
networking_tests.c)
add_test (networking-test networking-test)
set (NETWORKING_TEST_LINKER_WRAP_OPTIONS
"-Wl,-wrap,g_io_channel_new_file,-wrap,g_io_channel_shutdown")
target_include_directories (networking-test PRIVATE ${CGREEN_INCLUDE_DIRS} )
target_link_libraries (networking-test gvm_base_shared ${CGREEN_LIBRARIES} ${GLIB_LDFLAGS} ${LINKER_HARDENING_FLAGS})
target_link_libraries (networking-test gvm_base_shared ${CGREEN_LIBRARIES} ${GLIB_LDFLAGS} ${LINKER_HARDENING_FLAGS}
${NETWORKING_TEST_LINKER_WRAP_OPTIONS})

add_executable (version-test
EXCLUDE_FROM_ALL
Expand Down
338 changes: 338 additions & 0 deletions base/networking.c
Original file line number Diff line number Diff line change
Expand Up @@ -762,3 +762,341 @@ ipv6_is_enabled ()

return 1;
}

/* Functions used by alive detection module (Boreas). */

/**
* @brief Determine if IP is localhost.
*
* @return True if IP is localhost, else false.
*/
gboolean
ip_islocalhost (struct sockaddr_storage *storage)
{
struct in_addr addr;
struct in_addr *addr_p = &addr;
struct in6_addr addr6;
struct in6_addr *addr6_p = &addr6;
struct sockaddr_in *sin_p;
struct sockaddr_in6 *sin6_p;
struct ifaddrs *ifaddr = NULL;
struct ifaddrs *ifa = NULL;
int family;

family = storage->ss_family;

if (family == AF_INET)
{
sin_p = (struct sockaddr_in *) storage;
addr = sin_p->sin_addr;

if (addr_p == NULL)
return FALSE;
/* addr is 0.0.0.0 */
if ((addr_p)->s_addr == 0)
return TRUE;
/* addr starts with 127.0.0.1 */
if (((addr_p)->s_addr & htonl (0xFF000000)) == htonl (0x7F000000))
return TRUE;
}
if (family == AF_INET6)
{
sin6_p = (struct sockaddr_in6 *) storage;
addr6 = sin6_p->sin6_addr;

if (IN6_IS_ADDR_V4MAPPED (&addr6))
{
/* addr is 0.0.0.0 */
if (addr6_p->s6_addr32[3] == 0)
return 1;

/* addr starts with 127.0.0.1 */
if ((addr6_p->s6_addr32[3] & htonl (0xFF000000))
== htonl (0x7F000000))
return 1;
}
if (IN6_IS_ADDR_LOOPBACK (addr6_p))
return 1;
}

if (getifaddrs (&ifaddr) == -1)
{
g_debug ("%s: getifaddr failed: %s", __func__, strerror (errno));
return FALSE;
}
else
{
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
{
if (ifa->ifa_addr == NULL)
continue;
if (ifa->ifa_addr->sa_family == AF_INET)
{
sin = (struct sockaddr_in *) (ifa->ifa_addr);
/* Check if same address as local interface. */
if (addr_p->s_addr == sin->sin_addr.s_addr)
return TRUE;
}
if (ifa->ifa_addr->sa_family == AF_INET6)
{
sin6 = (struct sockaddr_in6 *) (ifa->ifa_addr);
/* Check if same address as local interface. */
if (IN6_ARE_ADDR_EQUAL (&(sin6->sin6_addr), addr6_p))
return TRUE;
}
}
freeifaddrs (ifaddr);
}

return FALSE;
}

/** Entry of routing table /proc/net/route */
struct route_entry
ArnoStiefvater marked this conversation as resolved.
Show resolved Hide resolved
{
gchar *interface;
unsigned long mask;
unsigned long dest;
};

/**
* @brief Get the entries of /proc/net/route as list of route_entry structs.
*
* @return GSList of route_entry structs.
*/
static GSList *
get_routes (void)
{
GSList *routes = NULL;
GError *err = NULL;
GIOChannel *file_channel;
gchar *line;
gchar **items_in_line;
int status;
struct route_entry *entry;

/* Open "/proc/net/route". */
file_channel = g_io_channel_new_file ("/proc/net/route", "r", &err);
if (file_channel == NULL)
{
g_warning ("%s: %s. ", __func__,
err ? err->message : "Error opening /proc/net/ipv6_route");
err = NULL;
return NULL;
}

/* Skip first first line of file. */
status = g_io_channel_read_line (file_channel, &line, NULL, NULL, &err);
if (status != G_IO_STATUS_NORMAL || !line || err)
{
g_warning ("%s: %s", __func__,
err ? err->message
: "g_io_channel_read_line() status != G_IO_STATUS_NORMAL");
if (line)
g_free (line);
err = NULL;
}

/* Until EOF or err we go through lines of file and extract Iface, Mask and
* Destination and but it into the to be returned list of routes.*/
mattmundell marked this conversation as resolved.
Show resolved Hide resolved
while (1)
{
gchar *interface;
unsigned long mask;
unsigned long dest;
gchar *char_p;
int count = 0;

/* Get new line. */
status = g_io_channel_read_line (file_channel, &line, NULL, NULL, &err);
mattmundell marked this conversation as resolved.
Show resolved Hide resolved
if ((status != G_IO_STATUS_NORMAL) || !line || err)
{
g_warning (
"%s: %s", __func__,
err ? err->message
: "g_io_channel_read_line() status != G_IO_STATUS_NORMAL");
err = NULL;
if (line)
g_free (line);
break;
}

/* Get items in line. */
items_in_line = g_strsplit (line, "\t", -1);
/* Check for missing entries in line of "/proc/net/route". */
for (; items_in_line[count]; count++)
;
if (11 != count)
{
g_strfreev (items_in_line);
g_free (line);
continue;
}

interface = g_strndup (items_in_line[0], 64);
/* Cut interface str after ":" if IP aliasing is used. */
if ((char_p = strchr (interface, ':')))
{
*char_p = '\0';
}
dest = strtoul (items_in_line[1], NULL, 16);
mask = strtoul (items_in_line[7], NULL, 16);

/* Fill GSList entry. */
entry = g_malloc0 (sizeof (struct route_entry));
entry->interface = interface;
entry->dest = dest;
entry->mask = mask;
routes = g_slist_append (routes, entry);

g_strfreev (items_in_line);
if (line)
g_free (line);
}

status = g_io_channel_shutdown (file_channel, TRUE, &err);
if ((G_IO_STATUS_NORMAL != status) || err)
g_warning ("%s: %s", __func__,
err ? err->message
: "g_io_channel_shutdown() was not successfull");

return routes;
}

/**
* @brief Get Interface which should be used for routing to destination addr.
*
* This function should be used sparingly as it parses /proc/net/route for
* every call.
*
* @param[in] storage_dest Destination address.
* @param[out] storage_source Source address. Is set to either address of the
* interface we use or global source address if set. Only gets filled if
* storage_source != NULL.
*
* @return Interface name of interface used for routing to destination address.
* NULL if no interface found or Error.
*/
gchar *
gvm_routethrough (struct sockaddr_storage *storage_dest,
struct sockaddr_storage *storage_source)
{
if (!storage_dest)
ArnoStiefvater marked this conversation as resolved.
Show resolved Hide resolved
return NULL;

struct ifaddrs *ifaddr = NULL;
struct ifaddrs *ifa = NULL;
gchar *interface_out = NULL;

if (getifaddrs (&ifaddr) == -1)
{
g_debug ("%s: getifaddr failed: %s", __func__, strerror (errno));
return NULL;
}

/* IPv4. */
if (storage_dest->ss_family == AF_INET)
{
GSList *routes = NULL;
mattmundell marked this conversation as resolved.
Show resolved Hide resolved
GSList *routes_p = NULL;
routes = get_routes ();

/* Set storage_source to localhost if storage_source was supplied and
* return name of loopback interface. */
if (ip_islocalhost (storage_dest))
{
// TODO: check for (storage_source->ss_family == AF_INET)
if (storage_source)
{
struct sockaddr_in *sin_p = (struct sockaddr_in *) storage_source;
sin_p->sin_addr.s_addr = htonl (0x7F000001);
}

for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
{
if ((ifa->ifa_addr->sa_family == AF_INET)
&& (ifa->ifa_flags & (IFF_LOOPBACK)))
{
interface_out = g_strdup (ifa->ifa_name);
break;
}
}
}
else
{
struct sockaddr_in *sin_dest_p = NULL;
mattmundell marked this conversation as resolved.
Show resolved Hide resolved
struct sockaddr_in *sin_src_p = NULL;
unsigned long best_match = 0;
struct in_addr global_src;

/* Check if global_source_addr in use. */
gvm_source_addr (&global_src);

sin_dest_p = (struct sockaddr_in *) storage_dest;
sin_src_p = (struct sockaddr_in *) storage_source;
/* Check routes for matching address. Get interface name and set
* storage_source*/
for (routes_p = routes; routes_p; routes_p = routes_p->next)
{
if (((sin_dest_p->sin_addr.s_addr
& ((struct route_entry *) (routes_p->data))->mask)
== ((struct route_entry *) (routes_p->data))->dest)
&& (((struct route_entry *) (routes_p->data))->mask
>= best_match))
{
/* Interface of matching route.*/
if (interface_out)
mattmundell marked this conversation as resolved.
Show resolved Hide resolved
g_free (interface_out);
interface_out = g_strdup (
((struct route_entry *) (routes_p->data))->interface);
best_match = ((struct route_entry *) (routes_p->data))->mask;

if (!storage_source)
continue;

/* Set storage_source to global source if global source
* present.*/
if (global_src.s_addr != INADDR_ANY)
sin_src_p->sin_addr.s_addr = global_src.s_addr;
/* Set storage_source to addr of matching interface if no
* gloabal source present.*/
else
{
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
{
if ((ifa->ifa_addr->sa_family == AF_INET)
&& (g_strcmp0 (interface_out, ifa->ifa_name)
== 0))
{
sin_src_p->sin_addr.s_addr =
((struct sockaddr_in *) (ifa->ifa_addr))
->sin_addr.s_addr;
break;
}
}
}
}
}
}
/* Free GSList. */
if (routes)
{
for (routes_p = routes; routes_p; routes_p = routes_p->next)
{
if (((struct route_entry *) (routes_p->data))->interface)
g_free (((struct route_entry *) (routes_p->data))->interface);
}
g_slist_free (routes);
}
}
else if (storage_dest->ss_family == AF_INET6)
{
g_warning ("%s: IPv6 not yet implemented for this function. Will be "
"implemented soon. Thanks for your patience.",
__func__);
}

return interface_out != NULL ? interface_out : NULL;
}
4 changes: 4 additions & 0 deletions base/networking.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,8 @@ port_in_port_ranges (int, port_protocol_t, array_t *);
int
ipv6_is_enabled ();

gchar *
gvm_routethrough (struct sockaddr_storage *storage_dest,
mattmundell marked this conversation as resolved.
Show resolved Hide resolved
struct sockaddr_storage *storage_source);

#endif /* not _GVM_NETWORKING_H */
Loading