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 2014/08/13 23:18:13 UTC

git commit: TS-698: Add IP filter to logging.

Repository: trafficserver
Updated Branches:
  refs/heads/master 83248169b -> 43bec913f


TS-698: Add IP filter to logging.


Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/43bec913
Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/43bec913
Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/43bec913

Branch: refs/heads/master
Commit: 43bec913f59a7542ee8e1ff76bdc365f18664102
Parents: 8324816
Author: Alan M. Carroll <am...@network-geographics.com>
Authored: Tue Aug 12 14:15:38 2014 -0500
Committer: Alan M. Carroll <am...@network-geographics.com>
Committed: Wed Aug 13 16:17:36 2014 -0500

----------------------------------------------------------------------
 .../configuration/logs_xml.config.en.rst        |  26 ++-
 lib/ts/IpMap.cc                                 |  10 +-
 lib/ts/IpMap.h                                  |  70 +++++-
 proxy/config/logs_xml.config.default            |  38 ++-
 proxy/logging/LogConfig.cc                      |   5 +-
 proxy/logging/LogFilter.cc                      | 232 +++++++++++++++++++
 proxy/logging/LogFilter.h                       |  40 ++++
 7 files changed, 375 insertions(+), 46 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/43bec913/doc/reference/configuration/logs_xml.config.en.rst
----------------------------------------------------------------------
diff --git a/doc/reference/configuration/logs_xml.config.en.rst b/doc/reference/configuration/logs_xml.config.en.rst
index ddf4cae..7924458 100644
--- a/doc/reference/configuration/logs_xml.config.en.rst
+++ b/doc/reference/configuration/logs_xml.config.en.rst
@@ -5,9 +5,9 @@
   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
@@ -138,6 +138,10 @@ The following list shows the ``LogFilter`` specifications.
     field type. For integer values, all of the operators are equivalent
     and mean that the field must be equal to the specified value.
 
+   For IP address fields, this can be a list of IP addresses and include ranges. A range is an IP address, followed by a
+   dash '``-``', and then another IP address of the same family. For instance, the 10/8 network can be represented by
+   ``10.0.0.0-10.255.255.255``. Currently network specifiers are not supported.
+
 .. note::
 
     There are no negative comparison operators. If you want to
@@ -233,15 +237,15 @@ The following list shows the ``LogObject`` specifications.
 
 ``<CollationHosts = "list_of_valid_hostnames:port|failover hosts"/>``
     Optional
-    A comma-separated list of collation servers (with pipe delimited 
-    failover servers) to which all log entries (for this object) are 
-    forwarded. Collation servers can be specified by name or IP address. 
-    Specify the collation port with a colon after the name. For example, 
-    in ``host1:5000|failhostA:5000|failhostB:6000, host2:6000`` logs 
-    would be sent to host1 and host2, with failhostA and failhostB 
-    acting as failover hosts for host1. When host1 disconnects, 
-    logs would be sent to failhostA. If failhostA disconnects, log 
-    entries would be sent to failhostB until host1 or failhostA comes 
+    A comma-separated list of collation servers (with pipe delimited
+    failover servers) to which all log entries (for this object) are
+    forwarded. Collation servers can be specified by name or IP address.
+    Specify the collation port with a colon after the name. For example,
+    in ``host1:5000|failhostA:5000|failhostB:6000, host2:6000`` logs
+    would be sent to host1 and host2, with failhostA and failhostB
+    acting as failover hosts for host1. When host1 disconnects,
+    logs would be sent to failhostA. If failhostA disconnects, log
+    entries would be sent to failhostB until host1 or failhostA comes
     back. Logs would also be sent to host2.
 
 ``<Header = "header"/>``

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/43bec913/lib/ts/IpMap.cc
----------------------------------------------------------------------
diff --git a/lib/ts/IpMap.cc b/lib/ts/IpMap.cc
index e6da38b..9a9421a 100644
--- a/lib/ts/IpMap.cc
+++ b/lib/ts/IpMap.cc
@@ -37,6 +37,14 @@
 // Validation / printing disabled until I figure out how to generalize so
 // as to not tie reporting into a particular project environment.
 
