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 2010/10/12 18:10:48 UTC
svn commit: r1021825 [2/4] - in /trafficserver/traffic/branches/wccp: ./
libinktomi++/ proxy/ proxy/mgmt2/ proxy/mgmt2/api2/include/ proxy/wccp/
Added: trafficserver/traffic/branches/wccp/proxy/wccp/Makefile.am
URL: http://svn.apache.org/viewvc/trafficserver/traffic/branches/wccp/proxy/wccp/Makefile.am?rev=1021825&view=auto
==============================================================================
--- trafficserver/traffic/branches/wccp/proxy/wccp/Makefile.am (added)
+++ trafficserver/traffic/branches/wccp/proxy/wccp/Makefile.am Tue Oct 12 16:10:47 2010
@@ -0,0 +1,48 @@
+#
+# Makefile.am for WCCP module.
+#
+# 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.
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/proxy/mgmt2/api2/include \
+ -DPKGSYSUSER=\"$(pkgsysuser)\"
+
+#WCCP_DEFS = @WCCP_DEFS@
+#DEFS += $(WCCP_DEFS)
+
+noinst_LIBRARIES = libwccp.a
+noinst_PROGRAMS = test-cache
+
+libwccp_a_SOURCES = \
+ Wccp.h \
+ WccpLocal.h \
+ WccpStatic.cc \
+ WccpMsg.cc \
+ WccpEndPoint.cc \
+ WccpConfig.cc \
+ Errata.h \
+ Errata.cc \
+ AtsBase.h \
+ AtsBaseStatic.cc \
+ AtsMeta.h \
+ NumericType.h \
+ IntrusivePtr.h
+
+test_cache_SOURCES = \
+ wccp-test-cache.cc
+
+test_cache_LDADD = ./libwccp.a -lconfig++ -lcrypto
\ No newline at end of file
Added: trafficserver/traffic/branches/wccp/proxy/wccp/Wccp.h
URL: http://svn.apache.org/viewvc/trafficserver/traffic/branches/wccp/proxy/wccp/Wccp.h?rev=1021825&view=auto
==============================================================================
--- trafficserver/traffic/branches/wccp/proxy/wccp/Wccp.h (added)
+++ trafficserver/traffic/branches/wccp/proxy/wccp/Wccp.h Tue Oct 12 16:10:47 2010
@@ -0,0 +1,482 @@
+# if ! defined(ATS_WCCP_API_H)
+# define ATS_WCCP_API_H
+
+/** @file
+ WCCP (v2) support API.
+ Original implementation: Alan M. Carroll
+ Sponsored by: Pavlov Media
+ */
+
+# include "AtsBase.h"
+# include "IntrusivePtr.h"
+# include "Errata.h"
+
+# include <memory.h>
+
+// INADDR_ANY
+# include <netinet/in.h>
+
+/// WCCP Support.
+namespace Wccp {
+using namespace ats::fixed_integers;
+
+/// Forward declare implementation classes.
+class Impl;
+class CacheImpl;
+class RouterImpl;
+
+/// Namespace for implementation details.
+namespace detail {
+ /// Cache implementation details.
+ namespace cache {
+ class GroupData;
+ }
+ namespace router {
+ class GroupData;
+ }
+}
+
+/// Basic time unit for WCCP in seconds
+/// @note Sec 4.14: HERE_I_AM_T.
+static time_t const TIME_UNIT = 10;
+static time_t const ASSIGN_WAIT = (3 * TIME_UNIT) / 2;
+static time_t const RAPID_TIME = TIME_UNIT/10;
+
+/// Service group related constants.
+/// @internal In a struct so enum values can be imported to more than
+/// one class.
+struct ServiceConstants {
+ /// Methods for forwarding intercepted packets to cache.
+ /// @internal Enumerations values match protocol values.
+ enum PacketStyle {
+ NO_PACKET_STYLE = 0, ///< Undefined or invalid.
+ GRE = 1, ///< GRE tunnel only. [default]
+ L2 = 2, ///< L2 rewrite only.
+ GRE_OR_L2 = 3 ///< L2 rewrite or GRE tunnel.
+ };
+
+ /// Cache assignment supported methods.
+ /// @internal Enumerations values match protocol values.
+ enum CacheAssignmentStyle {
+ NO_CACHE_ASSIGN_STYLE = 0, ///< Undefined or invalid.
+ HASH_ONLY = 1, ///< Use only hash assignment. [default]
+ MASK_ONLY = 2, ///< Use only mask assignment.
+ HASH_OR_MASK = 3 ///< Use hash or mask assignment.
+ };
+};
+
+/** Service group definition.
+
+ Also used as serialized layout internally by ServiceGroupElt. This is kept
+ in serialized form because it is copied to and from message data far more
+ often then it is accessed directly.
+ */
+class ServiceGroup : public ServiceConstants {
+public:
+ typedef ServiceGroup self; ///< Self reference type.
+
+ /// Type of service.
+ enum Type {
+ STANDARD = 0, ///< Well known service.
+ DYNAMIC = 1 ///< Dynamic (locally defined) service.
+ };
+
+ /// Result codes for service definition.
+ enum Result {
+ DEFINED = 0, ///< Service group was created by the call.
+ EXISTS = 1, ///< Service group already existed.
+ CONFLICT = 2 ///< Service group existed but didn't match new definition.
+ };
+
+ /// @name Well known (standard) services.
+ //@{
+ /// HTTP
+ static uint8 const HTTP = 0;
+ //@}
+ /// Service IDs of this value or less are reserved.
+ static uint8 const RESERVED = 50;
+
+ /// Number of ports in component (defined by protocol).
+ static int const N_PORTS = 8;
+
+ /// @name Flag mask values.
+ //@{
+ /// Source IP address hash
+ static uint32 const SRC_IP_HASH = 1<<0;
+ /// Destination IP address hash
+ static uint32 const DST_IP_HASH = 1<<1;
+ /// Source port hash.
+ static uint32 const SRC_PORT_HASH = 1<<2;
+ /// Destination port hash
+ static uint32 const DST_PORT_HASH = 1<<3;
+ /// @a m_ports has port information.
+ static uint32 const PORTS_DEFINED = 1<<4;
+ /// @a m_ports has source ports (otherwise destination ports).
+ static uint32 const PORTS_SOURCE = 1<<5;
+ /// Alternate source IP address hash
+ static uint32 const SRC_IP_ALT_HASH = 1<<8;
+ /// Alternate destination IP address hash
+ static uint32 const DST_IP_ALT_HASH = 1<<9;
+ /// Alternate source port hash
+ static uint32 const SRC_PORT_ALT_HASH = 1<<10;
+ /// Alternate destination port hash
+ static uint32 const DST_PORT_ALT_HASH = 1<<11;
+ //@}
+
+ /// Default constructor - no member initialization.
+ ServiceGroup();
+ /// Test for equivalent.
+ bool operator == (self const& that) const;
+ /// Test for not equivalent.
+ bool operator != (self const& that) const;
+
+ /// @name Accessors
+ //@{
+ ServiceGroup::Type getSvcType() const; ///< Get service type field.
+ /** Set the service type.
+ If @a svc is @c SERVICE_STANDARD then all fields except the
+ component header and service id are set to zero as required
+ by the protocol.
+ */
+ self& setSvcType(ServiceGroup::Type svc);
+
+ uint8 getSvcId() const; ///< Get service ID field.
+ self& setSvcId(uint8 id); ///< Set service ID field to @a id.
+
+ uint8 getPriority() const; ///< Get priority field.
+ self& setPriority(uint8 pri); ///< Set priority field to @a p.
+
+ uint8 getProtocol() const; ///< Get protocol field.
+ self& setProtocol(uint8 p); ///< Set protocol field to @a p.
+
+ uint32 getFlags() const; ///< Get flags field.
+ self& setFlags(uint32 f); ///< Set the flags flags in field to @a f.
+ /// Set the flags in the flag field that are set in @a f.
+ /// Other flags are unchanged.
+ self& enableFlags(uint32 f);
+ /// Clear the flags in the flag field that are set in @a f.
+ /// Other flags are unchanged.
+ self& disableFlags(uint32 f);
+
+ /// Get a port value.
+ uint16 getPort(
+ int idx ///< Index of target port.
+ ) const;
+ /// Set a port value.
+ self& setPort(
+ int idx, ///< Index of port.
+ uint16 port ///< Value for port.
+ );
+ /// Zero (clear) all ports.
+ self& clearPorts();
+ //@}
+
+protected:
+ uint8 m_svc_type; ///< @ref Type.
+ uint8 m_svc_id; ///< ID for service type.
+ uint8 m_priority; ///< Redirection priority ordering.
+ uint8 m_protocol; ///< IP protocol for service.
+ uint32 m_flags; ///< Flags.
+ uint16 m_ports[N_PORTS]; ///< Service ports.
+};
+
+/// Security component option (sub-type)
+enum SecurityOption {
+ SECURITY_NONE = 0, ///< No security @c WCCP2_NO_SECURITY
+ SECURITY_MD5 = 1 ///< MD5 security @c WCCP2_MD5_SECURITY
+};
+
+class EndPoint {
+public:
+ typedef EndPoint self; ///< Self reference type.
+ typedef Impl ImplType; ///< Implementation type.
+
+ /** Set the identifying IP address.
+ This is also used as the address for the socket.
+ */
+ self& setAddr(
+ uint32 addr ///< IP address.
+ );
+
+ /** Check if this endpoint is ready to use.
+ @return @c true if the address has been set and services
+ have been added.
+ */
+ bool isConfigured() const;
+
+ /** Open a socket for communications.
+
+ If @a addr is @c INADDR_ANY then the identifying address is used.
+ If that is not set this method will attempt to find an arbitrary
+ local address and use that as the identifying address.
+
+ Otherwise @a addr replaces any previously set address.
+
+ @return 0 on success, -ERRNO on failure.
+ @see setAddr
+ */
+ int open(
+ uint32 addr = INADDR_ANY ///< Local IP address for socket.
+ );
+
+ /// Get the internal socket.
+ /// Useful primarily for socket options and using
+ /// in @c select.
+ int getSocket() const;
+
+ /// Use MD5 based security, specifying the @a key.
+ void useMD5Security(
+ char const* key ///< Shared hash key.
+ );
+
+ /// Perform house keeping, including sending outbound messages.
+ int housekeeping();
+
+ /// Recieve and process a message on the socket.
+ /// @return 0 for success, -ERRNO on system error.
+ ats::Rv<int> handleMessage();
+
+protected:
+ /// Default constructor.
+ EndPoint();
+ /// Copy constructor.
+ EndPoint(self const& that);
+ /// Force virtual destructor
+ virtual ~EndPoint();
+
+ ats::IntrusivePtr<ImplType> m_ptr; ///< Implementation instance.
+
+ /** Get a pointer to the implementation instance, creating it if needed.
+ @internal This is paired with @c make so that the implementation check
+ can be done non-virtually inline, while still allowing the actual
+ implementation instantiation to be virtual so the correct type is
+ created.
+ */
+ ImplType* instance();
+
+ virtual ImplType* make() = 0; ///< Create a new implementation instance.
+};
+
+class Cache : public EndPoint {
+public:
+ typedef Cache self; ///< Self reference type.
+ typedef EndPoint super; ///< Parent type.
+ typedef CacheImpl ImplType; ///< Implementation type.
+
+ class Service;
+
+ /// Default constructor.
+ Cache();
+ /// Destructor
+ ~Cache();
+
+ /// Define services from a configuration file.
+ ats::Errata loadServicesFromFile(
+ char const* path ///< Path to file.
+ );
+
+ /** Define a service group.
+
+ Return a service reference object which references the group.
+
+ If @a result is not @c NULL then its target is set to
+ - @c ServiceGroup::DEFINED if the service was created.
+ - @c ServiceGroup::EXISTS if the service matches the existing service.
+ - @c ServiceGroup::CONFLICT if the service doesn't match the existing service.
+ */
+ Service defineServiceGroup(
+ ServiceGroup const& svc, ///< Service group description.
+ ServiceGroup::Result* result = 0
+ );
+
+ /** Add a seed router to the service group.
+
+ A seed router is one that is defined at start up and is where
+ initial messages will be sent. Other routers will be added as
+ discovered. The protocol cannot start successfully without at
+ least one seed router.
+
+ Seed routers are removed when a reply is received from that router.
+
+ */
+ self& addSeedRouter(
+ uint8 id, ///< Service group ID.
+ uint32 addr ///< IP address of router.
+ );
+
+ /// Number of seconds until next housekeeping activity is due.
+ time_t waitTime() const;
+protected:
+ /// Get implementation instance, creating if needed.
+ ImplType* instance();
+ /// Get the current implementation instance cast to correct type.
+ ImplType* impl();
+ /// Get the current implementation instance cast to correct type.
+ ImplType const* impl() const;
+ /// Create a new implementation instance.
+ super::ImplType* make();
+};
+
+/** Hold a reference to a service group in this end point.
+ This is useful when multiple operations are to be done on the
+ same group, rather than doing a lookup by id every time.
+*/
+class Cache::Service : public ServiceConstants {
+public:
+ typedef Service self; ///< Self reference type.
+
+ /// Default constructor (invalid reference).
+ Service();
+
+ /// Add an address for a seed router.
+ self& addSeedRouter(
+ uint32 addr ///< Router IP address.
+ );
+ /// Set the security key.
+ self& setKey(
+ char const* key /// Shared key.
+ );
+ /// Set the service local security option.
+ self& setSecurity(
+ SecurityOption opt ///< Security style to use.
+ );
+ /// Set intercepted packet forwarding style.
+ self& setForwarding(
+ PacketStyle style ///< Type of forwarding supported.
+ );
+ /// Enable or disable packet return by layer 2 rewrite.
+ self& setReturn(
+ PacketStyle style ///< Type of return supported.
+ );
+
+ /// Set cache assignment style.
+ self& setCacheAssignment(
+ CacheAssignmentStyle style ///< Style to use.
+ );
+
+
+private:
+ Service(Cache const& cache, detail::cache::GroupData& group);
+ Cache m_cache; ///< Parent cache.
+ detail::cache::GroupData* m_group; ///< Service Group data.
+ friend class Cache;
+};
+
+
+class Router : public EndPoint {
+public:
+ typedef Router self; ///< Self reference type.
+ typedef EndPoint super; ///< Parent type.
+ typedef RouterImpl ImplType; ///< Implementation type.
+
+ /// Default constructor
+ Router();
+ /// Destructor.
+ ~Router();
+
+ /// Transmit pending messages.
+ int sendPendingMessages();
+protected:
+ /// Get implementation instance, creating if needed.
+ ImplType* instance();
+ /// Get the current implementation instance cast to correct type.
+ ImplType* impl();
+ /// Create a new implementation instance.
+ super::ImplType* make();
+};
+
+// ------------------------------------------------------
+inline bool
+ServiceGroup::operator != (self const& that) const {
+ return !(*this == that);
+}
+
+inline ServiceGroup::Type
+ServiceGroup::getSvcType() const {
+ return static_cast<ServiceGroup::Type>(m_svc_type);
+}
+
+inline uint8_t
+ServiceGroup::getSvcId() const {
+ return m_svc_id;
+}
+
+inline ServiceGroup&
+ServiceGroup::setSvcId(uint8_t id) {
+ m_svc_id = id;
+ return *this;
+}
+
+inline uint8_t
+ServiceGroup::getPriority() const {
+ return m_priority;
+}
+
+inline ServiceGroup&
+ServiceGroup::setPriority(uint8_t pri) {
+ m_priority = pri;
+ return *this;
+}
+
+inline uint8_t
+ServiceGroup::getProtocol() const {
+ return m_protocol;
+}
+
+inline ServiceGroup&
+ServiceGroup::setProtocol(uint8_t proto) {
+ m_protocol = proto;
+ return *this;
+}
+
+inline uint32
+ServiceGroup::getFlags() const {
+ return ntohl(m_flags);
+}
+
+inline ServiceGroup&
+ServiceGroup::setFlags(uint32 flags) {
+ m_flags = htonl(flags);
+ return *this;
+}
+
+inline ServiceGroup&
+ServiceGroup::enableFlags(uint32 flags) {
+ m_flags |= htonl(flags);
+ return *this;
+}
+
+inline ServiceGroup&
+ServiceGroup::disableFlags(uint32 flags) {
+ m_flags &= ~htonl(flags);
+ return *this;
+}
+
+inline uint16_t
+ServiceGroup::getPort (int idx) const {
+ return ntohs(m_ports[idx]);
+}
+
+inline ServiceGroup&
+ServiceGroup::setPort(int idx, uint16_t port) {
+ m_ports[idx] = htons(port);
+ return *this;
+}
+
+inline ServiceGroup&
+ServiceGroup::clearPorts() {
+ memset(m_ports, 0, sizeof(m_ports));
+ return *this;
+}
+
+inline Cache::Service::Service() : m_group(0) { }
+
+inline Cache::Service::Service(
+ Cache const& cache,
+ detail::cache::GroupData& group
+) : m_cache(cache), m_group(&group) {
+}
+// ------------------------------------------------------
+
+} // namespace Wccp
+# endif // Include guard
Added: trafficserver/traffic/branches/wccp/proxy/wccp/WccpConfig.cc
URL: http://svn.apache.org/viewvc/trafficserver/traffic/branches/wccp/proxy/wccp/WccpConfig.cc?rev=1021825&view=auto
==============================================================================
--- trafficserver/traffic/branches/wccp/proxy/wccp/WccpConfig.cc (added)
+++ trafficserver/traffic/branches/wccp/proxy/wccp/WccpConfig.cc Tue Oct 12 16:10:47 2010
@@ -0,0 +1,777 @@
+# include "WccpLocal.h"
+# include <sstream>
+# include <libconfig.h++>
+# include <arpa/inet.h>
+# include <iostream>
+# include <errno.h>
+
+// Support that must go in the standard namespace.
+namespace std {
+ostream& operator << ( ostream& s, libconfig::Setting::Type const& t ) {
+ switch (t) {
+ case libconfig::Setting::TypeString: s << "string"; break;
+ case libconfig::Setting::TypeInt: s << "integer"; break;
+ case libconfig::Setting::TypeInt64: s << "integer (64 bit)"; break;
+ case libconfig::Setting::TypeFloat: s << "floating point"; break;
+ case libconfig::Setting::TypeBoolean: s << "boolean"; break;
+ case libconfig::Setting::TypeArray: s << "array"; break;
+ case libconfig::Setting::TypeList: s << "list"; break;
+ case libconfig::Setting::TypeGroup: s << "group"; break;
+ default: s << "*unknown*"; break;
+ };
+ return s;
+}
+} // namespace std
+
+// WCCP related things that are file local.
+namespace {
+using namespace Wccp;
+
+// Scratch global list of seed router addresses.
+// Yeah, not thread safe, but it's just during configuration load.
+std::vector<ats::uint32> Seed_Router;
+
+// Names used for various elements and properties.
+static char const * const SVC_NAME = "service";
+
+static char const * const SVC_PROP_ID = "id";
+static char const * const SVC_PROP_TYPE = "type";
+static char const * const SVC_PROP_PRIORITY = "priority";
+static char const * const SVC_PROP_PROTOCOL = "protocol";
+static char const * const SVC_PROP_FLAGS = "flags";
+static char const * const SVC_PROP_PRIMARY_HASH = "primary-hash";
+static char const * const SVC_PROP_ALT_HASH = "alt-hash";
+static char const * const SVC_PROP_PORTS = "ports";
+static char const * const SVC_PROP_PORT_TYPE = "port-type";
+static char const * const SVC_PROP_SECURITY = "security";
+static char const * const SVC_PROP_ROUTERS = "routers";
+static char const * const SVC_PROP_FORWARD = "forward";
+static char const * const SVC_PROP_RETURN = "return";
+static char const * const SVC_PROP_ASSIGN = "assignment";
+
+static char const * const SECURITY_PROP_OPTION = "option";
+static char const * const SECURITY_PROP_KEY = "key";
+
+/// Helper structure for processing configuration strings.
+struct CfgString {
+ char const* m_text; ///< Text value of the option.
+ bool m_found; ///< String was found.
+};
+typedef std::vector<CfgString> CfgOpts;
+
+# define N_OPTS(x) (sizeof(x) / sizeof(*x))
+
+CfgString FORWARD_OPTS[] = { { "gre" } , { "l2" } };
+size_t const N_FORWARD_OPTS = sizeof(FORWARD_OPTS)/sizeof(*FORWARD_OPTS);
+
+CfgString RETURN_OPTS[] = { { "gre" } , { "l2" } };
+size_t const N_RETURN_OPTS = sizeof(RETURN_OPTS)/sizeof(*RETURN_OPTS);
+
+CfgString ASSIGN_OPTS[] = { { "hash" } , { "mask" } };
+
+CfgString HASH_OPTS[] = { { "src_ip" } , { "dst_ip" } , { "src_port" } , { "dst_port" } };
+
+ats::Errata::Code code_max(ats::Errata const& err) {
+ ats::Errata::Code zret = std::numeric_limits<ats::Errata::Code::raw_type>::min();
+ ats::Errata::const_iterator spot = err.begin();
+ ats::Errata::const_iterator limit = err.end();
+ for ( ; spot != limit ; ++spot )
+ zret = std::max(zret, spot->getCode());
+ return zret;
+}
+
+ats::Errata::Message File_Syntax_Error(int line, char const* text) {
+ std::ostringstream out;
+ out << "Service configuration error. Line "
+ << line
+ << ": " << text
+ ;
+ return ats::Errata::Message(1, LVL_FATAL, out.str());
+}
+
+ats::Errata::Message File_Read_Error(char const* text) {
+ std::ostringstream out;
+ out << "Failed to parse configuration file."
+ << ": " << text
+ ;
+ return ats::Errata::Message(2, LVL_FATAL, out.str());
+}
+
+ats::Errata::Message Unable_To_Create_Service_Group(int line) {
+ std::ostringstream out;
+ out << "Unable to create service group at line " << line
+ << " because of configuration errors."
+ ;
+ return ats::Errata::Message(23, LVL_FATAL, out.str());
+}
+
+ats::Errata::Message Services_Not_Found() {
+ return ats::Errata::Message(3, LVL_INFO, "No services found in configuration.");
+}
+
+ats::Errata::Message Services_Not_A_Sequence() {
+ return ats::Errata::Message(4, LVL_INFO, "The 'services' setting was not a list nor array.");
+}
+
+ats::Errata::Message Service_Not_A_Group(int line) {
+ std::ostringstream out;
+ out << "'" << SVC_NAME << "' must be a group at line "
+ << line << "."
+ ;
+ return ats::Errata::Message(5, LVL_WARN, out.str());
+}
+
+ats::Errata::Message Service_Type_Defaulted(Wccp::ServiceGroup::Type type, int line) {
+ std::ostringstream out;
+ out << "'type' not found in " << SVC_NAME << " at line "
+ << line << "' -- defaulting to "
+ << ( type == Wccp::ServiceGroup::STANDARD ? "STANDARD" : "DYNAMIC" )
+ ;
+ return ats::Errata::Message(6, LVL_INFO, out.str());
+}
+
+ats::Errata::Message Service_Type_Invalid(char const* text, int line) {
+ std::ostringstream out;
+ out << "Service type '" << text
+ << "' at line " << line
+ << " invalid. Must be \"STANDARD\" or \"DYNAMIC\""
+ ;
+ return ats::Errata::Message(7, LVL_WARN, out.str());
+}
+
+ats::Errata::Message Prop_Not_Found(char const* prop_name, char const* group_name, int line) {
+ std::ostringstream out;
+ out << "Required '" << prop_name << "' property not found in '"
+ << group_name << "' at line " << line << "."
+ ;
+ return ats::Errata::Message(8, LVL_WARN, out.str());
+}
+
+ats::Errata::Message Prop_Invalid_Type(
+ libconfig::Setting& prop_cfg,
+ libconfig::Setting::Type expected
+) {
+ std::ostringstream out;
+ out << "'" << prop_cfg.getName() << "' at line " << prop_cfg.getSourceLine()
+ << " is of type '" << prop_cfg.getType()
+ << "' instead of required type '" << expected << "'."
+ ;
+ return ats::Errata::Message(9, LVL_WARN, out.str());
+}
+
+ats::Errata::Message Prop_List_Invalid_Type(
+ libconfig::Setting& elt_cfg, ///< List element.
+ libconfig::Setting::Type expected
+) {
+ std::ostringstream out;
+ out << "Element ";
+ if (elt_cfg.getName()) out << "'" << elt_cfg.getName() << "'";
+ else out << elt_cfg.getIndex();
+ out << " at line " << elt_cfg.getSourceLine()
+ << " in the aggregate property '" << elt_cfg.getParent().getName()
+ << "' is of type '" << elt_cfg.getType()
+ << "' instead of required type '" << expected << "'."
+ ;
+ return ats::Errata::Message(9, LVL_WARN, out.str());
+}
+
+ats::Errata::Message Svc_Prop_Out_Of_Range(
+ char const* name,
+ libconfig::Setting& elt_cfg,
+ int v, int min, int max
+) {
+ std::ostringstream out;
+ out << "Service property '" << name
+ << "' at line " << elt_cfg.getSourceLine()
+ << " has a value " << v
+ << " that is not in the allowed range of "
+ << min << ".." << max << "."
+ ;
+ return ats::Errata::Message(10, LVL_WARN, out.str());
+}
+
+ats::Errata::Message Svc_Prop_Ignored(char const* name, int line) {
+ std::ostringstream out;
+ out << "Service property '" << name << "' at line " << line
+ << " ignored because the service is of type standard."
+ ;
+ return ats::Errata::Message(11, LVL_INFO, out.str());
+}
+
+ats::Errata::Message Svc_Flags_No_Hash_Set(int line) {
+ std::ostringstream out;
+ out << "Service flags have no hash set at line " << line
+ ;
+ return ats::Errata::Message(12, LVL_WARN, out.str());
+}
+
+ats::Errata::Message Svc_Flags_Ignored(int line) {
+ std::ostringstream out;
+ out << "Invalid service flags at line " << line
+ << " ignored."
+ ;
+ return ats::Errata::Message(13, LVL_INFO, out.str());
+}
+
+ats::Errata::Message Svc_Ports_Too_Many(int line, int n) {
+ std::ostringstream out;
+ out << "Excess ports ignored at line " << line
+ << ". " << n << " ports specified, only"
+ << Wccp::ServiceGroup::N_PORTS << " supported."
+ ;
+ return ats::Errata::Message(14, LVL_INFO, out.str());
+}
+
+ats::Errata::Message Svc_Ports_Malformed(int line) {
+ std::ostringstream out;
+ out << "Port value ignored (not a number) at line " << line
+ << "."
+ ;
+ return ats::Errata::Message(15, LVL_INFO, out.str());
+}
+
+ats::Errata::Message Svc_Ports_None_Valid(int line) {
+ std::ostringstream out;
+ out << "A '" << SVC_PROP_PORTS << "' property was found at line "
+ << line << " but none of the ports were valid."
+ ;
+ return ats::Errata::Message(17, LVL_WARN, out.str());
+}
+
+ats::Errata::Message Svc_Ports_Not_Found(int line) {
+ std::ostringstream out;
+ out << "Ports not found in service at line " << line
+ << ". Ports must be defined for a dynamic service.";
+ return ats::Errata::Message(18, LVL_WARN, out.str());
+}
+
+ats::Errata::Message Svc_Prop_Ignored_In_Standard(const char* name, int line) {
+ std::ostringstream out;
+ out << "Service property '" << name << "' at line " << line
+ << " ignored because the service is of type STANDARD."
+ ;
+ return ats::Errata::Message(19, LVL_INFO, out.str());
+}
+
+ats::Errata::Message Security_Opt_Invalid(char const* text, int line) {
+ std::ostringstream out;
+ out << "Security option '" << text
+ << "' at line " << line
+ << " is invalid. It must be 'none' or 'md5'."
+ ;
+ return ats::Errata::Message(20, LVL_WARN, out.str());
+}
+
+ats::Errata::Message Value_Malformed(char const* name, char const* text, int line) {
+ std::ostringstream out;
+ out << "'" << name << "' value '" << text
+ << "' malformed at line " << line << "."
+ ;
+ return ats::Errata::Message(21, LVL_WARN, out.str());
+}
+
+ats::Errata::Message No_Valid_Routers(int line) {
+ std::ostringstream out;
+ out << "No valid IP address for routers found for Service Group at line "
+ << line << "."
+ ;
+ return ats::Errata::Message(22, LVL_WARN, out.str());
+}
+
+ats::Errata::Message Ignored_Option_Value(
+ char const* text,
+ char const* name,
+ int line
+) {
+ std::ostringstream out;
+ out << "Value '" << text << "' at line " << line
+ << " was ignored because it is not a valid option for '"
+ << name << "'."
+ ;
+ return ats::Errata::Message(24, LVL_INFO, out.str());
+}
+
+ats::Errata::Message Ignored_Opt_Errors(
+ char const* name,
+ int line
+) {
+ std::ostringstream out;
+ out << "Errors in '" << name << "' at line " << line
+ << " were ignored."
+ ;
+ return ats::Errata::Message(28, LVL_INFO, out.str());
+}
+
+ats::Errata::Message List_Valid_Opts(
+ char const* name,
+ int line,
+ CfgString* values,
+ size_t n
+) {
+ std::ostringstream out;
+ out << "Valid values for the '" << name << "' property at line " << line
+ << " are: "
+ ;
+ out << '"' << values[0].m_text << '"';
+ for ( size_t i = 1 ; i < n ; ++i )
+ out << ", \"" << values[i].m_text << '"';
+ out << '.';
+ return ats::Errata::Message(29, LVL_INFO, out.str());
+}
+
+ats::Errata::Message Port_Type_Invalid(char const* text, int line) {
+ std::ostringstream out;
+ out << "Value '" << text
+ << "' at line " << line
+ << "for property '" << SVC_PROP_PORT_TYPE
+ << "' is invalid. It must be 'src' or 'dst'."
+ ;
+ return ats::Errata::Message(30, LVL_WARN, out.str());
+}
+
+} // anon namespace
+
+namespace Wccp {
+
+ats::Errata
+load_option_set(libconfig::Setting& setting, CfgString* opts, size_t count) {
+ ats::Errata zret;
+ CfgString* spot;
+ CfgString* limit = opts + count;
+ char const* name = setting.getName();
+ int src_line = setting.getSourceLine();
+
+ // Clear all found flags.
+ for ( spot = opts ; spot < limit ; ++spot ) spot->m_found = false;
+
+ // Walk through the strings in the setting.
+ if (setting.isAggregate()) {
+ int nr = setting.getLength();
+ bool list_opts = false;
+ for ( int i = 0 ; i < nr ; ++i ) {
+ libconfig::Setting& item = setting[i];
+ if (libconfig::Setting::TypeString == item.getType()) {
+ char const* text = static_cast<char const*>(item);
+ for ( spot = opts ; spot < limit ; ++spot ) {
+ if (0 == strcasecmp(text, spot->m_text)) {
+ spot->m_found = true;
+ break;
+ }
+ }
+ if (spot >= limit) {
+ zret.push(Ignored_Option_Value(text, name, item.getSourceLine()));
+ list_opts = true;
+ }
+ } else {
+ zret.push(Prop_Invalid_Type(setting, libconfig::Setting::TypeString));
+ }
+ }
+ if (list_opts)
+ zret.push(List_Valid_Opts(name, src_line, opts, count));
+ } else {
+ zret.push(Prop_Invalid_Type(setting, libconfig::Setting::TypeArray));
+ }
+ return zret;
+}
+
+/** On success this returns a non @c NULL pointer if the MD5 option is
+ set. In that case the pointer points at the MD5 key. Otherwise
+ the option was none and the pointer is @c NULL
+ */
+ats::Rv<char const*>
+load_security (
+ libconfig::Setting& setting ///< Security setting.
+) {
+ ats::Rv<char const*> zret;
+ int src_line;
+ char const* text;
+
+ zret = 0;
+
+ src_line = setting.getSourceLine();
+ if (libconfig::Setting::TypeGroup == setting.getType()) {
+ if (setting.exists(SECURITY_PROP_OPTION)) {
+ libconfig::Setting& opt = setting[SECURITY_PROP_OPTION];
+ if (libconfig::Setting::TypeString == opt.getType()) {
+ text = static_cast<char const*>(opt);
+ if (0 == strcasecmp("none", text)) {
+ } else if (0 == strcasecmp("md5", text)) {
+ if (setting.exists(SECURITY_PROP_KEY)) {
+ libconfig::Setting& key = setting[SECURITY_PROP_KEY];
+ if (libconfig::Setting::TypeString == key.getType()) {
+ zret = static_cast<char const*>(key);
+ } else {
+ zret.push(Prop_Invalid_Type(key, libconfig::Setting::TypeString));
+ }
+ } else {
+ zret.push(Prop_Not_Found(SECURITY_PROP_KEY, SVC_PROP_SECURITY, src_line));
+ }
+ } else {
+ zret.push(Security_Opt_Invalid(text, opt.getSourceLine()));
+ }
+ } else {
+ zret.push(Prop_Invalid_Type(opt, libconfig::Setting::TypeString));
+ }
+ } else {
+ zret.push(Prop_Not_Found(SECURITY_PROP_OPTION, SVC_PROP_SECURITY, src_line));
+ }
+ } else {
+ zret.push(Prop_Invalid_Type(setting, libconfig::Setting::TypeGroup));
+ }
+ return zret;
+}
+
+/// Process a router address list.
+ats::Errata
+load_routers (
+ libconfig::Setting& setting, ///< Source of addresses.
+ std::vector<uint32>& addrs ///< Output list
+) {
+ ats::Errata zret;
+ int src_line;
+ char const* text;
+ static char const * const NAME = "IPv4 Address";
+
+ src_line = setting.getSourceLine();
+ if (setting.isAggregate()) {
+ int nr = setting.getLength();
+ for ( int i = 0 ; i < nr ; ++i ) {
+ libconfig::Setting& addr_cfg = setting[i];
+ int addr_line = addr_cfg.getSourceLine();
+ in_addr addr;
+ if (libconfig::Setting::TypeString == addr_cfg.getType()) {
+ text = static_cast<char const*>(addr_cfg);
+ if (inet_aton(text, &addr)) addrs.push_back(addr.s_addr);
+ else zret.push(Value_Malformed(NAME, text, addr_line));
+ } else {
+ zret.push(Prop_List_Invalid_Type(addr_cfg, libconfig::Setting::TypeString));
+ }
+ }
+ } else {
+ zret.push(Prop_Invalid_Type(setting, libconfig::Setting::TypeArray));
+ }
+ return zret;
+}
+
+ats::Errata
+CacheImpl::loadServicesFromFile(char const* path) {
+ ats::Errata zret;
+ libconfig::Config cfg;
+ int src_line = 0; // scratch for local source line caching.
+ std::vector<uint32> routers; // scratch per service loop.
+
+ // Can we read and parse the file?
+ try {
+ cfg.readFile(path);
+ } catch (libconfig::ParseException & x) {
+ return File_Syntax_Error(x.getLine(), x.getError());
+ } catch (std::exception const& x) {
+ return File_Read_Error(x.what());
+ }
+
+ // No point in going on from here.
+ if (!cfg.exists("services")) return Services_Not_Found();
+
+ libconfig::Setting& svc_list = cfg.lookup("services");
+ if (!svc_list.isAggregate()) return Services_Not_A_Sequence();
+
+ // Check for global (default) security setting.
+ if (cfg.exists(SVC_PROP_SECURITY)) {
+ libconfig::Setting& security = cfg.lookup(SVC_PROP_SECURITY);
+ ats::Rv<char const*> rv = load_security(security);
+ if (rv.isOK()) this->useMD5Security(rv);
+ else zret.join(rv.errata());
+ }
+
+ if (cfg.exists(SVC_PROP_ROUTERS)) {
+ libconfig::Setting& routers = cfg.lookup(SVC_PROP_ROUTERS);
+ zret.join(load_routers(routers, Seed_Router));
+ }
+
+ int idx, nsvc;
+ for ( idx = 0, nsvc = svc_list.getLength() ; idx < nsvc ; ++idx ) {
+ int x; // scratch int.
+ char const* text; // scratch text.
+ char const* md5_key = 0;
+ SecurityOption security_style = SECURITY_NONE;
+ bool use_group_local_security = false;
+ libconfig::Setting& svc_cfg = svc_list[idx];
+ int svc_line = svc_cfg.getSourceLine();
+ ServiceGroup svc_info;
+
+ if (!svc_cfg.isGroup()) {
+ zret.push(Service_Not_A_Group(svc_line));
+ continue;
+ }
+
+ // Get the service ID.
+ if (svc_cfg.exists(SVC_PROP_ID)) {
+ libconfig::Setting& id_prop = svc_cfg[SVC_PROP_ID];
+ if (id_prop.isNumber()) {
+ x = static_cast<int>(id_prop);
+ if (0 <= x && x <= 255)
+ svc_info.setSvcId(x);
+ else
+ zret.push(Svc_Prop_Out_Of_Range(SVC_PROP_ID, id_prop, x, 0, 255));
+ } else {
+ zret.push(Prop_Invalid_Type(id_prop, libconfig::Setting::TypeInt));
+ }
+ } else {
+ zret.push(Prop_Not_Found(SVC_PROP_ID, SVC_NAME, svc_line));
+ }
+
+ // Service type.
+ if (svc_cfg.exists(SVC_PROP_TYPE)) {
+ libconfig::Setting& prop = svc_cfg[SVC_PROP_TYPE];
+ if (libconfig::Setting::TypeString == prop.getType()) {
+ text = static_cast<char const*>(prop);
+ if (0 == strcasecmp("DYNAMIC", text))
+ svc_info.setSvcType(ServiceGroup::DYNAMIC);
+ else if (0 == strcasecmp("STANDARD", text))
+ svc_info.setSvcType(ServiceGroup::STANDARD);
+ else
+ zret.push(Service_Type_Invalid(text, prop.getSourceLine()));
+ } else {
+ zret.push(Prop_Invalid_Type(prop, libconfig::Setting::TypeString));
+ }
+ } else { // default type based on ID.
+ ServiceGroup::Type svc_type =
+ svc_info.getSvcId() <= ServiceGroup::RESERVED
+ ? ServiceGroup::STANDARD
+ : ServiceGroup::DYNAMIC
+ ;
+ svc_info.setSvcType(svc_type);
+ zret.push(Service_Type_Defaulted(svc_type, svc_line));
+ }
+
+ // Get the protocol.
+ if (svc_cfg.exists(SVC_PROP_PROTOCOL)) {
+ libconfig::Setting& proto_prop = svc_cfg[SVC_PROP_PROTOCOL];
+ if (svc_info.getSvcType() == ServiceGroup::STANDARD) {
+ zret.push(Svc_Prop_Ignored(SVC_PROP_PROTOCOL, proto_prop.getSourceLine()));
+ } else if (proto_prop.isNumber()) {
+ x = static_cast<int>(proto_prop);
+ if (0 <= x && x <= 255)
+ svc_info.setProtocol(x);
+ else
+ zret.push(Svc_Prop_Out_Of_Range(SVC_PROP_ID, proto_prop, x, 0, 255));
+ } else {
+ zret.push(Prop_Invalid_Type(proto_prop, libconfig::Setting::TypeInt));
+ }
+ } else if (svc_info.getSvcType() != ServiceGroup::STANDARD) {
+ // Required if it's not standard / predefined.
+ zret.push(Prop_Not_Found(SVC_PROP_PROTOCOL, SVC_NAME, svc_line));
+ }
+
+ // Get the priority.
+ svc_info.setPriority(0); // OK to default to this value.
+ if (svc_cfg.exists(SVC_PROP_PRIORITY)) {
+ libconfig::Setting& pri_prop = svc_cfg[SVC_PROP_PRIORITY];
+ if (svc_info.getSvcType() == ServiceGroup::STANDARD) {
+ zret.push(Svc_Prop_Ignored(SVC_PROP_PRIORITY, pri_prop.getSourceLine()));
+ } else if (pri_prop.isNumber()) {
+ x = static_cast<int>(pri_prop);
+ if (0 <= x && x <= 255)
+ svc_info.setPriority(x);
+ else
+ zret.push(Svc_Prop_Out_Of_Range(SVC_PROP_ID, pri_prop, x, 0, 255));
+ } else {
+ zret.push(Prop_Invalid_Type(pri_prop, libconfig::Setting::TypeInt));
+ }
+ }
+
+ // Service flags.
+ svc_info.setFlags(0);
+
+ if (svc_cfg.exists(SVC_PROP_PRIMARY_HASH)) {
+ libconfig::Setting& prop = svc_cfg[SVC_PROP_PRIMARY_HASH];
+ ats::Errata status = load_option_set(prop, HASH_OPTS, N_OPTS(HASH_OPTS));
+ uint32 f = 0;
+ src_line = prop.getSourceLine();
+ for ( size_t i = 0 ; i < N_OPTS(HASH_OPTS) ; ++i )
+ if (HASH_OPTS[i].m_found) f |= ServiceGroup::SRC_IP_HASH << i;
+ if (f) {
+ svc_info.enableFlags(f);
+ if (!status) zret.push(Ignored_Opt_Errors(SVC_PROP_PRIMARY_HASH, src_line).set(status));
+ } else {
+ zret.push(List_Valid_Opts(SVC_PROP_PRIMARY_HASH, src_line, HASH_OPTS, N_OPTS(HASH_OPTS)).set(status));
+ }
+ } else {
+ zret.push(Prop_Not_Found(SVC_PROP_PRIMARY_HASH, SVC_NAME, svc_line));
+ }
+
+ if (svc_cfg.exists(SVC_PROP_ALT_HASH)) {
+ libconfig::Setting& prop = svc_cfg[SVC_PROP_ALT_HASH];
+ ats::Errata status = load_option_set(prop, HASH_OPTS, N_OPTS(HASH_OPTS));
+ uint32 f = 0;
+ src_line = prop.getSourceLine();
+ for ( size_t i = 0 ; i < N_OPTS(HASH_OPTS) ; ++i )
+ if (HASH_OPTS[i].m_found) f |= ServiceGroup::SRC_IP_ALT_HASH << i;
+ if (f) svc_info.enableFlags(f);
+ if (!status) zret.push(Ignored_Opt_Errors(SVC_PROP_ALT_HASH, src_line).set(status));
+ }
+
+ if (svc_cfg.exists(SVC_PROP_PORT_TYPE)) {
+ libconfig::Setting& prop = svc_cfg[SVC_PROP_PORT_TYPE];
+ src_line = prop.getSourceLine();
+ if (libconfig::Setting::TypeString == prop.getType()) {
+ text = static_cast<char const*>(prop);
+ if (0 == strcasecmp("src", text))
+ svc_info.enableFlags(ServiceGroup::PORTS_SOURCE);
+ else if (0 == strcasecmp("dst", text))
+ svc_info.disableFlags(ServiceGroup::PORTS_SOURCE);
+ else
+ zret.push(Port_Type_Invalid(text, src_line));
+ } else {
+ zret.push(Prop_Invalid_Type(prop, libconfig::Setting::TypeString));
+ }
+ }
+
+ // Ports for service.
+ svc_info.clearPorts();
+ if (svc_cfg.exists(SVC_PROP_PORTS)) {
+ libconfig::Setting& prop = svc_cfg[SVC_PROP_PORTS];
+ src_line = prop.getSourceLine();
+ if (ServiceGroup::STANDARD == svc_info.getSvcType()) {
+ zret.push(Svc_Prop_Ignored_In_Standard(SVC_PROP_PORTS, src_line));
+ } else {
+ if ( prop.isAggregate() ) {
+ int nport = prop.getLength();
+ int pidx, sidx;
+ bool malformed_error = false;
+ // Clip to maximum protocol allowed ports.
+ if (nport > ServiceGroup::N_PORTS) {
+ zret.push(Svc_Ports_Too_Many(src_line, nport));
+ nport = ServiceGroup::N_PORTS;
+ }
+ // Step through the ports.
+ for ( pidx = sidx = 0 ; pidx < nport ; ++pidx ) {
+ libconfig::Setting& port_cfg = prop[pidx];
+ if (port_cfg.isNumber()) {
+ x = static_cast<int>(port_cfg);
+ if (0 <= x && x <= 65535)
+ svc_info.setPort(sidx++, x);
+ else
+ zret.push(Svc_Prop_Out_Of_Range(SVC_PROP_PORTS, port_cfg, x, 0, 65535));
+ } else if (!malformed_error) { // only report this once.
+ zret.push(Svc_Ports_Malformed(src_line));
+ malformed_error = true;
+ }
+ }
+ if (sidx) svc_info.enableFlags(ServiceGroup::PORTS_DEFINED);
+ else zret.push(Svc_Ports_None_Valid(src_line));
+ } else {
+ zret.push(Prop_Invalid_Type(prop, libconfig::Setting::TypeArray));
+ }
+ }
+ } else if (ServiceGroup::STANDARD != svc_info.getSvcType()) {
+ zret.push(Svc_Ports_Not_Found(svc_line));
+ }
+
+ // Security option for this service group.
+ if (svc_cfg.exists(SVC_PROP_SECURITY)) {
+ ats::Rv<char const*> security = load_security(svc_cfg[SVC_PROP_SECURITY]);
+ if (security.isOK()) {
+ use_group_local_security = true;
+ if (security.result()) {
+ md5_key = security;
+ security_style = SECURITY_MD5;
+ } else {
+ security_style = SECURITY_NONE;
+ }
+ }
+ zret.join(security.errata());
+ }
+
+ // Get any group specific routers.
+ routers.clear(); // reset list.
+ if (svc_cfg.exists(SVC_PROP_ROUTERS)) {
+ libconfig::Setting& rtr_cfg = svc_cfg[SVC_PROP_ROUTERS];
+ ats::Errata status = load_routers(rtr_cfg, routers);
+ if (!status)
+ zret.push(ats::Errata::Message(23, LVL_INFO, "Router specification invalid.").set(status));
+ }
+ if (!routers.size() && !Seed_Router.size())
+ zret.push(No_Valid_Routers(svc_line));
+
+ // See if can proceed with service group creation.
+ ats::Errata::Code code = code_max(zret);
+ if (code >= LVL_WARN) {
+ zret = Unable_To_Create_Service_Group(svc_line).set(zret);
+ return zret;
+ }
+
+ // Properties after this are optional so we can proceed if they fail.
+ GroupData& svc = this->defineServiceGroup(svc_info);
+ // Add seed routers.
+ std::vector<uint32>::iterator rspot, rlimit;
+ for ( rspot = routers.begin(), rlimit = routers.end() ; rspot != rlimit ; ++rspot )
+ svc.seedRouter(*rspot);
+ for ( rspot = Seed_Router.begin(), rlimit = Seed_Router.end() ; rspot != rlimit ; ++rspot )
+ svc.seedRouter(*rspot);
+
+ if (use_group_local_security)
+ svc.setSecurity(security_style).setKey(md5_key);
+
+ // Look for optional properties.
+
+ svc.m_packet_forward = ServiceGroup::GRE; // default
+ if (svc_cfg.exists(SVC_PROP_FORWARD)) {
+ libconfig::Setting& prop = svc_cfg[SVC_PROP_FORWARD];
+ ats::Errata status = load_option_set(prop, FORWARD_OPTS, N_FORWARD_OPTS);
+ bool gre = FORWARD_OPTS[0].m_found;
+ bool l2 = FORWARD_OPTS[1].m_found;
+ if (gre || l2) {
+ svc.m_packet_forward =
+ gre
+ ? l2 ? ServiceGroup::GRE_OR_L2 : ServiceGroup::GRE
+ : ServiceGroup::L2
+ ;
+ if (!status.isOK())
+ zret.push(Ignored_Opt_Errors(SVC_PROP_FORWARD, prop.getSourceLine()).set(status));
+ } else {
+ zret.push(ats::Errata::Message(26, LVL_INFO, "Defaulting to GRE forwarding.").set(status));
+ }
+ }
+
+ svc.m_packet_return = ServiceGroup::GRE; // default.
+ if (svc_cfg.exists(SVC_PROP_RETURN)) {
+ libconfig::Setting& prop = svc_cfg[SVC_PROP_RETURN];
+ ats::Errata status = load_option_set(prop, RETURN_OPTS, N_RETURN_OPTS);
+ bool gre = RETURN_OPTS[0].m_found;
+ bool l2 = RETURN_OPTS[1].m_found;
+ if (gre || l2) {
+ svc.m_packet_return =
+ gre
+ ? l2 ? ServiceGroup::GRE_OR_L2 : ServiceGroup::GRE
+ : ServiceGroup::L2
+ ;
+ if (!status.isOK()) zret.push(Ignored_Opt_Errors(SVC_PROP_RETURN, prop.getSourceLine()).set(status));
+ } else {
+ zret.push(ats::Errata::Message(26, LVL_INFO, "Defaulting to GRE return.").set(status));
+ }
+ }
+
+ svc.m_cache_assign = ServiceGroup::HASH_ONLY; // default
+ if (svc_cfg.exists(SVC_PROP_ASSIGN)) {
+ libconfig::Setting& prop = svc_cfg[SVC_PROP_ASSIGN];
+ ats::Errata status = load_option_set(prop, ASSIGN_OPTS, N_OPTS(ASSIGN_OPTS));
+ bool hash = ASSIGN_OPTS[0].m_found;
+ bool mask = ASSIGN_OPTS[1].m_found;
+ if (hash || mask) {
+ svc.m_cache_assign =
+ hash
+ ? mask ? ServiceGroup::HASH_OR_MASK : ServiceGroup::HASH_ONLY
+ : ServiceGroup::MASK_ONLY
+ ;
+ if (!status.isOK()) zret.push(Ignored_Opt_Errors(SVC_PROP_ASSIGN, prop.getSourceLine()).set(status));
+ } else {
+ status.push(ats::Errata::Message(26, LVL_INFO, "Defaulting to hash assignment only."));
+ zret.push(List_Valid_Opts(SVC_PROP_ASSIGN, src_line, ASSIGN_OPTS, N_OPTS(ASSIGN_OPTS)).set(status));
+ }
+ }
+ }
+ return zret;
+}
+
+} // namespace.
Added: trafficserver/traffic/branches/wccp/proxy/wccp/WccpEndPoint.cc
URL: http://svn.apache.org/viewvc/trafficserver/traffic/branches/wccp/proxy/wccp/WccpEndPoint.cc?rev=1021825&view=auto
==============================================================================
--- trafficserver/traffic/branches/wccp/proxy/wccp/WccpEndPoint.cc (added)
+++ trafficserver/traffic/branches/wccp/proxy/wccp/WccpEndPoint.cc Tue Oct 12 16:10:47 2010
@@ -0,0 +1,1117 @@
+# include "WccpLocal.h"
+# include "WccpUtil.h"
+# include "AtsMeta.h"
+# include <errno.h>
+// ------------------------------------------------------
+namespace Wccp {
+// ------------------------------------------------------
+Impl::GroupData::GroupData()
+ : m_generation(0)
+ , m_use_security_opt(false)
+ , m_use_security_key(false) {
+}
+
+Impl::GroupData&
+Impl::GroupData::setKey(char const* key) {
+ m_use_security_key = true;
+ memset(m_security_key, 0, SecurityComp::KEY_SIZE);
+ strncpy(m_security_key, key, SecurityComp::KEY_SIZE);
+ return *this;
+}
+
+Impl::GroupData&
+Impl::GroupData::setSecurity(SecurityOption style) {
+ m_use_security_opt = true;
+ m_security_opt = style;
+ return *this;
+}
+
+Impl::Impl()
+ : m_addr(INADDR_ANY)
+ , m_fd(ats::NO_FD) {
+}
+
+Impl::~Impl() {
+ this->close();
+}
+
+int
+Impl::open(uint addr) {
+ struct sockaddr saddr;
+ sockaddr_in& in_addr = reinterpret_cast<sockaddr_in&>(saddr);
+ int fd;
+
+ if (ats::NO_FD != m_fd) {
+ log(LVL_INFO, "Attempted to open already open WCCP Endpoint");
+ return -EALREADY;
+ }
+
+ if (ats::NO_FD == (fd = socket(PF_INET, SOCK_DGRAM, 0))) {
+ log_errno(LVL_FATAL, "Failed to create socket");
+ return -errno;
+ }
+
+ if (INADDR_ANY != addr) m_addr = addr; // overridden.
+ memset(&saddr, 0, sizeof(saddr));
+ in_addr.sin_family = AF_INET;
+ in_addr.sin_port = htons(DEFAULT_PORT);
+ in_addr.sin_addr.s_addr = m_addr;
+ int zret = bind(fd, &saddr, sizeof(saddr));
+ if (-1 == zret) {
+ log_errno(LVL_FATAL, "Failed to bind socket to port");
+ this->close();
+ return -errno;
+ }
+ logf(LVL_INFO, "WCCP socket bound to %s:%d", ip_addr_to_str(m_addr), DEFAULT_PORT);
+
+ // Now get the address. Usually the same but possibly different,
+ // certainly if addr was INADDR_ANY.
+ if (INADDR_ANY == m_addr && INADDR_ANY == (m_addr = Get_Local_Address(fd))) {
+ log_errno(LVL_FATAL, "Failed to get local address for socket");
+ this->close();
+ return -errno;
+ }
+
+ // Enable retrieval of destination address on packets.
+ int ip_pktinfo_flag = 1;
+ if (-1 == setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &ip_pktinfo_flag, sizeof(ip_pktinfo_flag))) {
+ log_errno(LVL_FATAL, "Failed to enable destination address retrieval");
+ this->close();
+ return -errno;
+ }
+
+ /// Disable PMTU on Linux because of a bug in IOS routers.
+ /// WCCP packets are rejected as duplicates if the IP fragment
+ /// identifier is 0, which is the value used when PMTU is enabled.
+ int pmtu = IP_PMTUDISC_DONT;
+ if (-1 == setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu))) {
+ log_errno(LVL_FATAL, "Failed to disable PMTU on WCCP socket.");
+ this->close();
+ return -errno;
+ }
+
+ m_fd = fd;
+ return 0;
+}
+
+void
+Impl::close() {
+ if (ats::NO_FD != m_fd) {
+ ::close(m_fd);
+ m_fd = ats::NO_FD;
+ }
+}
+
+void
+Impl::useMD5Security(char const* key) {
+ m_use_security_opt = true;
+ m_security_opt = SECURITY_MD5;
+ m_use_security_key = true;
+ memset(m_security_key, 0, SecurityComp::KEY_SIZE);
+ strncpy(m_security_key, key, SecurityComp::KEY_SIZE);
+}
+
+SecurityOption
+Impl::setSecurity(BaseMsg& msg, GroupData const& group) const {
+ SecurityOption zret = SECURITY_NONE;
+ if (group.m_use_security_opt) zret = group.m_security_opt;
+ else if (m_use_security_opt) zret = m_security_opt;
+ if (group.m_use_security_key) msg.m_security.setKey(group.m_security_key);
+ else if (m_use_security_key) msg.m_security.setKey(m_security_key);
+ return zret;
+}
+
+bool
+Impl::validateSecurity(BaseMsg& msg, GroupData const& group) {
+ SecurityOption opt = msg.m_security.getOption();
+ if (group.m_use_security_opt) {
+ if (opt != group.m_security_opt) return false;
+ } else if (m_use_security_opt) {
+ if (opt != m_security_opt) return false;
+ }
+ if (opt == SECURITY_MD5) {
+ if (group.m_use_security_key) msg.m_security.setKey(group.m_security_key);
+ else if (m_use_security_key) msg.m_security.setKey(m_security_key);
+ return msg.validateSecurity();
+ }
+ return true;
+}
+
+ats::Rv<int>
+Impl::handleMessage() {
+ ats::Rv<int> zret;
+ ssize_t n; // recv byte count.
+ struct sockaddr src_addr; // sender's address.
+ msghdr recv_hdr;
+ iovec recv_buffer;
+ IpHeader ip_header;
+ static ssize_t const BUFFER_SIZE = 65536;
+ char buffer[BUFFER_SIZE];
+ static size_t const ANC_BUFFER_SIZE = CMSG_ALIGN(CMSG_SPACE(sizeof(in_pktinfo)));
+ char anc_buffer[ANC_BUFFER_SIZE];
+
+ if (ats::NO_FD == m_fd) return -ENOTCONN;
+
+ recv_buffer.iov_base = buffer;
+ recv_buffer.iov_len = BUFFER_SIZE;
+
+ recv_hdr.msg_name = &src_addr;
+ recv_hdr.msg_namelen = sizeof(src_addr);
+ recv_hdr.msg_iov = &recv_buffer;
+ recv_hdr.msg_iovlen = 1;
+ recv_hdr.msg_control = anc_buffer;
+ recv_hdr.msg_controllen = ANC_BUFFER_SIZE;
+
+ n = recvmsg(m_fd, &recv_hdr, MSG_TRUNC);
+ if (n > BUFFER_SIZE) return -EMSGSIZE;
+ else if (n < 0) return -errno;
+
+ // Extract the original destination address.
+ ip_header.m_src = access_field(&sockaddr_in::sin_addr, &src_addr).s_addr;
+ for ( cmsghdr* anc = CMSG_FIRSTHDR(&recv_hdr);
+ anc;
+ anc = CMSG_NXTHDR(&recv_hdr, anc)
+ ) {
+ if (anc->cmsg_level == IPPROTO_IP && anc->cmsg_type == IP_PKTINFO) {
+ ip_header.m_dst = access_field(&in_pktinfo::ipi_addr, CMSG_DATA(anc)).s_addr;
+ break;
+ }
+ }
+
+ // Check to see if there is a valid header.
+ MsgHeaderComp header;
+ MsgBuffer msg_buffer(buffer,n);
+ if (PARSE_SUCCESS == header.parse(msg_buffer)) {
+ message_type_t msg_type = header.getType();
+ ats::Buffer chunk(buffer,n);
+
+ switch (msg_type) {
+ case HERE_I_AM: this->handleHereIAm(ip_header, chunk); break;
+ case I_SEE_YOU: this->handleISeeYou(ip_header, chunk); break;
+ case REDIRECT_ASSIGN: this->handleRedirectAssign(ip_header, chunk); break;
+ case REMOVAL_QUERY: this->handleRemovalQuery(ip_header, chunk); break;
+ default: fprintf(stderr, "Unknown message type %d ignored.\n", msg_type);
+ break;
+ };
+ } else {
+ fprintf(stderr, "Malformed message ignored.\n");
+ }
+ return zret;
+}
+
+ats::Errata
+Impl::handleHereIAm(IpHeader const&, ats::Buffer const&) {
+ return log(LVL_INFO, "Unanticipated WCCP2_HERE_I_AM message ignored");
+}
+ats::Errata
+Impl::handleISeeYou(IpHeader const&, ats::Buffer const& data) {
+ return log(LVL_INFO, "Unanticipated WCCP2_I_SEE_YOU message ignored.");
+}
+ats::Errata
+Impl::handleRedirectAssign(IpHeader const&, ats::Buffer const& data) {
+ return log(LVL_INFO, "Unanticipated WCCP2_REDIRECT_ASSIGN message ignored.");
+}
+ats::Errata
+Impl::handleRemovalQuery(IpHeader const&, ats::Buffer const& data) {
+ return log(LVL_INFO, "Unanticipated WCCP2_REMOVAL_QUERY message ignored.");
+}
+// ------------------------------------------------------
+CacheImpl::GroupData::GroupData()
+ : m_assignment_pending(false) {
+}
+
+CacheImpl::GroupData&
+CacheImpl::GroupData::seedRouter(uint32 addr) {
+ // Be nice and don't add it if it's already there.
+ if (m_seed_routers.end() == find_by_member(m_seed_routers, &SeedRouter::m_addr, addr))
+ m_seed_routers.push_back(SeedRouter(addr));
+ return *this;
+}
+
+time_t
+CacheImpl::GroupData::removeSeedRouter(uint32 addr) {
+ time_t zret = 0;
+ std::vector<SeedRouter>::iterator begin = m_seed_routers.begin();
+ std::vector<SeedRouter>::iterator end = m_seed_routers.end();
+ std::vector<SeedRouter>::iterator spot =
+ std::find_if(begin, end, ats::predicate(&SeedRouter::m_addr, addr));
+
+ if (end != spot) {
+ zret = spot->m_xmit;
+ m_seed_routers.erase(spot);
+ }
+
+ return zret;
+}
+
+CacheImpl::GroupData& CacheImpl::GroupData::setKey(char const* key) { return static_cast<self&>(this->super::setKey(key)); }
+
+CacheImpl::GroupData& CacheImpl::GroupData::setSecurity(SecurityOption style) { return static_cast<self&>(this->super::setSecurity(style)); }
+
+CacheImpl::CacheBag::iterator
+CacheImpl::GroupData::findCache(uint32 addr) {
+ return std::find_if(
+ m_caches.begin(),
+ m_caches.end(),
+ ats::predicate(&CacheData::idAddr, addr)
+ );
+}
+
+void
+CacheImpl::GroupData::resizeCacheSources() {
+ int count = m_routers.size();
+ for ( CacheBag::iterator spot = m_caches.begin(),
+ limit = m_caches.end();
+ spot != limit;
+ ++spot
+ ) {
+ spot->m_src.resize(count);
+ }
+}
+
+inline CacheImpl::RouterData::RouterData()
+ : m_addr(0)
+ , m_generation(0)
+ , m_rapid(0)
+ , m_assign(false)
+ , m_send_caps(false) {
+}
+
+inline CacheImpl::RouterData::RouterData(uint32 addr)
+ : m_addr(addr)
+ , m_generation(0)
+ , m_rapid(0)
+ , m_assign(false)
+ , m_send_caps(false) {
+}
+
+time_t
+CacheImpl::RouterData::pingTime(time_t now) const {
+ time_t tx = m_xmit.m_time + (m_rapid ? TIME_UNIT/10 : TIME_UNIT);
+ return tx < now ? 0 : tx - now;
+}
+
+time_t
+CacheImpl::RouterData::waitTime(time_t now) const {
+ return m_assign ? 0 : this->pingTime(now);
+}
+
+inline uint32
+CacheImpl::CacheData::idAddr() const {
+ return m_id.getAddr();
+}
+
+CacheImpl::GroupData&
+CacheImpl::defineServiceGroup(
+ ServiceGroup const& svc,
+ ServiceGroup::Result* result
+) {
+ uint8 svc_id = svc.getSvcId();
+ GroupMap::iterator spot = m_groups.find(svc_id);
+ GroupData* group; // service with target ID.
+ ServiceGroup::Result zret;
+ if (spot == m_groups.end()) { // not defined
+ group = &(m_groups[svc_id]);
+ group->m_svc = svc;
+ memset(&group->m_id, 0, sizeof(group->m_id));
+ group->m_id.setAddr(m_addr);
+ group->m_id.setUnassigned(true);
+ group->m_id.setBuckets(false);
+ zret = ServiceGroup::DEFINED;
+ } else {
+ group = &spot->second;
+ zret = group->m_svc == svc ? ServiceGroup::EXISTS : ServiceGroup::CONFLICT;
+ }
+ if (result) *result = zret;
+ return *group;
+}
+
+time_t
+CacheImpl::GroupData::waitTime(time_t now) const {
+ time_t zret = std::numeric_limits<time_t>::max();
+ // Active routers.
+ for ( RouterBag::const_iterator router = m_routers.begin(),
+ router_limit = m_routers.end() ;
+ router != router_limit && zret;
+ ++router
+ ) {
+ zret = std::min(zret, router->waitTime(now));
+ }
+ // Seed routers.
+ for ( std::vector<SeedRouter>::const_iterator
+ router = m_seed_routers.begin(),
+ router_limit = m_seed_routers.end() ;
+ router != router_limit && zret;
+ ++router
+ ) {
+ time_t tx = router->m_xmit + TIME_UNIT;
+ if (tx < now) zret = 0;
+ else zret = std::min(tx - now, zret);
+ }
+ // Assignment
+ if (m_assignment_pending) {
+ time_t tx = m_generation_time + ( 3 * TIME_UNIT / 2 );
+ if (tx < now) zret = 0;
+ else zret = std::min(tx - now, zret);
+ }
+
+ return zret;
+}
+
+bool
+CacheImpl::GroupData::cullRouters(time_t now) {
+ bool zret = false;
+ size_t idx = 0, n = m_routers.size();
+ while ( idx < n ) {
+ RouterData& router = m_routers[idx];
+ if (router.m_recv.m_time + TIME_UNIT * 3 < now) {
+ uint32 addr = router.m_addr;
+ // Clip the router by copying down and resizing.
+ // Must do all caches as well.
+ --n; // Decrement router counter first.
+ if (idx < n) router = m_routers[n];
+ m_routers.resize(n);
+ for ( CacheBag::iterator
+ cache = m_caches.begin(),
+ cache_limit = m_caches.end();
+ cache != cache_limit;
+ ++cache
+ ) {
+ if (idx < n) cache->m_src[idx] = cache->m_src[n];
+ cache->m_src.resize(n);
+ }
+ // Put it back in the seeds.
+ this->seedRouter(addr);
+ zret = true; // Router was culled, report it to caller.
+ logf(LVL_INFO, "Router " ATS_IP_PRINTF_CODE " timed out and was removed from the active list.", ATS_IP_OCTETS(addr));
+ } else {
+ ++idx; // move to next router.
+ }
+ }
+ if (zret) this->viewChanged(now);
+ return zret;
+}
+
+CacheImpl::GroupData&
+CacheImpl::GroupData::viewChanged(time_t now) {
+ m_generation += 1;
+ m_generation_time = now;
+ m_assign_info.setActive(false); // invalidate current assignment.
+ m_assignment_pending = m_routers.size() && m_caches.size();
+ // Cancel any pending assignment transmissions.
+ ats::for_each(m_routers, ats::assign_member(&RouterData::m_assign, false));
+ logf(LVL_DEBUG, "Service group %d view change (%d)", m_svc.getSvcId(), m_generation);
+
+ return *this;
+}
+
+Cache::Service&
+Cache::Service::setKey(char const* key) {
+ m_group->setKey(key);
+ return *this;
+}
+
+Cache::Service&
+Cache::Service::setSecurity(SecurityOption opt) {
+ m_group->setSecurity(opt);
+ return *this;
+}
+
+CacheImpl&
+CacheImpl::seedRouter(uint8 id, uint32 addr) {
+ GroupMap::iterator spot = m_groups.find(id);
+ if (spot != m_groups.end()) spot->second.seedRouter(addr);
+ return *this;
+}
+
+bool
+CacheImpl::isConfigured() const {
+ return INADDR_ANY != m_addr && m_groups.size() > 0;
+}
+
+int
+CacheImpl::open(uint32 addr) {
+ int zret = this->super::open(addr);
+ // If the socket was successfully opened, go through the
+ // services and update the local service descriptor.
+ if (0 <= zret) {
+ for ( GroupMap::iterator spot = m_groups.begin(), limit = m_groups.end();
+ spot != limit;
+ ++spot
+ ) {
+ spot->second.m_id.setAddr(m_addr);
+ }
+ }
+ return zret;
+}
+
+time_t
+CacheImpl::waitTime() const {
+ time_t now = time(0);
+ return ats::minima(m_groups, &GroupData::waitTime, now);
+}
+
+void
+CacheImpl::generateHereIAm(
+ HereIAmMsg& msg,
+ GroupData& group
+) {
+ msg.fill(group, this->setSecurity(msg, group), 0, 0);
+ msg.finalize();
+}
+
+void
+CacheImpl::generateHereIAm(
+ HereIAmMsg& msg,
+ GroupData& group,
+ RouterData& router
+) {
+ int i;
+ size_t n_routers = group.m_routers.size();
+ size_t n_caches = group.m_caches.size();
+
+ msg.fill(group, this->setSecurity(msg, group), n_routers, n_caches);
+ msg.fill_caps(router);
+
+ // Fill routers.
+ i = 0;
+ for ( RouterBag::iterator spot = group.m_routers.begin(),
+ limit = group.m_routers.end();
+ spot != limit;
+ ++spot, ++i
+ ) {
+ msg.m_cache_view.routerElt(i)
+ .setAddr(spot->m_addr)
+ .setRecvId(spot->m_recv.m_sn)
+ ;
+ }
+
+ // fill caches.
+ i = 0;
+ for ( CacheBag::iterator spot = group.m_caches.begin(),
+ limit = group.m_caches.end();
+ spot != limit;
+ ++spot, ++i
+ ) {
+ msg.m_cache_view.setCacheAddr(i, spot->idAddr());
+ }
+
+ msg.finalize();
+}
+
+void
+CacheImpl::generateRedirectAssign(
+ RedirectAssignMsg& msg,
+ GroupData& group
+) {
+ int i;
+ size_t n_caches = group.m_caches.size();
+ size_t n_routers = group.m_caches.size();
+ AssignmentKeyElt key(m_addr, group.m_generation);
+
+ msg.fill(group, this->setSecurity(msg, group), key, n_routers, n_caches);
+
+ // Fill routers.
+ i = 0;
+ for ( RouterBag::iterator spot = group.m_routers.begin(),
+ limit = group.m_routers.end();
+ spot != limit;
+ ++spot, ++i
+ ) {
+ msg.m_assign.routerElt(i)
+ .setChangeNumber(spot->m_generation)
+ .setAddr(spot->m_addr)
+ .setRecvId(spot->m_recv.m_sn)
+ ;
+ }
+
+ // fill caches.
+ i = 0;
+ for ( CacheBag::iterator spot = group.m_caches.begin(),
+ limit = group.m_caches.end();
+ spot != limit;
+ ++spot, ++i
+ ) {
+ msg.m_assign.setCacheAddr(i, spot->idAddr());
+ }
+
+ msg.finalize();
+}
+
+int
+CacheImpl::housekeeping() {
+ int zret = 0;
+ sockaddr_in dst_addr;
+ sockaddr* addr_ptr = reinterpret_cast<sockaddr*>(&dst_addr);
+ time_t now = time(0);
+ static size_t const BUFFER_SIZE = 4096;
+ MsgBuffer msg_buffer;
+ char msg_data[BUFFER_SIZE];
+ msg_buffer.set(msg_data, BUFFER_SIZE);
+
+ // Set up everything except the IP address.
+ memset(&dst_addr, 0, sizeof(dst_addr));
+ dst_addr.sin_family = AF_INET;
+ dst_addr.sin_port = htons(DEFAULT_PORT);
+
+ // Walk the service groups and do their housekeeping.
+ for ( GroupMap::iterator
+ svc_spot = m_groups.begin(),
+ svc_limit = m_groups.end();
+ svc_spot != svc_limit;
+ ++svc_spot
+ ) {
+ GroupData& group = svc_spot->second;
+
+ // Check to see if it's time for an assignment.
+ if (group.m_assignment_pending
+ && group.m_generation_time + ASSIGN_WAIT <= now
+ ) {
+ // Is a valid assignment possible?
+ if (group.m_assign_info.fill(group, m_addr))
+ ats::for_each(group.m_routers, ats::assign_member(&RouterData::m_assign, true));
+
+ // Always clear because no point in sending a assign we can't generate.
+ group.m_assignment_pending = false;
+ }
+
+ group.cullRouters(now); // TBD UPDATE VIEW!
+
+ // Check the active routers for scheduled packets.
+ for ( RouterBag::iterator rspot = group.m_routers.begin(),
+ rend = group.m_routers.end() ;
+ rspot != rend ;
+ ++rspot
+ ) {
+ dst_addr.sin_addr.s_addr = rspot->m_addr;
+ if (0 == rspot->pingTime(now)) {
+ HereIAmMsg here_i_am;
+ here_i_am.setBuffer(msg_buffer);
+ this->generateHereIAm(here_i_am, group, *rspot);
+ zret = sendto(m_fd, msg_data, here_i_am.getCount(), 0, addr_ptr, sizeof(dst_addr));
+ if (0 <= zret) {
+ rspot->m_xmit.set(now, group.m_generation);
+ rspot->m_send_caps = false;
+ logf(LVL_DEBUG, "Sent HERE_I_AM for service group %d to router %s%s[#%d,%lu].",
+ group.m_svc.getSvcId(),
+ ip_addr_to_str(rspot->m_addr),
+ rspot->m_rapid ? " [rapid] " : " ",
+ group.m_generation, now
+ );
+ if (rspot->m_rapid) --(rspot->m_rapid);
+ } else {
+ logf_errno(LVL_WARN, "Failed to send to router " ATS_IP_PRINTF_CODE " - ", ATS_IP_OCTETS(rspot->m_addr));
+ }
+ } else if (rspot->m_assign) {
+ RedirectAssignMsg redirect_assign;
+ redirect_assign.setBuffer(msg_buffer);
+ this->generateRedirectAssign(redirect_assign, group);
+ zret = sendto(m_fd, msg_data, redirect_assign.getCount(), 0, addr_ptr, sizeof(dst_addr));
+ if (0 <= zret) rspot->m_assign = false;
+ }
+ }
+
+ // Seed routers.
+ for ( std::vector<SeedRouter>::iterator
+ sspot = group.m_seed_routers.begin(),
+ slimit = group.m_seed_routers.end() ;
+ sspot != slimit ;
+ ++sspot
+ ) {
+ HereIAmMsg here_i_am;
+ here_i_am.setBuffer(msg_buffer);
+ // Is the router due for a ping?
+ if (sspot->m_xmit + TIME_UNIT > now) continue; // no
+
+ this->generateHereIAm(here_i_am, group);
+
+ dst_addr.sin_addr.s_addr = sspot->m_addr;
+ zret = sendto(m_fd, msg_data, here_i_am.getCount(), 0,
+ addr_ptr, sizeof(dst_addr));
+ if (0 <= zret) {
+ logf(LVL_DEBUG, "Sent HERE_I_AM for SG %d to seed router %s [#%d,%lu].",
+ group.m_svc.getSvcId(),
+ ip_addr_to_str(sspot->m_addr),
+ group.m_generation, now
+ );
+ sspot->m_xmit = now;
+ sspot->m_count += 1;
+ }
+ }
+ }
+ return zret;
+}
+
+ats::Errata
+CacheImpl::handleISeeYou(IpHeader const& ip_hdr, ats::Buffer const& chunk) {
+ ats::Errata zret;
+ ISeeYouMsg msg;
+ // Set if our view of the group changes enough to bump the
+ // generation number.
+ bool view_changed = false;
+ time_t now = time(0); // don't call this over and over.
+ int parse = msg.parse(chunk);
+
+ if (PARSE_SUCCESS != parse)
+ return log(LVL_INFO, "Ignored malformed WCCP2_I_SEE_YOU message.");
+
+ ServiceGroup svc(msg.m_service);
+ GroupMap::iterator spot = m_groups.find(svc.getSvcId());
+ if (spot == m_groups.end())
+ return logf(LVL_INFO, "WCCP2_I_SEE_YOU ignored - service group %d not found.", svc.getSvcId());
+
+ GroupData& group = spot->second;
+
+ if (!this->validateSecurity(msg, group))
+ return log(LVL_INFO, "Ignored WCCP2_I_SEE_YOU with invalid security.\n");
+
+ if (svc != group.m_svc)
+ return logf(LVL_INFO, "WCCP2_I_SEE_YOU ignored - service group definition %d does not match.\n", svc.getSvcId());
+
+ if (-1 == msg.m_router_id.findFromAddr(m_addr))
+ return logf(LVL_INFO, "WCCP2_I_SEE_YOU ignored -- cache not in from list.\n");
+
+ logf(LVL_DEBUG, "Received WCCP2_I_SEE_YOU for group %d.", group.m_svc.getSvcId());
+
+ // Prefered address for router.
+ uint32 router_addr = msg.m_router_id.idElt().getAddr();
+ // Where we sent our packet.
+ uint32 to_addr = msg.m_router_id.getToAddr();
+ uint32 recv_id = msg.m_router_id.idElt().getRecvId();
+ RouterBag::iterator ar_spot; // active router
+ int router_idx; // index in active routers.
+ std::vector< SeedRouter >::iterator seed_spot;
+
+ // Handle the router that sent us this.
+ ar_spot = find_by_member(group.m_routers, &RouterData::m_addr, router_addr);
+ if (ar_spot == group.m_routers.end()) {
+ // Remove this from the seed routers and copy the last packet
+ // sent time.
+ RouterData r(router_addr); // accumulate state before we commit it.
+ r.m_xmit.m_time = group.removeSeedRouter(to_addr);
+
+ // Validate capabilities.
+ CapComp& caps = msg.m_capabilities;
+ ServiceGroup::PacketStyle ps;
+ ServiceGroup::CacheAssignmentStyle as;
+ char const* caps_tag = caps.isEmpty() ? "default" : "router";
+
+ ps = caps.isEmpty() ? ServiceGroup::GRE : caps.getPacketForwardStyle();
+ if (ServiceGroup::GRE & ps & group.m_packet_forward)
+ r.m_packet_forward = ServiceGroup::GRE;
+ else if (ServiceGroup::L2 & ps & group.m_packet_forward)
+ r.m_packet_forward = ServiceGroup::L2;
+ else
+ logf(zret, LVL_WARN, "Packet forwarding (config=%d, %s=%d) did not match.", group.m_packet_forward, caps_tag, ps);
+
+ ps = caps.isEmpty() ? ServiceGroup::GRE : caps.getPacketReturnStyle();
+ if (ServiceGroup::GRE & ps & group.m_packet_return)
+ r.m_packet_return = ServiceGroup::GRE;
+ else if (ServiceGroup::L2 & ps & group.m_packet_return)
+ r.m_packet_return = ServiceGroup::L2;
+ else
+ logf(zret, LVL_WARN, "Packet return (local=%d, %s=%d) did not match.", group.m_packet_return, caps_tag, ps);
+
+ as = caps.isEmpty() ? ServiceGroup::HASH_ONLY : caps.getCacheAssignmentStyle();
+ if (ServiceGroup::HASH_ONLY & as & group.m_cache_assign)
+ r.m_cache_assign = ServiceGroup::HASH_ONLY;
+ else if (ServiceGroup::MASK_ONLY & as & group.m_cache_assign)
+ r.m_cache_assign = ServiceGroup::MASK_ONLY;
+ else
+ logf(zret, LVL_WARN, "Cache assignment (local=%d, %s=%d) did not match.", group.m_cache_assign, caps_tag, as);
+
+ r.m_send_caps = ! caps.isEmpty();
+
+ if (!zret) {
+ // cancel out, can't use this packet because we reject the router.
+ return logf(zret, LVL_WARN, "Router %s rejected because of capabilities mismatch.", ip_addr_to_str(router_addr));
+ }
+
+ group.m_routers.push_back(r);
+ ar_spot = group.m_routers.end() - 1;
+ view_changed = true;
+ logf(LVL_INFO, "Added source router %s to view %d", ip_addr_to_str(router_addr), group.m_svc.getSvcId());
+ }
+ ar_spot->m_recv.set(now, recv_id);
+ ar_spot->m_generation = msg.m_router_view.getChangeNumber();
+ router_idx = ar_spot - group.m_routers.begin();
+
+ // For all the other listed routers, seed them if they're not
+ // already active.
+ uint32 nr = msg.m_router_view.getRouterCount();
+ for ( uint32 idx = 0; idx < nr ; ++idx ) {
+ uint32 addr = msg.m_router_view.getRouterAddr(idx);
+ if (group.m_routers.end() == find_by_member(group.m_routers, &RouterData::m_addr, addr))
+ group.seedRouter(addr);
+ }
+
+ // Update/Install the caches.
+ // TBD: Must bump view if a router fails to report a cache it reported
+ // in its last packet.
+ group.resizeCacheSources();
+ uint32 nc = msg.m_router_view.getCacheCount();
+ for ( uint32 idx = 0 ; idx < nc ; ++idx ) {
+ CacheIdElt& cache = msg.m_router_view.cacheElt(idx);
+ CacheBag::iterator ac_spot = group.findCache(cache.getAddr());
+ if (group.m_caches.end() == ac_spot) {
+ group.m_caches.push_back(CacheData());
+ ac_spot = group.m_caches.end() - 1;
+ ac_spot->m_src.resize(group.m_routers.size());
+ logf(LVL_INFO, "Added cache %s to view %d", ip_addr_to_str(cache.getAddr()), group.m_svc.getSvcId());
+ view_changed = true;
+ }
+ ac_spot->m_id = cache;
+ ac_spot->m_src[router_idx].set(now, recv_id);
+ }
+
+ if (view_changed) group.viewChanged(now);
+
+ return zret;
+}
+
+// ------------------------------------------------------
+inline uint32
+RouterImpl::CacheData::idAddr() const {
+ return m_id.getAddr();
+}
+
+RouterImpl::GroupData::GroupData() { }
+
+RouterImpl::CacheBag::iterator
+RouterImpl::GroupData::findCache(uint32 addr) {
+ return std::find_if(
+ m_caches.begin(),
+ m_caches.end(),
+ ats::predicate(&CacheData::idAddr, addr)
+ );
+}
+
+RouterImpl::GroupData&
+RouterImpl::defineServiceGroup(
+ ServiceGroup const& svc,
+ ServiceGroup::Result* result
+) {
+ uint8 svc_id = svc.getSvcId();
+ GroupMap::iterator spot = m_groups.find(svc_id);
+ GroupData* group; // service with target ID.
+ ServiceGroup::Result zret;
+ if (spot == m_groups.end()) { // not defined
+ group = &(m_groups[svc_id]);
+ group->m_svc = svc;
+ zret = ServiceGroup::DEFINED;
+ } else {
+ group = &spot->second;
+ zret = group->m_svc == svc ? ServiceGroup::EXISTS : ServiceGroup::CONFLICT;
+ }
+ if (result) *result = zret;
+ return *group;
+}
+
+void
+RouterImpl::GroupData::resizeRouterSources() {
+ ats::for_each(m_routers, &RouterData::resize, m_caches.size());
+}
+
+ats::Errata
+RouterImpl::handleHereIAm(IpHeader const& ip_hdr, ats::Buffer const& chunk) {
+ ats::Errata zret;
+ HereIAmMsg msg;
+ static GroupData nil_group; // scratch until I clean up the security.
+ // Set if our view of the group changes enough to bump the
+ // generation number.
+ bool view_changed = false;
+ int i; // scratch index var.
+ time_t now = time(0); // don't call this over and over.
+ int parse = msg.parse(chunk);
+
+ if (PARSE_SUCCESS != parse)
+ return log(LVL_INFO, "Ignored malformed WCCP2_HERE_I_AM message.\n");
+
+ if (!this->validateSecurity(msg, nil_group))
+ return log(LVL_INFO, "Ignored WCCP2_HERE_I_AM with invalid security.\n");
+
+ ServiceGroup svc(msg.m_service);
+ ServiceGroup::Result r;
+ GroupData& group = this->defineServiceGroup(svc, &r);
+ if (ServiceGroup::CONFLICT == r)
+ return logf(LVL_INFO, "WCCP2_HERE_I_AM ignored - service group %d definition does not match.\n", svc.getSvcId());
+ else if (ServiceGroup::DEFINED == r)
+ return logf(LVL_INFO, "Service group %d defined by WCCP2_HERE_I_AM.\n", svc.getSvcId());
+
+ // Check if this cache is already known.
+ uint32 cache_addr = msg.m_cache_id.getAddr();
+ int cache_idx;
+ uint32 cache_gen;
+ CacheBag::iterator cache = group.findCache(cache_addr);
+ if (cache == group.m_caches.end()) { // not known
+ group.m_caches.push_back(CacheData());
+ // Vector modified, need clean end value.
+ cache = group.m_caches.end() - 1;
+ cache->m_recv_count = 0;
+ group.resizeRouterSources();
+ view_changed = true;
+ } else {
+ // Did the cache mention us specifically?
+ // If so, make sure the sequence # is correct.
+ RouterIdElt* me = msg.m_cache_view.findf_router_elt(m_addr);
+ if (me && me->getRecvId() != cache->m_recv_count)
+ return logf(LVL_INFO, "Discarded out of date (recv=%d, local=%ld) WCCP2_HERE_I_AM.\n", me->getRecvId(), cache->m_recv_count);
+ }
+
+ cache_gen = msg.m_cache_view.getChangeNumber();
+
+ cache_idx = cache - group.m_caches.begin();
+ cache->m_id = msg.m_cache_id.idElt();
+ cache->m_recv.set(now, cache_gen);
+ cache->m_pending = true;
+ cache->m_to_addr = ip_hdr.m_dst;
+
+ // Add any new routers
+ i = msg.m_cache_view.getRouterCount();
+ while(i-- > 0) {
+ uint32 addr = msg.m_cache_view.routerElt(i).getAddr();
+ RouterBag::iterator spot = find_by_member(group.m_routers, &RouterData::m_addr, addr);
+ if (spot == group.m_routers.end()) {
+ group.m_routers.push_back(RouterData());
+ // Can't count on previous end value, modified container.
+ spot = group.m_routers.end() - 1;
+ spot->m_addr = addr;
+ spot->m_src.resize(group.m_caches.size());
+ view_changed = true;
+ }
+ spot->m_src[cache_idx].set(now, cache_gen);
+ }
+
+ if (view_changed) ++(group.m_generation);
+ return zret;
+}
+
+void
+RouterImpl::generateISeeYou(
+ ISeeYouMsg& msg,
+ GroupData& group,
+ CacheData& cache
+) {
+ int i;
+ size_t n_routers = group.m_routers.size();
+ size_t n_caches = group.m_caches.size();
+
+ // Not handling multi-cast so target caches is hardwired to 1.
+ msg.fill(group, this->setSecurity(msg, group), group.m_assign_info, 1, n_routers, n_caches);
+
+ // Fill in ID data not done by fill.
+ msg.m_router_id
+ .setIdElt(m_addr, cache.m_recv_count + 1)
+ .setToAddr(cache.m_to_addr)
+ .setFromAddr(0, cache.idAddr());
+ ;
+
+ // Fill view routers.
+ i = 0;
+ for ( RouterBag::iterator router = group.m_routers.begin(),
+ router_limit = group.m_routers.end();
+ router != router_limit;
+ ++router, ++i
+ ) {
+ msg.m_router_view.setRouterAddr(i, router->m_addr);
+ }
+
+ // Fill view caches.
+ i = 0;
+ for ( CacheBag::iterator spot = group.m_caches.begin(),
+ limit = group.m_caches.end();
+ spot != limit;
+ ++spot, ++i
+ ) {
+ msg.m_router_view.cacheElt(i) = spot->m_id;
+ }
+
+ msg.finalize();
+}
+
+int
+RouterImpl::xmitISeeYou() {
+ int zret = 0;
+ ISeeYouMsg msg;
+ MsgBuffer buffer;
+ sockaddr_in dst_addr;
+ time_t now = time(0);
+ static size_t const BUFFER_SIZE = 4096;
+ char* data = static_cast<char*>(alloca(BUFFER_SIZE));
+
+ memset(&dst_addr, 0, sizeof(dst_addr));
+ dst_addr.sin_family = AF_INET;
+ dst_addr.sin_port = htons(DEFAULT_PORT);
+ buffer.set(data, BUFFER_SIZE);
+
+ // Send out messages for each service group.
+ for ( GroupMap::iterator svc_spot = m_groups.begin(),
+ svc_limit = m_groups.end() ;
+ svc_spot != svc_limit ;
+ ++svc_spot
+ ) {
+ GroupData& group = svc_spot->second;
+
+ // Check each active cache in the group.
+ for ( CacheBag::iterator cache = group.m_caches.begin(),
+ cache_limit = group.m_caches.end() ;
+ cache != cache_limit ;
+ ++cache
+ ) {
+ if (!cache->m_pending) continue;
+
+ msg.setBuffer(buffer);
+ this->generateISeeYou(msg, group, *cache);
+ dst_addr.sin_addr.s_addr = cache->m_id.getAddr();
+ zret = sendto(m_fd, data, msg.getCount(), 0,
+ reinterpret_cast<sockaddr*>(&dst_addr), sizeof(dst_addr));
+ if (0 <= zret) {
+ cache->m_xmit.set(now, group.m_generation);
+ cache->m_pending = false;
+ cache->m_recv_count = msg.m_router_id.getRecvId();
+ logf(LVL_DEBUG, "I_SEE_YOU -> %s\n", ip_addr_to_str(cache->m_id.getAddr()));
+ }
+ else {
+ log_errno(LVL_WARN, "Router transmit failed -");
+ return zret;
+ }
+ }
+ }
+ return zret;
+}
+
+int
+RouterImpl::housekeeping() {
+ return this->xmitISeeYou();
+}
+
+bool
+RouterImpl::isConfigured() const {
+ return false;
+}
+// ------------------------------------------------------
+EndPoint::EndPoint() {
+}
+
+EndPoint::~EndPoint() {
+}
+
+EndPoint::EndPoint(self const& that)
+ : m_ptr(that.m_ptr) {
+}
+
+inline EndPoint::ImplType*
+EndPoint::instance() {
+ return m_ptr ? m_ptr.get() : this->make();
+}
+
+EndPoint& EndPoint::setAddr(uint32 addr) {
+ this->instance()->m_addr = addr;
+ logf(LVL_DEBUG, "Endpoint address set to %s\n", ip_addr_to_str(addr));
+ return *this;
+}
+
+bool
+EndPoint::isConfigured() const {
+ return m_ptr && m_ptr->isConfigured();
+}
+
+int
+EndPoint::open(uint32 addr) {
+ return this->instance()->open(addr);
+}
+
+void
+EndPoint::useMD5Security(char const* key) {
+ this->instance()->useMD5Security(key);
+}
+
+int EndPoint::getSocket() const {
+ return m_ptr ? m_ptr->m_fd : ats::NO_FD;
+}
+
+int
+EndPoint::housekeeping() {
+ // Don't force an instance because if there isn't one,
+ // there's no socket either.
+ return m_ptr && ats::NO_FD != m_ptr->m_fd ? m_ptr->housekeeping() : -ENOTCONN;
+}
+
+ats::Rv<int>
+EndPoint::handleMessage() {
+ return m_ptr
+ ? m_ptr->handleMessage()
+ : ats::Rv<int>(-ENOTCONN, log(LVL_INFO, "EndPoint::handleMessage called on unconnected instance"));
+}
+// ------------------------------------------------------
+Cache::Cache() {
+}
+
+Cache::~Cache() {
+}
+
+EndPoint::ImplType*
+Cache::make() {
+ m_ptr.assign(new ImplType);
+ return m_ptr.get();
+}
+
+inline Cache::ImplType*
+Cache::instance() {
+ return static_cast<ImplType*>(this->super::instance());
+}
+
+inline Cache::ImplType* Cache::impl() {
+ return static_cast<ImplType*>(m_ptr.get());
+}
+
+inline Cache::ImplType const* Cache::impl() const {
+ return static_cast<ImplType*>(m_ptr.get());
+}
+
+Cache::Service
+Cache::defineServiceGroup(
+ ServiceGroup const& svc,
+ ServiceGroup::Result* result
+) {
+ return Service(*this, this->instance()->defineServiceGroup(svc, result));
+}
+
+time_t Cache::waitTime() const {
+ return m_ptr ? this->impl()->waitTime() : std::numeric_limits<time_t>::max();
+}
+
+Cache&
+Cache::addSeedRouter(uint8 id, uint32 addr) {
+ this->instance()->seedRouter(id, addr);
+ return *this;
+}
+
+ats::Errata
+Cache::loadServicesFromFile(char const* path) {
+ return this->instance()->loadServicesFromFile(path);
+}
+// ------------------------------------------------------
+Router::Router() {
+}
+
+Router::~Router() {
+}
+
+EndPoint::ImplType*
+Router::make() {
+ m_ptr.assign(new ImplType);
+ return m_ptr.get();
+}
+
+inline Router::ImplType*
+Router::instance() {
+ return static_cast<ImplType*>(this->super::instance());
+}
+
+inline Router::ImplType*
+Router::impl() {
+ return static_cast<ImplType*>(m_ptr.get());
+}
+// ------------------------------------------------------
+} // namespace Wccp