]> git.kaiwu.me - haproxy.git/commitdiff
MINOR: tools: add is_ip_untrusted() to check for non-local addresses
authorWilly Tarreau <w@1wt.eu>
Tue, 3 Nov 2026 13:57:53 +0000 (14:57 +0100)
committerWilly Tarreau <w@1wt.eu>
Wed, 4 Nov 2026 21:45:32 +0000 (22:45 +0100)
The function will return true for non-local ipv4/ipv6 addresses only.
Some are between the two (benchmark, doc, examples, cgn etc) and are
intentionally reported as non-local hence untrusted as well, the
rationale being that they're normally not deployed for production so
they shouldn't be taken for trusted local addresses. This will be
used in future commits.

include/haproxy/tools.h
src/tools.c

index 40e655dbdff1181ad153615e9fe997487c85ff09..be8f5e02094254d363ad1487507b67aabe722691 100644 (file)
@@ -887,6 +887,11 @@ int ipcmp(const struct sockaddr_storage *ss1, const struct sockaddr_storage *ss2
  */
 int ipcmp2net(const struct sockaddr_storage *addr, const struct net_addr *net);
 
+/* Returns non-zero if <addr> is an AF_INET or AF_INET6 address that is not
+ * necessarily trusted (i.e. not from a local address plan), otherwise zero.
+ */
+int is_ip_untrusted(const struct sockaddr_storage *addr);
+
 /* copy ip from <source> into <dest>
  * the caller must clear <dest> before calling.
  * Returns a pointer to the destination
index f257cee0a2e195d29e613b4f9ffeb9b549e4bfa9..7e4f7b4ea4e76257d424b832b68cb2ca030eb815 100644 (file)
@@ -4016,6 +4016,90 @@ int ipcmp2net(const struct sockaddr_storage *addr, const struct net_addr *net)
        return 1;
 }
 
+/* IPv4 networks that are certainly local, hence trusted, in host order */
+static const struct {
+       uint32_t net, mask;
+} ipv4_trusted[] = {
+       { 0x00000000, 0xff000000 }, /* 0.0.0.0/8      "this" network (RFC1122) */
+       { 0x0a000000, 0xff000000 }, /* 10.0.0.0/8     private (RFC1918)        */
+       { 0x7f000000, 0xff000000 }, /* 127.0.0.0/8    loopback (RFC1122)       */
+       { 0xa9fe0000, 0xffff0000 }, /* 169.254.0.0/16 link-local (RFC3927)     */
+       { 0xac100000, 0xfff00000 }, /* 172.16.0.0/12  private (RFC1918)        */
+       { 0xc0a80000, 0xffff0000 }, /* 192.168.0.0/16 private (RFC1918)        */
+};
+
+/* IPv6 networks that are certainly local, hence trusted, applied to the upper
+ * 64 bits of the address in host order. :: and ::1 are handled separately.
+ */
+static const struct {
+       uint64_t net, mask;
+} ipv6_trusted[] = {
+       { 0xfc00000000000000ULL, 0xfe00000000000000ULL }, /* fc00::/7  unique local (RFC4193) */
+       { 0xfe80000000000000ULL, 0xffc0000000000000ULL }, /* fe80::/10 link-local (RFC4291)   */
+       { 0xfec0000000000000ULL, 0xffc0000000000000ULL }, /* fec0::/10 site-local (RFC3879)   */
+};
+
+/* Returns non-zero if <addr> is an AF_INET or AF_INET6 address that is not
+ * necessarily trusted, otherwise zero. An untrusted address is one which might
+ * possibly belong to a network out of our local authority (i.e. not loopback,
+ * link-local, site-local, rfc1918, etc). Other families (e.g. AF_UNIX) and
+ * local addresses are considered trusted. Those which are not supposed to be
+ * usable (e.g. 224/3) are considered untrusted since we don't know how they'd
+ * arrive here and might result from non-standard network setups. There's a
+ * gray area for those that are not explicitly local but shouldn't be on the
+ * public internet, such as 198.18/15 (benchmark), the documentation ones
+ * (192.0.0/24, 192.0.2/24, etc) and even CGN (100.64/10) but these ones are
+ * not supposed to be used in production on local networks, so either they are
+ * expected to reproduce a public setup and they should be seen as untrusted,
+ * or they may convey traffic from untrusted sources (CGN), so let's consider
+ * them as untrusted.
+ */
+int is_ip_untrusted(const struct sockaddr_storage *addr)
+{
+       const struct in6_addr *a6;
+       uint64_t hi, lo;
+       uint32_t v4;
+       int i;
+
+       if (addr->ss_family == AF_INET) {
+               v4 = ntohl(((const struct sockaddr_in *)addr)->sin_addr.s_addr);
+               /* fall back to network check below */
+       }
+       else if (addr->ss_family == AF_INET6) {
+               a6 = &((const struct sockaddr_in6 *)addr)->sin6_addr;
+               hi = read_n64(&a6->s6_addr[0]);
+               lo = read_n64(&a6->s6_addr[8]);
+
+               /* non-local net ? */
+               if (hi) {
+                       for (i = 0; i < sizeof(ipv6_trusted) / sizeof(*ipv6_trusted); i++)
+                               if ((hi & ipv6_trusted[i].mask) == ipv6_trusted[i].net)
+                                       return 0;
+                       /* not local hence untrusted */
+                       return 1;
+               }
+
+               if ((lo >> 32) != 0xffff) {
+                       /* :: (unspecified) and ::1 (loopback) are trusted */
+                       return lo > 1;
+               }
+
+               /* IPv4-mapped (RFC4291): ::ffff:a.b.c.d */
+               v4 = (uint32_t)lo;
+               /* fall back to network check below */
+       }
+       else {
+               /* other families are local */
+               return 0;
+       }
+
+       /* check IPv4 networks */
+       for (i = 0; i < sizeof(ipv4_trusted) / sizeof(*ipv4_trusted); i++)
+               if ((v4 & ipv4_trusted[i].mask) == ipv4_trusted[i].net)
+                       return 0;
+       return 1;
+}
+
 /* copy IP address from <source> into <dest>
  * The caller must allocate and clear <dest> before calling.
  * The source must be in either AF_INET or AF_INET6 family, or the destination