+/* @internal It is a bit ugly to store a @c sockaddr equivalent in the table
+    as all that is actually needed is the raw address. Unfortunately some clients
+    require a @c sockaddr* return via the iterator and that's expensive to
+    compute all the time. I should, at some point, re-examine this and see if we
+    can do better and have a more compact internal format. I suspect I did this
+    before we had IpAddr as a type.
+*/
+
 namespace ts { namespace detail {
 
 // Helper functions
@@ -1362,7 +1370,7 @@ IpMap::clear() {
 }
 
 IpMap::iterator
-IpMap::begin() {
+IpMap::begin() const {
   Node* x = 0;
   if (_m4) x = _m4->getHead();
   if (!x && _m6) x = _m6->getHead();

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/43bec913/lib/ts/IpMap.h
----------------------------------------------------------------------
diff --git a/lib/ts/IpMap.h b/lib/ts/IpMap.h
index 2a16269..917158f 100644
--- a/lib/ts/IpMap.h
+++ b/lib/ts/IpMap.h
@@ -250,7 +250,6 @@ namespace ts { namespace detail {
     of disjoint ranges. Marking and unmarking can take O(log n) and
     may require memory allocation / deallocation although this is
     minimized.
-
 */
 
 class IpMap {
@@ -305,8 +304,8 @@ public:
     /// Default constructor.
     iterator() : _tree(0), _node(0) {}
 
-    reference operator* (); //!< value operator
-    pointer operator -> (); //!< dereference operator
+    reference operator* () const; //!< value operator
+    pointer operator -> () const; //!< dereference operator
     self& operator++(); //!< next node (prefix)
     self operator++(int); //!< next node (postfix)
     self& operator--(); ///< previous node (prefix)
@@ -322,8 +321,8 @@ public:
     bool operator!=(self const& that) const { return ! (*this == that); }
   private:
     /// Construct a valid iterator.
-    iterator(IpMap* tree, Node* node) : _tree(tree), _node(node) {}
-      IpMap* _tree; ///< Container.
+    iterator(IpMap const* tree, Node* node) : _tree(tree), _node(node) {}
+      IpMap const* _tree; ///< Container.
       Node* _node; //!< Current node.
     };
 
@@ -351,6 +350,17 @@ public:
     void* data = 0 ///< Client data.
   );
 
+  /** Mark a range.
+      All addresses in the range [ @a min , @a max ] are marked with @a data.
+      @note Convenience overload for IPv4 addresses.
+      @return This object.
+  */
+  self& mark(
+    IpAddr const& min, ///< Minimum address (network order).
+    IpAddr const& max, ///< Maximum address (network order).
+    void* data = 0 ///< Client data.
+  );
+
   /** Mark an IPv4 address @a addr with @a data.
       This is equivalent to calling @c mark(addr, addr, data).
       @note Convenience overload for IPv4 addresses.
@@ -372,7 +382,7 @@ public:
     void* data = 0 ///< Client data.
   );
 
-  /** Mark an IPv6 address @a addr with @a data.
+  /** Mark an address @a addr with @a data.
       This is equivalent to calling @c mark(addr, addr, data).
       @note Convenience overload.
       @return This object.
@@ -410,7 +420,8 @@ public:
       range that are @b not present in the map are added. No
       previously present address is changed.
 
-      @note This is useful for filling in first match tables.
+      @note This is useful for filling in first match tables because @a data for already present
+      addresses is not changed.
 
       @return This object.
   */
@@ -456,11 +467,32 @@ public:
     void **ptr = 0 ///< Client data return.
   ) const;
 
+  /** Test for membership.
+
+      @note Convenience overload for @c IpEndpoint.
+
+      @return @c true if the address is in the map, @c false if not.
+      If the address is in the map and @a ptr is not @c NULL, @c *ptr
+      is set to the client data for the address.
+  */
   bool contains(
     IpEndpoint const* target, ///< Search target (network order).
     void **ptr = 0 ///< Client data return.
   ) const;
 
+  /** Test for membership.
+
+      @note Convenience overload for @c IpAddr.
+
+      @return @c true if the address is in the map, @c false if not.
+      If the address is in the map and @a ptr is not @c NULL, @c *ptr
+      is set to the client data for the address.
+  */
+  bool contains(
+    IpAddr const& target, ///< Search target (network order).
+    void **ptr = 0 ///< Client data return.
+  ) const;
+
   /** Remove all addresses from the map.
 
       @note This is much faster than @c unmark.
@@ -469,9 +501,9 @@ public:
   self& clear();
 
   /// Iterator for first element.
-  iterator begin();
+  iterator begin() const;
   /// Iterator past last element.
-  iterator end();
+  iterator end() const;
   /// @return Number of distinct ranges in the map.
   size_t getCount() const;
 
@@ -501,6 +533,13 @@ inline IpMap& IpMap::mark(in_addr_t addr, void* data) {
   return this->mark(addr, addr, data);
 }
 
+inline IpMap& IpMap::mark(IpAddr const& min, IpAddr const& max, void* data) {
+  IpEndpoint x,y;
+  x.assign(min);
+  y.assign(max);
+  return this->mark(&x.sa, &y.sa, data);
+}
+
 inline IpMap& IpMap::mark(IpEndpoint const* addr, void* data) {
   return this->mark(&addr->sa, &addr->sa, data);
 }
@@ -521,8 +560,15 @@ inline bool IpMap::contains(IpEndpoint const* target, void** ptr) const {
   return this->contains(&target->sa, ptr);
 }
 
+inline bool
+IpMap::contains(IpAddr const& addr, void** ptr) const {
+  IpEndpoint ip;
+  ip.assign(addr);
+  return this->contains(&ip.sa, ptr);
+}
+
 inline IpMap::iterator
-IpMap::end() {
+IpMap::end() const {
   return iterator(this, 0);
 }
 
@@ -546,12 +592,12 @@ IpMap::iterator::operator == (iterator const& that) const {
 }
 
 inline IpMap::iterator::reference
-IpMap::iterator::operator * () {
+IpMap::iterator::operator * () const {
   return *_node;
 }
 
 inline IpMap::iterator::pointer
-IpMap::iterator::operator -> () {
+IpMap::iterator::operator -> () const {
   return _node;
 }
 

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/43bec913/proxy/config/logs_xml.config.default
----------------------------------------------------------------------
diff --git a/proxy/config/logs_xml.config.default b/proxy/config/logs_xml.config.default
index 00bae06..37041b3 100644
--- a/proxy/config/logs_xml.config.default
+++ b/proxy/config/logs_xml.config.default
@@ -14,7 +14,7 @@ LogFormats
 An event log format specifies which fields are to be gathered from each
 protocol event access.  The following tags are possible for LogFormat
 specifications (a '*' denotes a tag that is required):
- 
+
 * <Name = "valid_format_name"/>
       Valid format names include anything except (squid, common, extended,
       extended2), which are the pre-defined formats.  There is no default.
@@ -48,8 +48,8 @@ specifications (a '*' denotes a tag that is required):
 
   <Interval = "aggregate_interval_secs"/>
       This tag is needed when the format contains any of the aggregate
-      operators.  The "aggregate_interval_secs" value is a number 
-      representing the number of seconds over which the entry is aggregated 
+      operators.  The "aggregate_interval_secs" value is a number
+      representing the number of seconds over which the entry is aggregated
       before being produced.  The valid set of aggregate operators are:
             COUNT	'*' can be used for the field value
 	    SUM
@@ -93,20 +93,20 @@ contains the following tags (a '*' denotes a tag that is required):
       value.
 
       The "valid_log_field"s are all Inktomi fields that are not a combination
-      of other (simpler) fields. 
+      of other (simpler) fields.
 
       "valid_filter_operator"s are:
 	MATCH
 	CASE_INSENSITIVE_MATCH
 	CONTAIN
 	CASE_INSENSITIVE_CONTAIN
-      These operators are all equivalent to MATCH for integer fields, but
-      provide different functionality for string fields:
+      These operators are all equivalent to MATCH for integer and IP address fields
+      but provide different functionality for string fields:
         MATCH: the field and the value must be identical strings
         CASE_INSENSITIVE_MATCH: the only difference between field and value
                                 is the case
         CONTAIN: value is a substring of field (case matters)
-        CASE_INSENSITIVE_CONTAIN: value is a substring of field (case does 
+        CASE_INSENSITIVE_CONTAIN: value is a substring of field (case does
                                   not matter)
 
 * <Action = "valid_action"/>
@@ -147,7 +147,7 @@ Example2: do not log requests for domain unwanted.com
   <Filename = "file_name"/>
       This tag specifies the name of the file to which this log object
       writes. This can be a local file, or a file on a different machine
-      if this Traffic Server is a collation client (see the LogHostName 
+      if this Traffic Server is a collation client (see the LogHostName
       tag below.)
       All filenames are relative to the default logging directory. If
       "file_name" has no file extension, then a ".log" extension will
@@ -166,7 +166,7 @@ Example2: do not log requests for domain unwanted.com
   <Mode = "valid_logging_mode"/>
       Valid logging modes include ascii, binary, and ascii_pipe.
       ascii: write log in human readable form (plain ascii).
-      binary: write log in a binary format that can later be read using 
+      binary: write log in a binary format that can later be read using
       the logcat utility.
       ascii_pipe: do not write log to a regular file, but to a named UNIX
       pipe (this option is only available on Linux and Solaris).
@@ -174,7 +174,7 @@ Example2: do not log requests for domain unwanted.com
   <Filters = "list of valid filter names"/>
       A comma separated list of valid filter names to apply to this
       object. Valid filter names include any previously-defined LogFilter.
-      If more than one filter is specified, then ALL filters should 
+      If more than one filter is specified, then ALL filters should
       accept a record for it to be logged (in other words, filters are
       ANDed together.)
 
@@ -187,7 +187,7 @@ Example2: do not log requests for domain unwanted.com
 
   <ServerHosts = "list of servers"/>
       This tag provides an easy way to create a filter that logs only the
-      requests to hosts in the comma separated list. Only entries from the 
+      requests to hosts in the comma separated list. Only entries from the
       named servers will be included in the log file. (Servers can only
       be specified by name, not by ip.)
 
@@ -212,7 +212,7 @@ Example2: do not log requests for domain unwanted.com
 
   <RollingEnabled = "rolling_enabled_value"/>
       This tag specifies the automatic rolling mode for the LogObject.
-      It overrides the configuration variable rolling_enabled. 
+      It overrides the configuration variable rolling_enabled.
       The possible values of rolling_enabled_value are as follows:
       0: do not roll
          do not automatically roll this log file
@@ -230,7 +230,7 @@ Example2: do not log requests for domain unwanted.com
 	 if the size of the file equals or exceeds the specified size
 
   <RollingIntervalSec = "seconds"/>
-      This tag specifies the seconds between consecutive log file 
+      This tag specifies the seconds between consecutive log file
       rolls for the LogObject. It overrides the configuration variable
       rolling_interval_sec.
 
@@ -246,7 +246,7 @@ Example2: do not log requests for domain unwanted.com
       This tag specifies the size (in megabytes) the log file must reach
       before it is rolled if rolling is based on size.
 
-  Please note: 
+  Please note:
 
   - The "Format" and "Filename" tags are mandatory, all others are optional.
 
@@ -276,7 +276,7 @@ Example2: do not log requests for domain unwanted.com
   Example2: create a local log file for the minimal format defined above, but
   do not log the domain unwanted.com (use the filter defined
   above). Create a binary log rather than an ascii log.
- 
+
   <LogObject>
       <Format = "minimal"/>
       <Filename = "minimal_without_unwanted"/>
@@ -284,7 +284,7 @@ Example2: do not log requests for domain unwanted.com
       <Mode = "binary"/>
   </LogObject>
 
-  Example3: like example2, but log only REFRESH_HIT entries (from all domains 
+  Example3: like example2, but log only REFRESH_HIT entries (from all domains
   except unwanted.com) and use the default ascii mode.
 
   <LogObject>
@@ -355,8 +355,8 @@ WebTrends Enhanced Log Format
 -----------------------------
 
 The following <LogFormat> is compatible with the WebTrends Enhanced Log
-Format. If you want to generate a log that can be parsed by WebTrends 
-reporting tools, simply create a <LogObject> that uses this format. 
+Format. If you want to generate a log that can be parsed by WebTrends
+reporting tools, simply create a <LogObject> that uses this format.
 
 -------------------------------------------------------------------------->
 
@@ -387,5 +387,3 @@ simply create a <LogObject> that uses this format.
   <Name = "squid_seconds_only_timestamp"/>
   <Format = "%<cqts> %<ttms> %<chi> %<crc>/%<pssc> %<psql> %<cqhm> %<cquc> %<caun> %<phr>/%<pqsn> %<psct>"/>
 </LogFormat>
-
-

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/43bec913/proxy/logging/LogConfig.cc
----------------------------------------------------------------------
diff --git a/proxy/logging/LogConfig.cc b/proxy/logging/LogConfig.cc
index 297f7f2..171c77b 100644
--- a/proxy/logging/LogConfig.cc
+++ b/proxy/logging/LogConfig.cc
@@ -1928,8 +1928,9 @@ LogConfig::read_xml_log_config(int from_memory)
         break;
 
       case LogField::IP:
-        Warning("Internal error: IP filters not yet supported cannot create filter %s.", filter_name);
-        continue;
+
+        filter = new LogFilterIP(filter_name, logfield, act, oper, val_str);
+        break;
 
       default:
 

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/43bec913/proxy/logging/LogFilter.cc
----------------------------------------------------------------------
diff --git a/proxy/logging/LogFilter.cc b/proxy/logging/LogFilter.cc
index aee5bc7..22ffd17 100644
--- a/proxy/logging/LogFilter.cc
+++ b/proxy/logging/LogFilter.cc
@@ -629,6 +629,233 @@ LogFilterInt::display_as_XML(FILE * fd)
   fprintf(fd, "</LogFilter>\n");
 }
 
+/*-------------------------------------------------------------------------
+  LogFilterIP::LogFilterIP
+  -------------------------------------------------------------------------*/
+LogFilterIP::LogFilterIP(const char *name, LogField * field,
+                           LogFilter::Action action, LogFilter::Operator oper, IpAddr value)
+  : LogFilter(name, field, action, oper)
+{
+  m_map.mark(value,value);
+  this->init();
+}
+ 
+LogFilterIP::LogFilterIP(const char *name, LogField * field,
+                           LogFilter::Action action, LogFilter::Operator oper, size_t num_values, IpAddr* value)
+  : LogFilter(name, field, action, oper)
+{
+  for ( IpAddr* limit = value + num_values ; value != limit ; ++value )
+    m_map.mark(*value,*value);
+  this->init();
+}
+
+LogFilterIP::LogFilterIP(const char *name, LogField * field,
+                           LogFilter::Action action, LogFilter::Operator oper, char *values)
+  : LogFilter(name, field, action, oper)
+{
+  // parse the comma-separated list of values and construct array
+  //
+  size_t i = 0;
+  SimpleTokenizer tok(values, ',');
+  size_t n = tok.getNumTokensRemaining();
+  char* t; // temp token pointer.
+
+  if (n) {
+    while (t = tok.getNext(), t != NULL) {
+      IpAddr min, max;
+      char* x = strchr(t, '-');
+      if (x) *x++ = 0;
+      if (0 == min.load(t)) {
+        if (x) {
+          if (0 != max.load(x)) {
+            Warning("LogFilterIP Configuration: '%s-%s' looks like a range but the second address was ill formed", t,x);
+            continue;
+          }
+        } else {
+          max = min;
+        }
+        m_map.mark(min,max);
+        ++i;
+      } else {
+        Warning("LogFilterIP Configuration:  '%s' is ill formed", t);
+      }
+    }
+    if (i < n) {
+      Warning("There were invalid IP values in the definition of filter %s"
+              " only %zu out of %zu values will be used.", name, i, n);
+    }
+  } else {
+    Warning("No values in the definition of filter %s.", name);
+  }
+  this->init();
+}
+
+LogFilterIP::LogFilterIP(const LogFilterIP & rhs)
+  : LogFilter(rhs.m_name, rhs.m_field, rhs.m_action, rhs.m_operator)
+{
+  for ( IpMap::iterator spot(rhs.m_map.begin()), limit(rhs.m_map.end()) ; spot != limit ; ++spot ) {
+    m_map.mark(spot->min(), spot->max(), spot->data());
+  }
+  this->init();
+}
+
+void
+LogFilterIP::init()
+{
+  m_type = IP_FILTER;
+  m_num_values = m_map.getCount();
+}
+
+/*-------------------------------------------------------------------------
+  LogFilterIP::~LogFilterIP
+  -------------------------------------------------------------------------*/
+
+LogFilterIP::~LogFilterIP()
+{
+}   
+    
+/*-------------------------------------------------------------------------
+  LogFilterIP::operator==
+
+  This operator is not very intelligent and expects the objects being
+  compared to have the same values specified *in the same order*.
+  Filters with the same values specified in different order are considered
+  to be different.
+
+  -------------------------------------------------------------------------*/
+
+bool
+LogFilterIP::operator==(LogFilterIP & rhs)
+{
+  if (m_type == rhs.m_type &&
+      *m_field == *rhs.m_field &&
+      m_action == rhs.m_action &&
+      m_operator == rhs.m_operator &&
+      m_num_values == rhs.m_num_values) {
+    
+    IpMap::iterator left_spot(m_map.begin());
+    IpMap::iterator left_limit(m_map.end());
+    IpMap::iterator right_spot(rhs.m_map.begin());
+    IpMap::iterator right_limit(rhs.m_map.end());
+
+    while (left_spot != left_limit && right_spot != right_limit) {
+      if (!ats_ip_addr_eq(left_spot->min(), right_spot->min()) ||
+          !ats_ip_addr_eq(left_spot->max(), right_spot->max()))
+        break;
+      ++left_spot;
+      ++right_spot;
+    }
+
+    return left_spot == left_limit && right_spot == right_limit;
+  }
+  return false;
+}
+
+/*-------------------------------------------------------------------------
+  LogFilterIP::toss_this_entry
+  -------------------------------------------------------------------------*/
+
+bool
+LogFilterIP::is_match(LogAccess* lad)
+{
+  bool zret = false;
+
+  if (m_field && lad) {
+    LogFieldIpStorage value;
+    m_field->marshal(lad, reinterpret_cast<char *>(&value));
+    // This is bad, we abuse the fact that the initial layout of LogFieldIpStorage and IpAddr
+    // are identical. We should look at converting the log stuff to use IpAddr directly.
+    zret = m_map.contains(reinterpret_cast<IpAddr&>(value));
+  }
+
+  return zret;
+}
+
+bool 
+LogFilterIP::toss_this_entry(LogAccess* lad)
+{
+  bool cond_satisfied = this->is_match(lad);
+  return (m_action == REJECT && cond_satisfied) || (m_action == ACCEPT && !cond_satisfied);
+}
+
+bool
+LogFilterIP::wipe_this_entry(LogAccess*)
+{
+# if 0
+  bool zret = WIPE_FIELD_VALUE == m_action && this->is_match(lad);
+  if (zret) {
+    // set to ADDR_ANY.
+  }
+  return zret;
+# else
+  return false;
+# endif
+}
+
+/*-------------------------------------------------------------------------
+  LogFilterIP::display
+  -------------------------------------------------------------------------*/
+
+void
+LogFilterIP::displayRange(FILE* fd, IpMap::iterator const& iter)
+{
+  ip_text_buffer ipb;
+
+  fprintf(fd, "%s", ats_ip_ntop(iter->min(), ipb, sizeof(ipb)));
+
+  if (!ats_ip_addr_eq(iter->min(), iter->max()))
+    fprintf(fd, "-%s", ats_ip_ntop(iter->max(), ipb, sizeof(ipb)));
+}
+
+void
+LogFilterIP::displayRanges(FILE * fd)
+{
+  IpMap::iterator spot(m_map.begin()), limit(m_map.end());
+  ink_assert(spot != limit);
+
+  this->displayRange(fd, spot);
+  for ( ++spot ; spot != limit ; ++spot ) {
+    for (size_t i = 1; i < m_num_values; ++i) {
+      fprintf(fd, ",");
+      this->displayRange(fd, spot);
+    }
+  }
+}
+
+void
+LogFilterIP::display(FILE* fd)
+{
+  ink_assert(fd != NULL);
+
+  if (0 == m_map.getCount()) {
+    fprintf(fd, "Filter \"%s\" is inactive, no values specified\n", m_name);
+  } else {
+    fprintf(fd, "Filter \"%s\" %sS records if %s %s ", m_name, ACTION_NAME[m_action], m_field->symbol(), OPERATOR_NAME[m_operator]);
+    this->displayRanges(fd);
+    fprintf(fd, "\n");
+  }
+}
+
+ 
+void 
+LogFilterIP::display_as_XML(FILE * fd)
+{  
+  ink_assert(fd != NULL);
+  fprintf(fd,
+          "<LogFilter>\n"
+          "  <Name      = \"%s\"/>\n" 
+          "  <Action    = \"%s\"/>\n"
+          "  <Condition = \"%s %s ", m_name, ACTION_NAME[m_action], m_field->symbol(), OPERATOR_NAME[m_operator]);
+
+  if (m_map.getCount() == 0) {
+    fprintf(fd, "<no values>");  
+  } else {
+    this->displayRanges(fd);
+  }
+  fprintf(fd, "\"/>\n");
+  fprintf(fd, "</LogFilter>\n");
+}
+
 bool
 filters_are_equal(LogFilter * filt1, LogFilter * filt2)
 {
@@ -639,6 +866,8 @@ filters_are_equal(LogFilter * filt1, LogFilter * filt2)
     if (filt1->type() == LogFilter::INT_FILTER) {
       Debug("log-filter-compare", "int compare");
       ret = (*((LogFilterInt *) filt1) == *((LogFilterInt *) filt2));
+    } else if (filt1->type() == LogFilter::IP_FILTER) {
+      ret = (*((LogFilterIP *) filt1) == *((LogFilterIP *) filt2));
     } else if (filt1->type() == LogFilter::STRING_FILTER) {
       ret = (*((LogFilterString *) filt1) == *((LogFilterString *) filt2));
     } else {
@@ -720,6 +949,9 @@ LogFilterList::add(LogFilter * filter, bool copy)
     if (filter->type() == LogFilter::INT_FILTER) {
       LogFilterInt *f = new LogFilterInt(*((LogFilterInt *) filter));
       m_filter_list.enqueue(f);
+    } else if (filter->type() == LogFilter::IP_FILTER) {   
+      LogFilterIP *f = new LogFilterIP(*((LogFilterIP *) filter));
+      m_filter_list.enqueue(f);
     } else {
       LogFilterString *f = new LogFilterString(*((LogFilterString *) filter));
       m_filter_list.enqueue(f);

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/43bec913/proxy/logging/LogFilter.h
----------------------------------------------------------------------
diff --git a/proxy/logging/LogFilter.h b/proxy/logging/LogFilter.h
index d754e0e..35590ec 100644
--- a/proxy/logging/LogFilter.h
+++ b/proxy/logging/LogFilter.h
@@ -26,6 +26,7 @@
 #define LOG_FILTER_H
 
 #include "libts.h"
+#include "IpMap.h"
 #include "LogAccess.h"
 #include "LogField.h"
 #include "LogFormat.h"
@@ -45,6 +46,7 @@ public:
   {
     INT_FILTER = 0,
     STRING_FILTER,
+    IP_FILTER,
     N_TYPES
   };
 
@@ -192,6 +194,44 @@ private:
   LogFilterInt & operator=(LogFilterInt & rhs);
 };
 
+/*-------------------------------------------------------------------------
+  LogFilterIP
+  
+  Filter for IP fields using IpAddr.
+  -------------------------------------------------------------------------*/
+class LogFilterIP:public LogFilter
+{
+public:
+  LogFilterIP(const char *name, LogField * field, Action a, Operator o, IpAddr value);
+  LogFilterIP(const char *name, LogField * field, Action a, Operator o, size_t num_values,  IpAddr* value);
+  LogFilterIP(const char *name, LogField * field, Action a, Operator o, char *values);
+  LogFilterIP(const LogFilterIP & rhs);
+  ~LogFilterIP();
+
+  bool operator==(LogFilterIP & rhs);
+
+  virtual bool toss_this_entry(LogAccess * lad);
+  virtual bool wipe_this_entry(LogAccess * lad);
+  void display(FILE * fd = stdout);
+  void display_as_XML(FILE * fd = stdout);
+
+private:
+  IpMap m_map;
+
+  /// Initialization common to all constructors.
+  void init();
+
+  void displayRanges(FILE* fd);
+  void displayRange(FILE* fd, IpMap::iterator const& iter);
+
+  // Checks for a match on this filter.
+  bool is_match(LogAccess* lad);
+  
+  // -- member functions that are not allowed --
+  LogFilterIP();
+  LogFilterIP & operator=(LogFilterIP & rhs);
+};
+
 bool filters_are_equal(LogFilter * filt1, LogFilter * filt2);