You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by am...@apache.org on 2022/11/21 20:12:52 UTC
[trafficserver] branch 10-Dev updated: libswoc: Add ATS IP address utitilies on top of libswoc. (#9209)
This is an automated email from the ASF dual-hosted git repository.
amc pushed a commit to branch 10-Dev
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/10-Dev by this push:
new 8df36ae51 libswoc: Add ATS IP address utitilies on top of libswoc. (#9209)
8df36ae51 is described below
commit 8df36ae51db5536a2b88b37c364e02316c256a90
Author: Alan M. Carroll <am...@apache.org>
AuthorDate: Mon Nov 21 14:12:47 2022 -0600
libswoc: Add ATS IP address utitilies on top of libswoc. (#9209)
libswoc: Add ATS IP address utilities on top of libswoc.
---
include/tscpp/util/Makefile.am | 1 +
include/tscpp/util/ts_ip.h | 450 +++++++++++++++++++++++++++++++++++++++++
src/tscpp/util/Makefile.am | 2 +-
src/tscpp/util/ts_ip.cc | 123 +++++++++++
4 files changed, 575 insertions(+), 1 deletion(-)
diff --git a/include/tscpp/util/Makefile.am b/include/tscpp/util/Makefile.am
index 1c9826e4c..bde4eb3d0 100644
--- a/include/tscpp/util/Makefile.am
+++ b/include/tscpp/util/Makefile.am
@@ -20,6 +20,7 @@ library_includedir=$(includedir)/tscpp/util
library_include_HEADERS = \
ts_diag_levels.h \
+ ts_ip.h \
IntrusiveDList.h \
LocalBuffer.h \
PostScript.h \
diff --git a/include/tscpp/util/ts_ip.h b/include/tscpp/util/ts_ip.h
new file mode 100644
index 000000000..2248b985d
--- /dev/null
+++ b/include/tscpp/util/ts_ip.h
@@ -0,0 +1,450 @@
+/** @file
+
+ IP address handling support.
+
+ Built on top of libswoc IP networking support to provide utilities specialized for ATS.
+
+ @section license License
+
+ Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.
+ See the NOTICE file distributed with this work for additional information regarding copyright
+ ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance with the License. You may obtain a
+ copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software distributed under the License
+ is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ or implied. See the License for the specific language governing permissions and limitations under
+ the License.
+*/
+
+#pragma once
+
+#include <optional>
+
+#include "swoc/swoc_ip.h"
+
+namespace ts
+{
+/// Pair of addresses, each optional.
+/// Used in situations where both an IPv4 and IPv6 may be needed.
+class IPAddrPair
+{
+public:
+ using self_type = IPAddrPair;
+
+ IPAddrPair() = default; ///< Default construct empty pair.
+
+ /** Construct with IPv4 address.
+ *
+ * @param a4 Address.
+ */
+ IPAddrPair(swoc::IP4Addr a4);
+
+ /** Construct with IPv6 address.
+ *
+ * @param a6 Address.
+ */
+ IPAddrPair(swoc::IP6Addr a6);
+
+ /// @return @c true if either address is present.
+ bool has_value() const;
+
+ /// @return @c true if an IPv4 address is present.
+ bool has_ip4() const;
+
+ /// @return @c true if an IPv6 address is present.
+ bool has_ip6() const;
+
+ /// @return The IPv4 address
+ /// @note Does not check if the address is present.
+ swoc::IP4Addr const &ip4() const;
+
+ /// @return The IPv6 address
+ /// @note Does not check if the address is present.
+ swoc::IP6Addr const &ip6() const;
+
+ /** Assign the IPv4 address.
+ *
+ * @param addr Address to assign.
+ * @return @a this
+ */
+ self_type &operator=(swoc::IP4Addr const &addr);
+
+ /** Assign the IPv6 address.
+ *
+ * @param addr Address to assign.
+ * @return @a this
+ */
+ self_type &operator=(swoc::IP6Addr const &addr);
+
+ /** Assign an address.
+ *
+ * @param addr Address to assign.
+ * @return @a this
+ *
+ * The appropriate internal address is assigned based on the address in @a addr.
+ */
+ self_type &operator=(swoc::IPAddr const &addr);
+
+protected:
+ std::optional<swoc::IP4Addr> _ip4;
+ std::optional<swoc::IP6Addr> _ip6;
+};
+
+inline IPAddrPair::IPAddrPair(swoc::IP4Addr a4) : _ip4(a4) {}
+inline IPAddrPair::IPAddrPair(swoc::IP6Addr a6) : _ip6(a6) {}
+
+inline bool
+IPAddrPair::has_value() const
+{
+ return _ip4.has_value() || _ip6.has_value();
+}
+
+inline bool
+IPAddrPair::has_ip4() const
+{
+ return _ip4.has_value();
+}
+
+inline bool
+IPAddrPair::has_ip6() const
+{
+ return _ip6.has_value();
+}
+
+inline swoc::IP4Addr const &
+IPAddrPair::ip4() const
+{
+ return _ip4.value();
+}
+
+inline swoc::IP6Addr const &
+IPAddrPair::ip6() const
+{
+ return _ip6.value();
+}
+
+inline auto
+IPAddrPair::operator=(swoc::IP4Addr const &addr) -> self_type &
+{
+ _ip4 = addr;
+ return *this;
+}
+
+inline auto
+IPAddrPair::operator=(swoc::IP6Addr const &addr) -> self_type &
+{
+ _ip6 = addr;
+ return *this;
+}
+
+inline auto
+IPAddrPair::operator=(swoc::IPAddr const &addr) -> self_type &
+{
+ if (addr.is_ip4()) {
+ _ip4 = addr.ip4();
+ } else if (addr.is_ip6()) {
+ _ip6 = addr.ip6();
+ }
+
+ return *this;
+}
+
+/// Pair of services, each optional.
+/// Used in situations where both IPv4 and IPv6 may be needed.
+class IPSrvPair
+{
+ using self_type = IPSrvPair;
+
+public:
+ IPSrvPair() = default; ///< Default construct empty pair.
+
+ /** Construct from address(es) and port.
+ *
+ * @param a4 IPv4 address.
+ * @param a6 IPv6 address.
+ * @param port Port
+ *
+ * @a port is used for both service instances.
+ */
+ IPSrvPair(swoc::IP4Addr const &a4, swoc::IP6Addr const &a6, in_port_t port = 0);
+
+ /** Construct from IPv4 address and optional port.
+ *
+ * @param a4 IPv4 address
+ * @param port Port.
+ */
+ IPSrvPair(swoc::IP4Addr const &a4, in_port_t port = 0);
+
+ /** Construct from IPv6 address and optional port.
+ *
+ * @param a6 IPv6 address
+ * @param port Port.
+ */
+ IPSrvPair(swoc::IP6Addr const &a6, in_port_t port = 0);
+
+ /** Construct from an address pair and optional port.
+ *
+ * @param a Address pair.
+ * @param port port.
+ *
+ * For each family the service is instantatied only if the address is present in @a a.
+ * @a port is used for all service instances.
+ */
+ explicit IPSrvPair(IPAddrPair const &a, in_port_t port = 0);
+
+ /// @return @c true if any service is present.
+ bool has_value() const;
+
+ /// @return @c true if the IPv4 service is present.
+ bool has_ip4() const;
+
+ /// @return @c true if the the IPv6 service is present.
+ bool has_ip6() const;
+
+ /// @return The IPv4 service.
+ /// @note Does not check if the service is present.
+ swoc::IP4Srv const &ip4() const;
+
+ /// @return The IPv6 service.
+ /// @note Does not check if the service is present.
+ swoc::IP6Srv const &ip6() const;
+
+ /** Assign the IPv4 service.
+ *
+ * @param srv Service to assign.
+ * @return @a this
+ */
+ self_type &operator=(swoc::IP4Srv const &srv);
+
+ /** Assign the IPv6 service.
+ *
+ * @param srv Service to assign.
+ * @return @a this
+ */
+ self_type &operator=(swoc::IP6Srv const &srv);
+
+ /** Assign a service.
+ *
+ * @param srv Service to assign.
+ * @return @a this
+ *
+ * The assigned service is the same family as @a srv.
+ */
+ self_type &operator=(swoc::IPSrv const &srv);
+
+protected:
+ std::optional<swoc::IP4Srv> _ip4;
+ std::optional<swoc::IP6Srv> _ip6;
+};
+
+inline IPSrvPair::IPSrvPair(swoc::IP4Addr const &a4, swoc::IP6Addr const &a6, in_port_t port)
+ : _ip4(swoc::IP4Srv(a4, port)), _ip6(swoc::IP6Srv(a6, port))
+{
+}
+
+inline IPSrvPair::IPSrvPair(swoc::IP4Addr const &a4, in_port_t port) : _ip4(swoc::IP4Srv(a4, port)) {}
+
+inline IPSrvPair::IPSrvPair(swoc::IP6Addr const &a6, in_port_t port) : _ip6(swoc::IP6Srv(a6, port)) {}
+
+inline IPSrvPair::IPSrvPair(IPAddrPair const &a, in_port_t port)
+{
+ if (a.has_ip4()) {
+ _ip4 = swoc::IP4Srv(a.ip4(), port);
+ }
+
+ if (a.has_ip6()) {
+ _ip6 = swoc::IP6Srv(a.ip6(), port);
+ }
+}
+
+inline bool
+IPSrvPair::has_value() const
+{
+ return _ip4.has_value() || _ip6.has_value();
+}
+
+inline bool
+IPSrvPair::has_ip4() const
+{
+ return _ip4.has_value();
+}
+
+inline bool
+IPSrvPair::has_ip6() const
+{
+ return _ip6.has_value();
+}
+
+inline swoc::IP4Srv const &
+IPSrvPair::ip4() const
+{
+ return _ip4.value();
+}
+
+inline swoc::IP6Srv const &
+IPSrvPair::ip6() const
+{
+ return _ip6.value();
+}
+
+inline auto
+IPSrvPair::operator=(swoc::IP4Srv const &srv) -> self_type &
+{
+ _ip4 = srv;
+ return *this;
+}
+
+inline auto
+IPSrvPair::operator=(swoc::IP6Srv const &srv) -> self_type &
+{
+ _ip6 = srv;
+ return *this;
+}
+
+inline auto
+IPSrvPair::operator=(swoc::IPSrv const &srv) -> self_type &
+{
+ if (srv.is_ip4()) {
+ _ip4 = srv.ip4();
+ } else if (srv.is_ip6()) {
+ _ip6 = srv.ip6();
+ }
+ return *this;
+}
+
+/** Get the best address info for @a name.
+
+ * @param name Address / host.
+ * @return An address pair.
+ *
+ * If @a name is a valid IP address it is interpreted as such. Otherwise it is presumed
+ * to be a host name suitable for resolution using @c getaddrinfo. The "best" address is
+ * selected by ranking the types of addresses in the order
+ *
+ * - Global, multi-cast, non-routable (private), link local, loopback
+ *
+ * For a host name, an IPv4 and IPv6 address may be returned. The "best" is computed independently
+ * for each family.
+ *
+ * @see getaddrinfo
+ * @see ts::getbestsrvinfo
+ */
+IPAddrPair getbestaddrinfo(swoc::TextView name);
+
+/** Get the best address and port info for @a name.
+
+ * @param name Address / host.
+ * @return An address pair.
+ *
+ * If @a name is a valid IP address (with optional port) it is interpreted as such. Otherwise it is
+ * presumed to be a host name (with optional port) suitable for resolution using @c getaddrinfo. The "best" address is
+ * selected by ranking the types of addresses in the order
+ *
+ * - Global, multi-cast, non-routable (private), link local, loopback
+ *
+ * For a host name, an IPv4 and IPv6 service may be returned. The "best" is computed independently
+ * for each family. The port, if present, is the same for all returned services.
+ *
+ * @see getaddrinfo
+ * @see ts::getbestaddrinfo
+ */
+IPSrvPair getbestsrvinfo(swoc::TextView name);
+
+/** An IPSpace that contains only addresses.
+ *
+ * This is to @c IPSpace as @c std::set is to @c std::map. The @c value_type is removed from the API
+ * and only the keys are visible. This suits use cases where the goal is to track the presence of
+ * addresses without any additional data.
+ *
+ * @note Because there is only one value stored, there is no difference between @c mark and @c fill.
+ */
+class IPAddrSet
+{
+ using self_type = IPAddrSet;
+
+public:
+ /// Default construct empty set.
+ IPAddrSet() = default;
+
+ /** Add addresses to the set.
+ *
+ * @param r Range of addresses to add.
+ * @return @a this
+ *
+ * Identical to @c fill.
+ */
+ self_type &mark(swoc::IPRange const &r);
+
+ /** Add addresses to the set.
+ *
+ * @param r Range of addresses to add.
+ * @return @a this
+ *
+ * Identical to @c mark.
+ */
+ self_type &fill(swoc::IPRange const &r);
+
+ /// @return @c true if @a addr is in the set.
+ bool contains(swoc::IPAddr const &addr) const;
+
+ /// @return Number of ranges in the set.
+ size_t count() const;
+
+protected:
+ /// Empty struct to use for payload.
+ /// This declares the struct and defines the singleton instance used.
+ static inline constexpr struct Mark {
+ using self_type = Mark;
+ /// @internal @c IPSpace requires equality / inequality operators.
+ /// These make all instance equal to each other.
+ bool operator==(self_type const &that);
+ bool operator!=(self_type const &that);
+ } MARK{};
+
+ /// The address set.
+ swoc::IPSpace<Mark> _addrs;
+};
+
+inline auto
+IPAddrSet::mark(swoc::IPRange const &r) -> self_type &
+{
+ _addrs.mark(r, MARK);
+ return *this;
+}
+
+inline auto
+IPAddrSet::fill(swoc::IPRange const &r) -> self_type &
+{
+ _addrs.mark(r, MARK);
+ return *this;
+}
+
+inline bool
+IPAddrSet::contains(swoc::IPAddr const &addr) const
+{
+ return _addrs.find(addr) != _addrs.end();
+}
+
+inline size_t
+IPAddrSet::count() const
+{
+ return _addrs.count();
+}
+
+inline bool
+IPAddrSet::Mark::operator==(IPAddrSet::Mark::self_type const &that)
+{
+ return true;
+}
+
+inline bool
+IPAddrSet::Mark::operator!=(IPAddrSet::Mark::self_type const &that)
+{
+ return false;
+}
+
+} // namespace ts
diff --git a/src/tscpp/util/Makefile.am b/src/tscpp/util/Makefile.am
index d018e4956..66dffc2cc 100644
--- a/src/tscpp/util/Makefile.am
+++ b/src/tscpp/util/Makefile.am
@@ -30,7 +30,7 @@ libtscpputil_la_LDFLAGS = @AM_LDFLAGS@ -no-undefined -version-info @TS_LIBTOOL_V
libtscpputil_la_LIBADD = @SWOC_LIBS@
libtscpputil_la_SOURCES = \
- ts_diags.cc TextView.cc
+ ts_diags.cc ts_ip.cc TextView.cc
test_tscpputil_CPPFLAGS = $(AM_CPPFLAGS)\
-I$(abs_top_srcdir)/tests/include @SWOC_INCLUDES@
diff --git a/src/tscpp/util/ts_ip.cc b/src/tscpp/util/ts_ip.cc
new file mode 100644
index 000000000..01e8ae8dc
--- /dev/null
+++ b/src/tscpp/util/ts_ip.cc
@@ -0,0 +1,123 @@
+/** @file
+
+ IP address handling support.
+
+ @section license License
+
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with this
+ work for additional information regarding copyright ownership. The ASF
+ licenses this file to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ License for the specific language governing permissions and limitations under
+ the License.
+*/
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#include <tscore/ink_memory.h>
+
+#include "tscpp/util/ts_ip.h"
+
+namespace ts
+{
+IPAddrPair
+getbestaddrinfo(swoc::TextView name)
+{
+ // If @a name parses as a valid address, return it as that address.
+
+ if (swoc::IP4Addr addr; addr.load(name)) {
+ return addr;
+ }
+
+ if (swoc::IP6Addr addr; addr.load(name)) {
+ return addr;
+ }
+
+ // Presume it is a host name, make a copy to guarantee C string.
+ char *tmp = static_cast<char *>(alloca(name.size() + 1));
+ memcpy(tmp, name.data(), name.size());
+ tmp[name.size()] = 0;
+ name.assign(tmp, name.size());
+
+ // List of address types, in order of worst to best.
+ enum {
+ NA, // Not an (IP) Address.
+ LO, // Loopback.
+ LL, // Link Local.
+ PR, // Private.
+ MC, // Multicast.
+ GL // Global.
+ } spot_type = NA,
+ ip4_type = NA, ip6_type = NA;
+ addrinfo ai_hints;
+ addrinfo *ai_result;
+ IPAddrPair zret;
+
+ // Do the resolution
+ ink_zero(ai_hints);
+ ai_hints.ai_family = AF_UNSPEC;
+ ai_hints.ai_flags = AI_ADDRCONFIG;
+
+ if (0 == getaddrinfo(name.data(), nullptr, &ai_hints, &ai_result)) { // Walk the returned addresses and pick the "best".
+ for (addrinfo *ai_spot = ai_result; ai_spot; ai_spot = ai_spot->ai_next) {
+ swoc::IPAddr addr(ai_spot->ai_addr);
+ if (!addr.is_valid()) {
+ spot_type = NA;
+ } else if (addr.is_loopback()) {
+ spot_type = LO;
+ } else if (addr.is_link_local()) {
+ spot_type = LL;
+ } else if (addr.is_private()) {
+ spot_type = PR;
+ } else if (addr.is_multicast()) {
+ spot_type = MC;
+ } else {
+ spot_type = GL;
+ }
+
+ if (spot_type == NA) {
+ continue; // Next!
+ }
+
+ if (addr.is_ip4()) {
+ if (spot_type > ip4_type) {
+ zret = addr.ip4();
+ ip4_type = spot_type;
+ }
+ } else if (addr.is_ip6()) {
+ if (spot_type > ip6_type) {
+ zret = addr.ip6();
+ ip6_type = spot_type;
+ }
+ }
+ }
+
+ freeaddrinfo(ai_result); // free *after* the copy.
+ }
+
+ return zret;
+}
+
+IPSrvPair
+getbestsrvinfo(swoc::TextView src)
+{
+ swoc::TextView addr_text;
+ swoc::TextView port_text;
+ if (swoc::IPEndpoint::tokenize(src, &addr_text, &port_text)) {
+ in_port_t port = swoc::svtoi(port_text);
+ return IPSrvPair{ts::getbestaddrinfo(addr_text), port};
+ }
+ return {};
+}
+
+} // namespace ts