Commit a0ac1c6d65 for strongswan.org
commit a0ac1c6d65e8ddbcd93bacc01c11e09c6b88f8d6
Author: Tobias Brunner <tobias@strongswan.org>
Date: Thu Sep 17 10:26:27 2026 +0200
host: Add helper functions to check if an IP is loopback or link-local
diff --git a/src/libstrongswan/networking/host.c b/src/libstrongswan/networking/host.c
index a63673a76e..dc2bf5b8c3 100644
--- a/src/libstrongswan/networking/host.c
+++ b/src/libstrongswan/networking/host.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006-2014 Tobias Brunner
+ * Copyright (C) 2006-2026 Tobias Brunner
* Copyright (C) 2006 Daniel Roethlisberger
* Copyright (C) 2005-2006 Martin Willi
* Copyright (C) 2005 Jan Hutter
@@ -701,3 +701,65 @@ host_t *host_create_any(int family)
free(this);
return NULL;
}
+
+/*
+ * Described in header
+ */
+bool host_is_loopback(host_t *host)
+{
+ private_host_t *this = (private_host_t*)host;
+
+ if (!host)
+ {
+ return FALSE;
+ }
+
+ switch (this->address.sa_family)
+ {
+ case AF_INET:
+ {
+ /* for IPv4 it's the whole 127.0.0.0/8 subnet */
+ return ntohl(this->address4.sin_addr.s_addr) >> 24 == 0x7f;
+ }
+ case AF_INET6:
+ {
+ /* only a single address (::1) for IPv6 */
+ return IN6_IS_ADDR_LOOPBACK(&this->address6.sin6_addr);
+ }
+ default:
+ {
+ return FALSE;
+ }
+ }
+}
+
+/*
+ * Described in header
+ */
+bool host_is_linklocal(host_t *host)
+{
+ private_host_t *this = (private_host_t*)host;
+
+ if (!host)
+ {
+ return FALSE;
+ }
+
+ switch (this->address.sa_family)
+ {
+ case AF_INET:
+ {
+ /* 169.254.0.0/16 */
+ return ntohl(this->address4.sin_addr.s_addr) >> 16 == 0xa9fe;
+ }
+ case AF_INET6:
+ {
+ /* fe80::/10 */
+ return IN6_IS_ADDR_LINKLOCAL(&this->address6.sin6_addr);
+ }
+ default:
+ {
+ return FALSE;
+ }
+ }
+}
diff --git a/src/libstrongswan/networking/host.h b/src/libstrongswan/networking/host.h
index 6abd937439..96d5a8c21b 100644
--- a/src/libstrongswan/networking/host.h
+++ b/src/libstrongswan/networking/host.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006-2014 Tobias Brunner
+ * Copyright (C) 2006-2026 Tobias Brunner
* Copyright (C) 2006 Daniel Roethlisberger
* Copyright (C) 2005-2008 Martin Willi
* Copyright (C) 2005 Jan Hutter
@@ -220,6 +220,22 @@ host_t *host_create_netmask(int family, int netbits);
*/
host_t *host_create_any(int family);
+/**
+ * Check if the host's IP address is set to a loopback address.
+ *
+ * @param host host to check
+ * @return TRUE if host matches 127.0.0.0/8 or ::1/128
+ */
+bool host_is_loopback(host_t *host);
+
+/**
+ * Check if the host's IP address is set to a link-local address.
+ *
+ * @param host host to check
+ * @return TRUE if host matches 169.254.0.0/16 or fe80::/10
+ */
+bool host_is_linklocal(host_t *host);
+
/**
* printf hook function for host_t.
*
diff --git a/src/libstrongswan/tests/suites/test_host.c b/src/libstrongswan/tests/suites/test_host.c
index 27cec85ddc..7ac6a0541c 100644
--- a/src/libstrongswan/tests/suites/test_host.c
+++ b/src/libstrongswan/tests/suites/test_host.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Tobias Brunner
+ * Copyright (C) 2013-2026 Tobias Brunner
*
* Copyright (C) secunet Security Networks AG
*
@@ -642,6 +642,74 @@ START_TEST(test_equals_any)
}
END_TEST
+/*******************************************************************************
+ * host_is_loopback/host_is_linklocal
+ */
+
+START_TEST(test_host_is_loopback)
+{
+ host_t *a;
+
+ ck_assert(!host_is_loopback(NULL));
+
+ a = host_create_from_string("192.168.0.1", 0);
+ ck_assert(!host_is_loopback(a));
+ a->destroy(a);
+
+ a = host_create_from_string("127.0.0.1", 0);
+ ck_assert(host_is_loopback(a));
+ a->destroy(a);
+
+ a = host_create_from_string("127.255.255.255", 0);
+ ck_assert(host_is_loopback(a));
+ a->destroy(a);
+
+ a = host_create_from_string("fec1::1", 0);
+ ck_assert(!host_is_loopback(a));
+ a->destroy(a);
+
+ a = host_create_from_string("::1", 0);
+ ck_assert(host_is_loopback(a));
+ a->destroy(a);
+
+ a = host_create_from_string("::2", 0);
+ ck_assert(!host_is_loopback(a));
+ a->destroy(a);
+}
+END_TEST
+
+START_TEST(test_host_is_linklocal)
+{
+ host_t *a;
+
+ ck_assert(!host_is_linklocal(NULL));
+
+ a = host_create_from_string("192.168.0.1", 0);
+ ck_assert(!host_is_linklocal(a));
+ a->destroy(a);
+
+ a = host_create_from_string("169.254.0.1", 0);
+ ck_assert(host_is_linklocal(a));
+ a->destroy(a);
+
+ a = host_create_from_string("169.254.255.255", 0);
+ ck_assert(host_is_linklocal(a));
+ a->destroy(a);
+
+ a = host_create_from_string("fec1::1", 0);
+ ck_assert(!host_is_linklocal(a));
+ a->destroy(a);
+
+ a = host_create_from_string("fe80::1", 0);
+ ck_assert(host_is_linklocal(a));
+ a->destroy(a);
+
+ a = host_create_from_string("febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 0);
+ ck_assert(host_is_linklocal(a));
+ a->destroy(a);
+}
+END_TEST
+
/*******************************************************************************
* clone
*/
@@ -790,6 +858,11 @@ Suite *host_suite_create()
tcase_add_test(tc, test_equals_any);
suite_add_tcase(s, tc);
+ tc = tcase_create("host_is_loopback/host_is_linklocal");
+ tcase_add_test(tc, test_host_is_loopback);
+ tcase_add_test(tc, test_host_is_linklocal);
+ suite_add_tcase(s, tc);
+
tc = tcase_create("clone");
tcase_add_test(tc, test_clone);
suite_add_tcase(s, tc);