This patch adds Netfilter mark support and overhauls existing QOS features.
Netfilter mark support is added to all existing TOS functionality including
qos_flows, clientside_tos and tcp_outgoing_tos.
Netfilter QOS marking requires the libnetfilter_conntrack library. Netfilter
marking for clientside_tos and tcp_outgoing_tos requires no additional
libraries.
=== modified file 'configure.in'
--- configure.in 2010-09-05 09:27:34 +0000
+++ configure.in 2010-09-05 17:03:00 +0000
@@ -1279,6 +1279,43 @@
AC_MSG_NOTICE([Linux Netfilter support requested: ${enable_linux_netfilter:=auto}])
#will be AC_DEFINEd later, after checking for appropriate infrastructure
+
+dnl Look for libnetfilter_conntrack options (needed for QOS netfilter marking)
+AC_ARG_WITH(netfilter-conntrack,
+ AS_HELP_STRING([--with-netfilter-conntrack=PATH],
+ [Compile with the Netfilter conntrack libraries. The path to
+ the development libraries and headers
+ installation can be specified if outside of the
+ system standard directories (default=autodetect)]), [
+ case "$withval" in
+ yes) unset netfilterconntrackpath ;;
+ no) netfilterconntrackpath=no ;;
+ *) netfilterconntrackpath=$withval ;;
+ esac
+])
+if test x"$netfilterconntrackpath" != "xno"; then
+ if test "x$netfilterconntrackpath" != "x"; then
+ if ! test -x "$netfilterconntrackpath"; then
+ AC_MSG_ERROR([--with-netfilter-conntrack path '$netfilterconntrackpath' not executable])
+ fi
+ LDFLAGS="-L$netfilterconntrackpath/lib $LDFLAGS"
+ CPPFLAGS="-I$netfilterconntrackpath/include $CPPFLAGS"
+ with_netfilter_conntrack=yes
+ else
+ AC_CHECK_LIB([netfilter_conntrack], [nfct_query],,
+ AC_MSG_ERROR([libnetfilter-conntrack library not found. Needed for netfilter-conntrack support]),
+ [-lnetfilter_conntrack])
+ AC_CHECK_HEADERS([libnetfilter_conntrack/libnetfilter_conntrack.h \
+ libnetfilter_conntrack/libnetfilter_conntrack_tcp.h])
+ if test "x$ac_cv_lib_netfilter_conntrack_nfct_query" = "xyes" \
+ -a "x$ac_cv_header_libnetfilter_conntrack_h"="xyes" \
+ -a "x$ac_cv_header_libnetfilter_conntrack_tcp_h"="xyes"; then
+ with_netfilter_conntrack=yes
+ fi
+ fi
+fi
+
+
dnl Enable Large file support
buildmodel=""
squid_opt_enable_large_files=no
@@ -2009,10 +2046,19 @@
SQUID_YESNO([$enableval],
[unrecognized argument to --enable-zph-qos: $enableval])
])
-SQUID_DEFINE_BOOL(USE_ZPH_QOS,${enable_zph_qos:=no},
+SQUID_DEFINE_BOOL(USE_QOS_TOS,${enable_zph_qos:=no},
[Enable Zero Penalty Hit QOS. When set, Squid will alter the
TOS field of HIT responses to help policing network traffic])
AC_MSG_NOTICE([ZPH QOS enabled: $enable_zph_qos])
+if test "$enable_zph_qos" = "yes" ; then
+ if test "$with_netfilter_conntrack" = "yes" ; then
+ AC_MSG_NOTICE([QOS netfilter marking enabled: $with_netfilter_conntrack])
+ SQUID_DEFINE_BOOL(USE_QOS_NFMARK,$with_netfilter_conntrack,
+ [Enable support for QOS netfilter packet marking])
+ else
+ AC_MSG_WARN([--with-netfilter-conntrack not enabled. QOS features will not support Netfilter marking.])
+ fi
+fi
if $CPPUNITCONFIG --help >/dev/null; then
squid_cv_cppunit_version="`$CPPUNITCONFIG --version`"
=== modified file 'doc/release-notes/release-3.2.sgml'
--- doc/release-notes/release-3.2.sgml 2010-09-03 13:30:11 +0000
+++ doc/release-notes/release-3.2.sgml 2010-09-05 16:15:22 +0000
@@ -403,6 +403,25 @@
Please check and update your squid.conf to use the text none for no limit instead of the old 0 (zero).
All users upgrading need to be aware that from Squid-3.3 setting this option to 0 (zero) will mean zero bytes of memory get pooled.
+ qos_flows
+
New options mark and tos
+
tos retains the original QOS functionality of the IP header TOS field.
+
mark offers the same functionality, but with a netfilter mark value.
+
These options should be placed immediately after qos_flows.
+
The tos value is optional in order to maintain backwards compatability.
+
This feature requires libnetfilter_conntrack, which must be included during compilation using --with-netfilter-conntrack.
+
The preserve-miss functionality is available with the mark option and requires no kernel patching.
+
+ tcp_outgoing_tos
+
This parameter is now compatible with persistent server connections.
+
+ clientside_mark and tcp_outgoing_mark
+
New configuration parameters clientside_mark and tcp_outgoing_mark
+
These 2 new configuration parameters allow packets leaving Squid on both the server and client side to be marked with a Netfilter mark value.
+
clientside_mark marks packets on the client side, in the same way as the existing clientside_tos feature.
+
tcp_outgoing_mark marks packets on the server side, in the same way as the existing tcp_outgoing_tos feature.
+
This feature is only available for Netfilter environments. Unlike qos_flows, libnetfilter_conntrack is not required.
+
windows_ipaddrchangemonitor
Now only available to be set in Windows builds.
@@ -479,6 +498,9 @@
Currently one demo helper fake is provided in shell and C++ forms to demonstrate
the helper protocol usage and provide exemplar code.
+ --with-netfiler-conntrack
+
Includes the libnetfilter_conntrack library, required for the new qos_flows option mark .
+
Changes to existing options
=== modified file 'src/ClientRequestContext.h'
--- src/ClientRequestContext.h 2010-02-07 03:38:46 +0000
+++ src/ClientRequestContext.h 2010-09-04 15:10:14 +0000
@@ -50,7 +50,8 @@
bool redirect_done;
bool no_cache_done;
bool interpreted_req_hdrs;
- bool clientside_tos_done;
+ bool clientsideTosDone;
+ bool clientsideNfmarkDone;
private:
CBDATA_CLASS(ClientRequestContext);
=== modified file 'src/Parsing.cc'
--- src/Parsing.cc 2010-07-25 08:10:12 +0000
+++ src/Parsing.cc 2010-08-18 22:08:55 +0000
@@ -210,3 +210,42 @@
return true;
}
+
+bool
+xstrtoul(const char *s, char **end, unsigned long *value,
+ unsigned long min, unsigned long max)
+{
+ unsigned long v;
+ char *my_end;
+
+ errno = 0;
+ v = strtoul(s, &my_end, 0);
+
+ if (my_end == s)
+ return false;
+ if (end != NULL)
+ *end = my_end;
+
+ if (errno != ERANGE && min <= v && (max == 0 || v <= max)) {
+ if (value != NULL)
+ *value = v;
+ if (end == NULL)
+ return *my_end == '\0';
+ return true;
+ }
+
+ return false;
+}
+
+bool
+xstrtoui(const char *s, char **end, unsigned int *value,
+ unsigned int min, unsigned int max)
+{
+ unsigned long v;
+ bool ret;
+
+ ret = xstrtoul(s, end, &v, min, max);
+ if (value != NULL)
+ *value = v;
+ return ret;
+}
=== modified file 'src/Parsing.h'
--- src/Parsing.h 2010-05-26 03:06:02 +0000
+++ src/Parsing.h 2010-08-23 21:11:46 +0000
@@ -68,4 +68,25 @@
*/
extern bool GetHostWithPort(char *token, Ip::Address *ipa);
+/**
+ * xstrtou{i,l} - string to number conversion
+ * @param s input string
+ * @param end like strtoul's "end" pointer
+ * @param value pointer for result
+ * @param min minimum accepted value
+ * @param max maximum accepted value
+ *
+ * If @end is NULL, we assume the caller wants a "strict strtoul", and hence
+ * "15a" is rejected.
+ * In either case, the value obtained is compared for min-max compliance.
+ * Base is always 0, i.e. autodetect depending on @s.
+ *
+ * @return Returns true/false whether number was accepted. On failure, *value has
+ * undefined contents.
+ */
+extern bool xstrtoul(const char *s, char **end, unsigned long *value,
+ unsigned long min, unsigned long max);
+extern bool xstrtoui(const char *s, char **end, unsigned int *value,
+ unsigned int min, unsigned int max);
+
#endif /* SQUID_PARSING_H */
=== modified file 'src/cache_cf.cc'
--- src/cache_cf.cc 2010-08-25 03:08:37 +0000
+++ src/cache_cf.cc 2010-09-05 07:50:10 +0000
@@ -57,9 +57,10 @@
#include "HttpRequestMethod.h"
#include "ident/Config.h"
#include "ip/Intercept.h"
-#include "ip/QosConfig.h"
+#include "ip/Qos.h"
#include "ip/tools.h"
#include "log/Config.h"
+#include
#include "MemBuf.h"
#include "Parsing.h"
#include "ProtoPort.h"
@@ -1252,8 +1253,7 @@
{
acl_tos *l;
acl_tos **tail = head; /* sane name below */
- int tos;
- char junk;
+ unsigned int tos; /* Initially uint for strtoui. Casted to unsigned char before return */
char *token = strtok(NULL, w_space);
if (!token) {
@@ -1261,12 +1261,8 @@
return;
}
- if (sscanf(token, "0x%x%c", &tos, &junk) != 1) {
- self_destruct();
- return;
- }
-
- if (tos < 0 || tos > 255) {
+ // Changed from sscanf for greater flexibility for inputs and error checking
+ if (!xstrtoui(token, NULL, &tos, 0, std::numeric_limits::max())) {
self_destruct();
return;
}
@@ -1275,7 +1271,7 @@
l = cbdataAlloc(acl_tos);
- l->tos = tos;
+ l->tos = (unsigned char)tos;
aclParseAclList(LegacyParser, &l->aclList);
@@ -1296,6 +1292,76 @@
}
}
+CBDATA_TYPE(acl_nfmark);
+
+static void
+dump_acl_nfmark(StoreEntry * entry, const char *name, acl_nfmark * head)
+{
+ acl_nfmark *l;
+
+ for (l = head; l; l = l->next) {
+ if (l->nfmark > 0)
+ storeAppendPrintf(entry, "%s 0x%02X", name, l->nfmark);
+ else
+ storeAppendPrintf(entry, "%s none", name);
+
+ dump_acl_list(entry, l->aclList);
+
+ storeAppendPrintf(entry, "\n");
+ }
+}
+
+static void
+freed_acl_nfmark(void *data)
+{
+ acl_nfmark *l = static_cast(data);
+ aclDestroyAclList(&l->aclList);
+}
+
+static void
+parse_acl_nfmark(acl_nfmark ** head)
+{
+ acl_nfmark *l;
+ acl_nfmark **tail = head; /* sane name below */
+ uint32_t mark;
+ char *token = strtok(NULL, w_space);
+
+ if (!token) {
+ self_destruct();
+ return;
+ }
+
+ // Changed from sscanf for greater flexibility for inputs and error checking
+ if (!xstrtoui(token, NULL, &mark, 0, std::numeric_limits::max())) {
+ self_destruct();
+ return;
+ }
+
+ CBDATA_INIT_TYPE_FREECB(acl_nfmark, freed_acl_nfmark);
+
+ l = cbdataAlloc(acl_nfmark);
+
+ l->nfmark = mark;
+
+ aclParseAclList(LegacyParser, &l->aclList);
+
+ while (*tail)
+ tail = &(*tail)->next;
+
+ *tail = l;
+}
+
+static void
+free_acl_nfmark(acl_nfmark ** head)
+{
+ while (*head) {
+ acl_nfmark *l = *head;
+ *head = l->next;
+ l->next = NULL;
+ cbdataFree(l);
+ }
+}
+
CBDATA_TYPE(acl_size_t);
static void
=== modified file 'src/cf.data.depend'
--- src/cf.data.depend 2010-04-05 10:29:50 +0000
+++ src/cf.data.depend 2010-09-04 22:12:41 +0000
@@ -5,6 +5,7 @@
acl_address acl
acl_b_size_t acl
acl_tos acl
+acl_nfmark acl
address
authparam
b_int64_t
=== modified file 'src/cf.data.pre'
--- src/cf.data.pre 2010-08-31 13:19:37 +0000
+++ src/cf.data.pre 2010-09-05 17:21:02 +0000
@@ -1481,10 +1481,10 @@
NAME: tcp_outgoing_tos tcp_outgoing_ds tcp_outgoing_dscp
TYPE: acl_tos
DEFAULT: none
-LOC: Config.accessList.outgoing_tos
+LOC: Config.accessList.outgoingTos
DOC_START
- Allows you to select a TOS/Diffserv value to mark outgoing
- connections with, based on the username or source address
+ Allows you to select a TOS/Diffserv value for packets outgoing
+ on the server side, based on the username or source address
making the request.
tcp_outgoing_tos ds-field [!]aclname ...
@@ -1509,41 +1509,101 @@
Processing proceeds in the order specified, and stops at first fully
matching line.
- Note: The use of this directive using client dependent ACLs is
- incompatible with the use of server side persistent connections. To
- ensure correct results it is best to set server_persisten_connections
- to off when using this directive in such configurations.
+ This feature is now compatible with persistent server connections.
DOC_END
NAME: clientside_tos
TYPE: acl_tos
DEFAULT: none
-LOC: Config.accessList.clientside_tos
+LOC: Config.accessList.clientsideTos
DOC_START
- Allows you to select a TOS/Diffserv value to mark client-side
- connections with, based on the username or source address
+ Allows you to select a TOS/Diffserv value for packets being transmitted
+ on the client-side, based on the username or source address
making the request.
+
+ clientside_tos ds-field [!]aclname ...
+
+ Example where normal_service_net uses the TOS value 0x00
+ and good_service_net uses 0x20
+
+ acl normal_service_net src 10.0.0.0/24
+ acl good_service_net src 10.0.1.0/24
+ clientside_tos 0x00 normal_service_net
+ clientside_tos 0x20 good_service_net
+
+ Note: This feature is incompatible with qos_flows. Any TOS values set here
+ will be overwritten by TOS values in qos_flows.
+DOC_END
+
+NAME: tcp_outgoing_mark
+TYPE: acl_nfmark
+IFDEF: SO_MARK
+DEFAULT: none
+LOC: Config.accessList.outgoingNfmark
+DOC_START
+ Allows you to apply a Netfilter mark value to outgoing packets
+ on the server side, based on the username or source address making
+ the request.
+
+ tcp_outgoing_mark mark-value [!]aclname ...
+
+ Example where normal_service_net uses the mark value 0x00
+ and good_service_net uses 0x20
+
+ acl normal_service_net src 10.0.0.0/255.255.255.0
+ acl good_service_net src 10.0.1.0/255.255.255.0
+ tcp_outgoing_mark 0x00 normal_service_net
+ tcp_outgoing_mark 0x20 good_service_net
+DOC_END
+
+NAME: clientside_mark
+TYPE: acl_nfmark
+IFDEF: SO_MARK
+DEFAULT: none
+LOC: Config.accessList.clientsideNfmark
+DOC_START
+ Allows you to apply a Netfilter mark value to packets being transmitted
+ on the client-side, based on the username or source address making
+ the request.
+
+ clientside_mark mark-value [!]aclname ...
+
+ Example where normal_service_net uses the mark value 0x00
+ and good_service_net uses 0x20
+
+ acl normal_service_net src 10.0.0.0/255.255.255.0
+ acl good_service_net src 10.0.1.0/255.255.255.0
+ clientside_mark 0x00 normal_service_net
+ clientside_mark 0x20 good_service_net
+
+ Note: This feature is incompatible with qos_flows. Any mark values set here
+ will be overwritten by mark values in qos_flows.
DOC_END
NAME: qos_flows
TYPE: QosConfig
-IFDEF: USE_ZPH_QOS
+IFDEF: USE_QOS_TOS
DEFAULT: none
LOC: Ip::Qos::TheConfig
DOC_START
Allows you to select a TOS/DSCP value to mark outgoing
- connections with, based on where the reply was sourced.
+ connections with, based on where the reply was sourced. For
+ platforms using netfilter, allows you to set a netfilter mark
+ value instead of, or in addition to, a TOS value.
TOS values really only have local significance - so you should
know what you're specifying. For more information, see RFC2474,
RFC2475, and RFC3260.
The TOS/DSCP byte must be exactly that - octet value 0x00-0xFF.
- Note that in practice often only values up to 0x3F are usable
- as the two highest bits have been redefined for use by ECN
- (RFC3168).
-
- This setting is configured by setting the source TOS values:
+ Note that in practice often only values up to 0x3F are usable as
+ the two highest bits have been redefined for use by ECN (RFC3168).
+
+ Mark values can be any unsigned integer value (normally up to 0xFFFFFFFF)
+
+ This setting is configured by setting the following values:
+
+ tos|mark Whether to set TOS or netfilter mark values
local-hit=0xFF Value to mark local cache hits.
@@ -1551,23 +1611,31 @@
parent-hit=0xFF Value to mark hits from parent peers.
-
- NOTE: 'miss' preserve feature is only possible on Linux at this time.
-
- For the following to work correctly, you will need to patch your
- linux kernel with the TOS preserving ZPH patch.
- The kernel patch can be downloaded from http://zph.bratcheda.org
+ The TOS varient of the following features are only possible on Linux
+ and require your kernel to be patched with the TOS preserving ZPH
+ patch, available from http://zph.bratcheda.org
+ No patch is needed to preserve the netfilter mark, which will work
+ with all varients of netfilter.
disable-preserve-miss
- By default, the existing TOS value of the response coming
- from the remote server will be retained and masked with
- miss-mark. This option disables that feature.
+ This option disables the preservation of the TOS or netfilter
+ mark. By default, the existing TOS or netfilter mark value of
+ the response coming from the remote server will be retained
+ and masked with miss-mark.
+ NOTE: in the case of a netfilter mark, the mark must be set on
+ the connection (using the CONNMARK target) not on the packet
+ (MARK target).
miss-mask=0xFF
- Allows you to mask certain bits in the TOS received from the
- remote server, before copying the value to the TOS sent
- towards clients.
- Default: 0xFF (TOS from server is not changed).
+ Allows you to mask certain bits in the TOS or mark value
+ received from the remote server, before copying the value to
+ the TOS sent towards clients.
+ Default for tos: 0xFF (TOS from server is not changed).
+ Default for mark: 0xFFFFFFFF (mark from server is not changed).
+
+ All of these features require the --enable-zph-qos compilation flag.
+ Netfilter marking also requires the libnetfilter_conntrack libraries
+ (--with-netfilter-conntrack) and libcap 2.09+ (--with-libcap)
DOC_END
=== modified file 'src/client_side_reply.cc'
--- src/client_side_reply.cc 2010-08-24 04:07:00 +0000
+++ src/client_side_reply.cc 2010-08-27 19:49:48 +0000
@@ -55,7 +55,7 @@
#include "forward.h"
#include "HttpReply.h"
#include "HttpRequest.h"
-#include "ip/QosConfig.h"
+#include "ip/Qos.h"
#include "MemObject.h"
#include "SquidTime.h"
#include "StoreClient.h"
@@ -1682,12 +1682,15 @@
/* guarantee nothing has been sent yet! */
assert(http->out.size == 0);
assert(http->out.offset == 0);
-#if USE_ZPH_QOS
- if (Ip::Qos::TheConfig.tos_local_hit) {
- debugs(33, 2, "ZPH Local hit, TOS=" << Ip::Qos::TheConfig.tos_local_hit);
- comm_set_tos(http->getConn()->fd, Ip::Qos::TheConfig.tos_local_hit);
- }
-#endif /* USE_ZPH_QOS */
+
+ if (Ip::Qos::TheConfig.isTosActive()) {
+ Ip::Qos::doTosLocalHit(http->getConn()->fd);
+ }
+
+ if (Ip::Qos::TheConfig.isMarkActive()) {
+ Ip::Qos::doNfmarkLocalHit(http->getConn()->fd);
+ }
+
localTempBuffer.offset = reqofs;
localTempBuffer.length = getNextNode()->readBuffer.length;
localTempBuffer.data = getNextNode()->readBuffer.data;
@@ -1967,23 +1970,15 @@
body_buf = buf;
}
-#if USE_ZPH_QOS
if (reqofs==0 && !logTypeIsATcpHit(http->logType)) {
assert(fd >= 0); // the beginning of this method implies fd may be -1
- int tos = 0;
- if (Ip::Qos::TheConfig.tos_sibling_hit && http->request->hier.code==SIBLING_HIT ) {
- tos = Ip::Qos::TheConfig.tos_sibling_hit;
- debugs(33, 2, "ZPH: Sibling Peer hit with hier.code=" << http->request->hier.code << ", TOS=" << tos);
- } else if (Ip::Qos::TheConfig.tos_parent_hit && http->request->hier.code==PARENT_HIT) {
- tos = Ip::Qos::TheConfig.tos_parent_hit;
- debugs(33, 2, "ZPH: Parent Peer hit with hier.code=" << http->request->hier.code << ", TOS=" << tos);
- } else if (Ip::Qos::TheConfig.preserve_miss_tos && Ip::Qos::TheConfig.preserve_miss_tos_mask) {
- tos = fd_table[fd].upstreamTOS & Ip::Qos::TheConfig.preserve_miss_tos_mask;
- debugs(33, 2, "ZPH: Preserving TOS on miss, TOS="<request->hier);
+ }
+ if (Ip::Qos::TheConfig.isMarkActive()) {
+ Ip::Qos::doNfmarkLocalMiss(fd, &http->request->hier);
+ }
}
-#endif
/* We've got the final data to start pushing... */
flags.storelogiccomplete = 1;
=== modified file 'src/client_side_request.cc'
--- src/client_side_request.cc 2010-08-24 04:07:00 +0000
+++ src/client_side_request.cc 2010-09-05 16:24:16 +0000
@@ -63,6 +63,7 @@
#include "fde.h"
#include "HttpReply.h"
#include "HttpRequest.h"
+#include "ip/Qos.h"
#include "MemObject.h"
#include "ProtoPort.h"
#include "Store.h"
@@ -1265,7 +1266,8 @@
* the callout. This is strictly for convenience.
*/
-extern int aclMapTOS (acl_tos * head, ACLChecklist * ch);
+extern unsigned char aclMapTOS (acl_tos * head, ACLChecklist * ch);
+extern uint32_t aclMapNfmark (acl_nfmark * head, ACLChecklist * ch);
void
ClientHttpRequest::doCallouts()
@@ -1328,15 +1330,27 @@
}
}
- if (!calloutContext->clientside_tos_done) {
- calloutContext->clientside_tos_done = true;
+ if (!calloutContext->clientsideTosDone) {
+ calloutContext->clientsideTosDone = true;
if (getConn() != NULL) {
ACLFilledChecklist ch(NULL, request, NULL);
ch.src_addr = request->client_addr;
ch.my_addr = request->my_addr;
- int tos = aclMapTOS(Config.accessList.clientside_tos, &ch);
+ unsigned char tos = aclMapTOS(Config.accessList.clientsideTos, &ch);
if (tos)
- comm_set_tos(getConn()->fd, tos);
+ Ip::Qos::setSockTos(getConn()->fd, tos);
+ }
+ }
+
+ if (!calloutContext->clientsideNfmarkDone) {
+ calloutContext->clientsideNfmarkDone = true;
+ if (getConn() != NULL) {
+ ACLFilledChecklist ch(NULL, request, NULL);
+ ch.src_addr = request->client_addr;
+ ch.my_addr = request->my_addr;
+ uint32_t mark = aclMapNfmark(Config.accessList.clientsideNfmark, &ch);
+ if (mark)
+ Ip::Qos::setSockNfmark(getConn()->fd, mark);
}
}
=== modified file 'src/comm.cc'
--- src/comm.cc 2010-08-17 16:24:18 +0000
+++ src/comm.cc 2010-09-05 13:30:40 +0000
@@ -51,6 +51,7 @@
#include "icmp/net_db.h"
#include "ip/Address.h"
#include "ip/Intercept.h"
+#include "ip/Qos.h"
#include "ip/tools.h"
#if defined(_SQUID_CYGWIN_)
@@ -72,7 +73,7 @@
static void commStopHalfClosedMonitor(int fd);
static IOCB commHalfClosedReader;
-static void comm_init_opened(int new_socket, Ip::Address &addr, unsigned char TOS, const char *note, struct addrinfo *AI);
+static void comm_init_opened(int new_socket, Ip::Address &addr, unsigned char tos, uint32_t nfmark, const char *note, struct addrinfo *AI);
static int comm_apply_flags(int new_socket, Ip::Address &addr, int flags, struct addrinfo *AI);
@@ -593,7 +594,7 @@
int flags,
const char *note)
{
- return comm_openex(sock_type, proto, addr, flags, 0, note);
+ return comm_openex(sock_type, proto, addr, flags, 0, 0, note);
}
int
@@ -609,7 +610,7 @@
flags |= COMM_DOBIND;
/* attempt native enabled port. */
- sock = comm_openex(sock_type, proto, addr, flags, 0, note);
+ sock = comm_openex(sock_type, proto, addr, flags, 0, 0, note);
return sock;
}
@@ -620,20 +621,6 @@
return anErrno == ENFILE || anErrno == EMFILE;
}
-int
-comm_set_tos(int fd, int tos)
-{
-#ifdef IP_TOS
- int x = setsockopt(fd, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof(int));
- if (x < 0)
- debugs(50, 1, "comm_set_tos: setsockopt(IP_TOS) on FD " << fd << ": " << xstrerror());
- return x;
-#else
- debugs(50, 0, "WARNING: setsockopt(IP_TOS) not supported on this platform");
- return -1;
-#endif
-}
-
void
comm_set_v6only(int fd, int tos)
{
@@ -674,11 +661,11 @@
int proto,
Ip::Address &addr,
int flags,
- unsigned char TOS,
+ unsigned char tos,
+ uint32_t nfmark,
const char *note)
{
int new_socket;
- int tos = 0;
struct addrinfo *AI = NULL;
PROF_start(comm_open);
@@ -729,9 +716,12 @@
debugs(50, 3, "comm_openex: Opened socket FD " << new_socket << " : family=" << AI->ai_family << ", type=" << AI->ai_socktype << ", protocol=" << AI->ai_protocol );
/* set TOS if needed */
- if (TOS && comm_set_tos(new_socket, TOS) ) {
- tos = TOS;
- }
+ if (tos)
+ Ip::Qos::setSockTos(new_socket, tos);
+
+ /* set netfilter mark if needed */
+ if (nfmark)
+ Ip::Qos::setSockNfmark(new_socket, nfmark);
if ( Ip::EnableIpv6&IPV6_SPECIAL_SPLITSTACK && addr.IsIPv6() )
comm_set_v6only(new_socket, 1);
@@ -741,7 +731,7 @@
if ( Ip::EnableIpv6&IPV6_SPECIAL_V4MAPPING && addr.IsIPv6() )
comm_set_v6only(new_socket, 0);
- comm_init_opened(new_socket, addr, TOS, note, AI);
+ comm_init_opened(new_socket, addr, tos, nfmark, note, AI);
new_socket = comm_apply_flags(new_socket, addr, flags, AI);
addr.FreeAddrInfo(AI);
@@ -755,7 +745,8 @@
void
comm_init_opened(int new_socket,
Ip::Address &addr,
- unsigned char TOS,
+ unsigned char tos,
+ uint32_t nfmark,
const char *note,
struct addrinfo *AI)
{
@@ -778,7 +769,9 @@
F->local_addr = addr;
- F->tos = TOS;
+ F->outgoingTOS = tos;
+
+ F->outgoingNfmark = nfmark;
F->sock_family = AI->ai_family;
}
@@ -857,7 +850,7 @@
assert(fd >= 0);
assert(AI);
- comm_init_opened(fd, addr, 0, note, AI);
+ comm_init_opened(fd, addr, 0, 0, note, AI);
if (!(flags & COMM_NOCLOEXEC))
fd_table[fd].flags.close_on_exec = 1;
@@ -1088,8 +1081,11 @@
}
F->local_addr.FreeAddrInfo(AI);
- if (F->tos)
- comm_set_tos(fd, F->tos);
+ if (F->outgoingTOS)
+ Ip::Qos::setSockTos(fd, F->outgoingTOS);
+
+ if (F->outgoingNfmark)
+ Ip::Qos::setSockNfmark(fd, F->outgoingNfmark);
if ( Ip::EnableIpv6&IPV6_SPECIAL_SPLITSTACK && F->local_addr.IsIPv6() )
comm_set_v6only(fd, 1);
=== modified file 'src/comm.h'
--- src/comm.h 2010-07-06 23:09:44 +0000
+++ src/comm.h 2010-09-02 09:46:35 +0000
@@ -74,9 +74,8 @@
*/
SQUIDCEXTERN int comm_open_listener(int sock_type, int proto, Ip::Address &addr, int flags, const char *note);
-SQUIDCEXTERN int comm_openex(int, int, Ip::Address &, int, unsigned char TOS, const char *);
+SQUIDCEXTERN int comm_openex(int, int, Ip::Address &, int, unsigned char TOS, uint32_t nfmark, const char *);
SQUIDCEXTERN u_short comm_local_port(int fd);
-SQUIDCEXTERN int comm_set_tos(int fd, int tos);
SQUIDCEXTERN void commSetSelect(int, unsigned int, PF *, void *, time_t);
SQUIDCEXTERN void commResetSelect(int);
=== modified file 'src/fde.cc'
--- src/fde.cc 2009-06-01 02:52:28 +0000
+++ src/fde.cc 2010-09-05 15:53:03 +0000
@@ -126,3 +126,39 @@
pconn.uses++;
pconn.pool = pool;
}
+
+bool
+isAclNfmarkActive()
+{
+ acl_nfmark ** nfmarkAcls [] = { &Config.accessList.outgoingNfmark,
+ &Config.accessList.clientsideNfmark };
+
+ for (int i=0; i<2; i++) {
+ while (*nfmarkAcls[i]) {
+ acl_nfmark *l = *nfmarkAcls[i];
+ if (l->nfmark > 0)
+ return true;
+ *nfmarkAcls[i] = l->next;
+ }
+ }
+
+ return false;
+}
+
+bool
+isAclTosActive()
+{
+ acl_tos ** tosAcls [] = { &Config.accessList.outgoingTos,
+ &Config.accessList.clientsideTos };
+
+ for (int i=0; i<2; i++) {
+ while (*tosAcls[i]) {
+ acl_tos *l = *tosAcls[i];
+ if (l->tos > 0)
+ return true;
+ *tosAcls[i] = l->next;
+ }
+ }
+
+ return false;
+}
=== modified file 'src/fde.h'
--- src/fde.h 2010-07-06 23:09:44 +0000
+++ src/fde.h 2010-09-05 15:54:02 +0000
@@ -35,6 +35,20 @@
class PconnPool;
+/**
+* Iterates through any outgoing_nfmark or clientside_nfmark configuration parameters
+* to find out if any Netfilter marking is required.
+* This function is used on initialisation to define capabilities required (Netfilter
+* marking requires CAP_NET_ADMIN.
+*/
+bool isAclNfmarkActive();
+
+/**
+* Iterates through any outgoing_tos or clientside_tos configuration parameters
+* to find out if packets should be marked with TOS flags.
+*/
+bool isAclTosActive();
+
class fde
{
@@ -56,7 +70,10 @@
u_short remote_port;
Ip::Address local_addr;
- unsigned char tos;
+ unsigned char outgoingTOS; /**< The TOS value for packets going towards the server.
+ See also upstreamTOS. */
+ uint32_t outgoingNfmark; /**< The netfilter mark for packets going towards the server.
+ See also upstreamNfmark. */
int sock_family;
char ipaddr[MAX_IPSTRLEN]; /* dotted decimal address of peer */
char desc[FD_DESC_SZ];
@@ -109,9 +126,16 @@
long handle;
} win32;
#endif
-#if USE_ZPH_QOS
- unsigned char upstreamTOS; /* see FwdState::dispatch() */
-#endif
+ unsigned char upstreamTOS; /**< Stores the TOS flags of the packets from the remote server.
+ See FwdState::dispatch(). Note that this differs to
+ outgoingTos in that this is the value we *receive* from the,
+ connection, whereas outgoingTos is the value to set on packets
+ *leaving* Squid. */
+ unsigned int upstreamNfmark; /**< Stores the Netfilter mark value of the connection from the remote
+ server. See FwdState::dispatch(). Note that this differs to
+ outgoingNfmark in that this is the value we *receive* from the,
+ connection, whereas outgoingNfmark is the value to set on packets
+ *leaving* Squid. */
private:
/** Clear the fde class back to NULL equivalent. */
=== modified file 'src/forward.cc'
--- src/forward.cc 2010-08-10 10:33:04 +0000
+++ src/forward.cc 2010-09-05 17:05:37 +0000
@@ -42,6 +42,7 @@
#include "hier_code.h"
#include "HttpReply.h"
#include "HttpRequest.h"
+#include "ip/Qos.h"
#include "MemObject.h"
#include "pconn.h"
#include "SquidTime.h"
@@ -778,7 +779,6 @@
int ftimeout = Config.Timeout.forward - (squid_curtime - start_t);
Ip::Address outgoing;
- unsigned short tos;
Ip::Address client_addr;
assert(fs);
assert(server_fd == -1);
@@ -890,9 +890,16 @@
outgoing.SetIPv4();
}
- tos = getOutgoingTOS(request);
+ unsigned char tos = getOutgoingTOS(request);
- debugs(17, 3, "fwdConnectStart: got outgoing addr " << outgoing << ", tos " << tos);
+#if defined(SO_MARK)
+ uint32_t mark = getOutgoingNfmark(request);
+ debugs(17, 3, "fwdConnectStart: got outgoing addr " << outgoing << ", tos " << int(tos)
+ << ", netfilter mark " << mark);
+#else
+ uint32_t mark = 0;
+ debugs(17, 3, "fwdConnectStart: got outgoing addr " << outgoing << ", tos " << int(tos));
+#endif
int commFlags = COMM_NONBLOCKING;
if (request->flags.spoof_client_ip) {
@@ -901,7 +908,7 @@
// else no tproxy today ...
}
- fd = comm_openex(SOCK_STREAM, IPPROTO_TCP, outgoing, commFlags, tos, url);
+ fd = comm_openex(SOCK_STREAM, IPPROTO_TCP, outgoing, commFlags, tos, mark, url);
debugs(17, 3, "fwdConnectStart: got TCP FD " << fd);
@@ -990,44 +997,41 @@
netdbPingSite(request->GetHost());
-#if USE_ZPH_QOS && defined(_SQUID_LINUX_)
- /* Bug 2537: This part of ZPH only applies to patched Linux kernels. */
+ /* Update server side TOS and Netfilter mark if using persistent connections.
+ Persistent connections were previously incompatible with outgoing_tos. */
+ if (Config.onoff.server_pconns) {
+ if (isAclTosActive()) {
+ unsigned char tos = getOutgoingTOS(request);
+ Ip::Qos::setSockTos(server_fd, tos);
+ }
+#ifdef SO_MARK
+ if (isAclNfmarkActive()) {
+ uint32_t mark = getOutgoingNfmark(request);
+ Ip::Qos::setSockNfmark(server_fd, mark);
+ }
+#endif
+ }
- /* Retrieves remote server TOS value, and stores it as part of the
+ /* Retrieves remote server TOS or MARK value, and stores it as part of the
* original client request FD object. It is later used to forward
- * remote server's TOS in the response to the client in case of a MISS.
+ * remote server's TOS/MARK in the response to the client in case of a MISS.
*/
- fde * clientFde = &fd_table[client_fd];
- if (clientFde) {
- int tos = 1;
- int tos_len = sizeof(tos);
- clientFde->upstreamTOS = 0;
- if (setsockopt(server_fd,SOL_IP,IP_RECVTOS,&tos,tos_len)==0) {
- unsigned char buf[512];
- int len = 512;
- if (getsockopt(server_fd,SOL_IP,IP_PKTOPTIONS,buf,(socklen_t*)&len) == 0) {
- /* Parse the PKTOPTIONS structure to locate the TOS data message
- * prepared in the kernel by the ZPH incoming TCP TOS preserving
- * patch.
- */
- unsigned char * pbuf = buf;
- while (pbuf-buf < len) {
- struct cmsghdr *o = (struct cmsghdr*)pbuf;
- if (o->cmsg_len<=0)
- break;
+ if (Ip::Qos::TheConfig.isMarkActive()) {
+ fde * clientFde = &fd_table[client_fd];
+ fde * servFde = &fd_table[server_fd];
+ if (clientFde && servFde) {
+ /* Get the netfilter mark for the connection */
+ Ip::Qos::getUpstreamNfMark(server_fd, servFde, clientFde);
+ }
+ }
- if (o->cmsg_level == SOL_IP && o->cmsg_type == IP_TOS) {
- int *tmp = (int*)CMSG_DATA(o);
- clientFde->upstreamTOS = (unsigned char)*tmp;
- break;
- }
- pbuf += CMSG_LEN(o->cmsg_len);
- }
- } else {
- debugs(33, 1, "ZPH: error in getsockopt(IP_PKTOPTIONS) on FD "<next) {
+ if (!l->aclList || ch->matchAclListFast(l->aclList))
+ return l->nfmark;
+ }
+
+ return 0;
+}
+
Ip::Address
getOutgoingAddr(HttpRequest * request, struct peer *dst_peer)
{
@@ -1396,7 +1415,7 @@
return aclMapAddr(Config.accessList.outgoing_address, &ch);
}
-unsigned long
+unsigned char
getOutgoingTOS(HttpRequest * request)
{
ACLFilledChecklist ch(NULL, request, NULL);
@@ -1406,7 +1425,20 @@
ch.my_addr = request->my_addr;
}
- return aclMapTOS(Config.accessList.outgoing_tos, &ch);
+ return aclMapTOS(Config.accessList.outgoingTos, &ch);
+}
+
+uint32_t
+getOutgoingNfmark(HttpRequest * request)
+{
+ ACLFilledChecklist ch(NULL, request, NULL);
+
+ if (request) {
+ ch.src_addr = request->client_addr;
+ ch.my_addr = request->my_addr;
+ }
+
+ return aclMapNfmark(Config.accessList.outgoingNfmark, &ch);
}
=== modified file 'src/ip/Makefile.am'
--- src/ip/Makefile.am 2010-07-25 08:10:12 +0000
+++ src/ip/Makefile.am 2010-08-13 10:55:48 +0000
@@ -9,8 +9,9 @@
Address.cc \
Intercept.h \
Intercept.cc \
- QosConfig.h \
- QosConfig.cc \
+ Qos.h \
+ Qos.cc \
+ Qos.cci \
tools.cc \
tools.h
=== renamed file 'src/ip/QosConfig.cc' => 'src/ip/Qos.cc'
--- src/ip/QosConfig.cc 2010-04-17 02:29:04 +0000
+++ src/ip/Qos.cc 2010-09-04 15:14:43 +0000
@@ -1,79 +1,354 @@
+#include
#include "squid.h"
-
-#if USE_ZPH_QOS
-
-#include "QosConfig.h"
-
-Ip::Qos::QosConfig Ip::Qos::TheConfig;
-
-Ip::Qos::QosConfig::QosConfig() :
- tos_local_hit(0),
- tos_sibling_hit(0),
- tos_parent_hit(0),
- preserve_miss_tos(1),
- preserve_miss_tos_mask(255)
-{
- ;
-}
-
-void
-Ip::Qos::QosConfig::parseConfigLine()
-{
- // %i honors 0 and 0x prefixes, which are important for things like umask
+#include "fde.h"
+#include "Qos.h"
+#include "HierarchyLogEntry.h"
+#include "ip/tools.h"
+#include "Parsing.h"
+
+/* Qos namespace */
+
+void
+Ip::Qos::getUpstreamTOS(const int server_fd, fde *clientFde)
+{
+#if USE_QOS_TOS
+ unsigned char tos = 1;
+ int tos_len = sizeof(tos);
+ clientFde->upstreamTOS = 0;
+ if (setsockopt(server_fd,SOL_IP,IP_RECVTOS,&tos,tos_len)==0) {
+ unsigned char buf[512];
+ int len = 512;
+ if (getsockopt(server_fd,SOL_IP,IP_PKTOPTIONS,buf,(socklen_t*)&len) == 0) {
+ /* Parse the PKTOPTIONS structure to locate the TOS data message
+ * prepared in the kernel by the ZPH incoming TCP TOS preserving
+ * patch.
+ */
+ unsigned char * pbuf = buf;
+ while (pbuf-buf < len) {
+ struct cmsghdr *o = (struct cmsghdr*)pbuf;
+ if (o->cmsg_len<=0)
+ break;
+
+ if (o->cmsg_level == SOL_IP && o->cmsg_type == IP_TOS) {
+ int *tmp = (int*)CMSG_DATA(o);
+ clientFde->upstreamTOS = (unsigned char)*tmp;
+ break;
+ }
+ pbuf += CMSG_LEN(o->cmsg_len);
+ }
+ } else {
+ debugs(33, 1, "QOS: error in getsockopt(IP_PKTOPTIONS) on FD " << server_fd << " " << xstrerror());
+ }
+ } else {
+ debugs(33, 1, "QOS: error in setsockopt(IP_RECVTOS) on FD " << server_fd << " " << xstrerror());
+ }
+#endif
+}
+
+void Ip::Qos::getUpstreamNfMark(const int server_fd, const fde *servFde, const fde *clientFde)
+{
+#if USE_QOS_NFMARK
+ /* Allocate a new conntrack */
+ if (struct nf_conntrack *ct = nfct_new()) {
+
+ /* Prepare data needed to find the connection in the conntrack table.
+ * We need the local and remote IP address, and the local and remote
+ * port numbers.
+ */
+
+ Ip::Address serv_fde_local_conn;
+ struct addrinfo *addr = NULL;
+ serv_fde_local_conn.InitAddrInfo(addr);
+ getsockname(server_fd, addr->ai_addr, &(addr->ai_addrlen));
+ serv_fde_local_conn = *addr;
+ serv_fde_local_conn.GetAddrInfo(addr);
+
+ unsigned short serv_fde_local_port = ((struct sockaddr_in*)addr->ai_addr)->sin_port;
+ struct in6_addr serv_fde_local_ip6;
+ struct in_addr serv_fde_local_ip;
+
+ if (Ip::EnableIpv6 && serv_fde_local_conn.IsIPv6()) {
+ serv_fde_local_ip6 = ((struct sockaddr_in6*)addr->ai_addr)->sin6_addr;
+ nfct_set_attr_u8(ct, ATTR_L3PROTO, AF_INET6);
+ struct in6_addr serv_fde_remote_ip6;
+ inet_pton(AF_INET6,servFde->ipaddr,(struct in6_addr*)&serv_fde_remote_ip6);
+ nfct_set_attr(ct, ATTR_IPV6_DST, serv_fde_remote_ip6.s6_addr);
+ nfct_set_attr(ct, ATTR_IPV6_SRC, serv_fde_local_ip6.s6_addr);
+ } else {
+ serv_fde_local_ip = ((struct sockaddr_in*)addr->ai_addr)->sin_addr;
+ nfct_set_attr_u8(ct, ATTR_L3PROTO, AF_INET);
+ nfct_set_attr_u32(ct, ATTR_IPV4_DST, inet_addr(servFde->ipaddr));
+ nfct_set_attr_u32(ct, ATTR_IPV4_SRC, serv_fde_local_ip.s_addr);
+ }
+
+ nfct_set_attr_u8(ct, ATTR_L4PROTO, IPPROTO_TCP);
+ nfct_set_attr_u16(ct, ATTR_PORT_DST, htons(servFde->remote_port));
+ nfct_set_attr_u16(ct, ATTR_PORT_SRC, serv_fde_local_port);
+
+ /* Open a handle to the conntrack */
+ if (struct nfct_handle *h = nfct_open(CONNTRACK, 0)) {
+ /* Register the callback. The callback function will record the mark value. */
+ nfct_callback_register(h, NFCT_T_ALL, getNfMarkCallback, (void *)clientFde);
+ /* Query the conntrack table using the data previously set */
+ int x = nfct_query(h, NFCT_Q_GET, ct);
+ if (x == -1) {
+ debugs(17, 2, "QOS: Failed to retrieve connection mark: (" << x << ") " << strerror(errno)
+ << " (Destination " << servFde->ipaddr << ":" << servFde->remote_port
+ << ", source " << serv_fde_local_conn << ")" );
+ }
+
+ nfct_close(h);
+ } else {
+ debugs(17, 2, "QOS: Failed to open conntrack handle for upstream netfilter mark retrieval.");
+ }
+ serv_fde_local_conn.FreeAddrInfo(addr);
+ nfct_destroy(ct);
+
+ } else {
+ debugs(17, 2, "QOS: Failed to allocate new conntrack for upstream netfilter mark retrieval.");
+ }
+#endif
+}
+
+#if USE_QOS_NFMARK
+int
+Ip::Qos::getNfMarkCallback(enum nf_conntrack_msg_type type,
+ struct nf_conntrack *ct,
+ void *data)
+{
+ fde *clientFde = (fde *)data;
+ clientFde->upstreamNfmark = nfct_get_attr_u32(ct, ATTR_MARK);
+ debugs(17, 3, "QOS: Retrieved connection mark value: " << clientFde->upstreamNfmark);
+
+ return NFCT_CB_CONTINUE;
+}
+#endif
+
+int
+Ip::Qos::doTosLocalMiss(const int fd, const HierarchyLogEntry *hier)
+{
+ unsigned char tos = 0;
+ if (Ip::Qos::TheConfig.tosSiblingHit && hier->code==SIBLING_HIT ) {
+ tos = Ip::Qos::TheConfig.tosSiblingHit;
+ debugs(33, 2, "QOS: Sibling Peer hit with hier code=" << hier->code << ", TOS=" << int(tos));
+ } else if (Ip::Qos::TheConfig.tosParentHit && hier->code==PARENT_HIT) {
+ tos = Ip::Qos::TheConfig.tosParentHit;
+ debugs(33, 2, "QOS: Parent Peer hit with hier code=" << hier->code << ", TOS=" << int(tos));
+ } else if (Ip::Qos::TheConfig.preserveMissTos && Ip::Qos::TheConfig.preserveMissTosMask) {
+ tos = fd_table[fd].upstreamTOS & Ip::Qos::TheConfig.preserveMissTosMask;
+ debugs(33, 2, "QOS: Preserving TOS on miss, TOS=" << int(tos));
+ }
+ return setSockTos(fd, tos);
+}
+
+int
+Ip::Qos::doNfmarkLocalMiss(const int fd, const HierarchyLogEntry *hier)
+{
+ uint32_t mark = 0;
+ if (Ip::Qos::TheConfig.markSiblingHit && hier->code==SIBLING_HIT ) {
+ mark = Ip::Qos::TheConfig.markSiblingHit;
+ debugs(33, 2, "QOS: Sibling Peer hit with hier code=" << hier->code << ", Mark=" << mark);
+ } else if (Ip::Qos::TheConfig.markParentHit && hier->code==PARENT_HIT) {
+ mark = Ip::Qos::TheConfig.markParentHit;
+ debugs(33, 2, "QOS: Parent Peer hit with hier code=" << hier->code << ", Mark=" << mark);
+ } else if (Ip::Qos::TheConfig.preserveMissMark) {
+ mark = fd_table[fd].upstreamNfmark & Ip::Qos::TheConfig.preserveMissMarkMask;
+ debugs(33, 2, "QOS: Preserving mark on miss, Mark=" << mark);
+ }
+ return setSockNfmark(fd, mark);
+}
+
+int
+Ip::Qos::doTosLocalHit(const int fd)
+{
+ debugs(33, 2, "QOS: Setting TOS for local hit, TOS=" << int(Ip::Qos::TheConfig.tosLocalHit));
+ return setSockTos(fd, Ip::Qos::TheConfig.tosLocalHit);
+}
+
+int
+Ip::Qos::doNfmarkLocalHit(const int fd)
+{
+ debugs(33, 2, "QOS: Setting netfilter mark for local hit, mark=" << Ip::Qos::TheConfig.markLocalHit);
+ return setSockNfmark(fd, Ip::Qos::TheConfig.markLocalHit);
+}
+
+/* Qos::Config class */
+
+Ip::Qos::Config Ip::Qos::TheConfig;
+
+Ip::Qos::Config::Config()
+{
+ tosLocalHit = 0;
+ tosSiblingHit = 0;
+ tosParentHit = 0;
+ preserveMissTos = false;
+ preserveMissTosMask = 0xFF;
+ markLocalHit = 0;
+ markSiblingHit = 0;
+ markParentHit = 0;
+ preserveMissMark = false;
+ preserveMissMarkMask = 0xFFFFFFFF;
+}
+
+void
+Ip::Qos::Config::parseConfigLine()
+{
/* parse options ... */
char *token;
+ /* These are set as appropriate and then used to check whether the initial loop has been done */
+ bool mark = false;
+ bool tos = false;
+ /* Assume preserve is true. We don't set at initialisation as this affects isTosActive().
+ We have to do this now, as we may never match the 'tos' parameter below */
+#if !USE_QOS_TOS
+ debugs(3, DBG_CRITICAL, "ERROR: Invalid option 'qos_flows'. QOS features not enabled in this build");
+ self_destruct();
+#endif
+
while ( (token = strtok(NULL, w_space)) ) {
+ // Work out TOS or mark. Default to TOS for backwards compatibility
+ if (!(mark || tos)) {
+ if (strncmp(token, "mark",4) == 0) {
+#if USE_QOS_NFMARK && defined(_SQUID_LINUX_)
+ mark = true;
+ // Assume preserve is true. We don't set at initialisation as this affects isMarkActive()
+ preserveMissMark = 1;
+#elif defined(_SQUID_LINUX_)
+ debugs(3, DBG_CRITICAL, "ERROR: Invalid parameter 'mark' in qos_flows option. "
+ << "Netfilter marking only available for Linux environment");
+ self_destruct();
+#else
+ debugs(3, DBG_CRITICAL, "ERROR: Invalid parameter 'mark' in qos_flows option. "
+ << "Netfilter marking not enabled in this build");
+ self_destruct();
+#endif
+ } else if (strncmp(token, "tos",3) == 0) {
+ preserveMissTos = true;
+ tos = true;
+ } else {
+ preserveMissTos = true;
+ tos = true;
+ }
+ }
+
if (strncmp(token, "local-hit=",10) == 0) {
- sscanf(&token[10], "%i", &tos_local_hit);
+
+ if (mark) {
+ if (!xstrtoui(&token[10], NULL, &markLocalHit, 0, std::numeric_limits::max()))
+ debugs(3, DBG_CRITICAL, "ERROR: Bad mark local-hit value " << &token[10]);
+ } else {
+ unsigned int v = 0;
+ if (!xstrtoui(&token[10], NULL, &v, 0, std::numeric_limits::max()))
+ debugs(3, DBG_CRITICAL, "ERROR: Bad TOS local-hit value " << &token[10]);
+ tosLocalHit = (unsigned char)v;
+ }
+
} else if (strncmp(token, "sibling-hit=",12) == 0) {
- sscanf(&token[12], "%i", &tos_sibling_hit);
+
+ if (mark) {
+ if (!xstrtoui(&token[12], NULL, &markSiblingHit, 0, std::numeric_limits::max()))
+ debugs(3, DBG_CRITICAL, "ERROR: Bad mark sibling-hit value " << &token[12]);
+ } else {
+ unsigned int v = 0;
+ if (!xstrtoui(&token[12], NULL, &v, 0, std::numeric_limits::max()))
+ debugs(3, DBG_CRITICAL, "ERROR: Bad TOS sibling-hit value " << &token[12]);
+ tosSiblingHit = (unsigned char)v;
+ }
+
} else if (strncmp(token, "parent-hit=",11) == 0) {
- sscanf(&token[11], "%i", &tos_parent_hit);
+
+ if (mark) {
+ if (!xstrtoui(&token[11], NULL, &markParentHit, 0, std::numeric_limits::max()))
+ debugs(3, DBG_CRITICAL, "ERROR: Bad mark parent-hit value " << &token[11]);
+ } else {
+ unsigned int v = 0;
+ if (!xstrtoui(&token[11], NULL, &v, 0, std::numeric_limits::max()))
+ debugs(3, DBG_CRITICAL, "ERROR: Bad TOS parent-hit value " << &token[11]);
+ tosParentHit = (unsigned char)v;
+ }
+
} else if (strcmp(token, "disable-preserve-miss") == 0) {
- preserve_miss_tos = 0;
- preserve_miss_tos_mask = 0;
- } else if (preserve_miss_tos && strncmp(token, "miss-mask=",10) == 0) {
- sscanf(&token[10], "%i", &preserve_miss_tos_mask);
+
+ if (preserveMissTosMask!=0xFFU || preserveMissMarkMask!=0xFFFFFFFFU) {
+ debugs(3, DBG_CRITICAL, "ERROR: miss-mask feature cannot be set with disable-preserve-miss");
+ }
+ if (mark) {
+ preserveMissMark = false;
+ preserveMissMarkMask = 0;
+ } else {
+ preserveMissTos = false;
+ preserveMissTosMask = 0;
+ }
+
+ } else if (strncmp(token, "miss-mask=",10) == 0) {
+
+ if (mark && preserveMissMark) {
+ if (!xstrtoui(&token[10], NULL, &preserveMissMarkMask, 0, std::numeric_limits::max()))
+ debugs(3, DBG_CRITICAL, "ERROR: Bad mark miss-mark value " << &token[10]);
+ } else if (preserveMissTos) {
+ unsigned int v = 0;
+ if (!xstrtoui(&token[10], NULL, &v, 0, std::numeric_limits::max()))
+ debugs(3, DBG_CRITICAL, "ERROR: Bad TOS miss-mark value " << &token[10]);
+ preserveMissTosMask = (unsigned char)v;
+ } else {
+ debugs(3, DBG_CRITICAL, "ERROR: miss-mask feature cannot be set with disable-preserve-miss");
+ }
+
}
}
}
-/**
- * NOTE: Due to the low-level nature of the library these
- * objects are part of the dump function must be self-contained.
- * which means no StoreEntry refrences. Just a basic char* buffer.
- */
void
-Ip::Qos::QosConfig::dumpConfigLine(char *entry, const char *name) const
+Ip::Qos::Config::dumpConfigLine(char *entry, const char *name) const
{
char *p = entry;
- snprintf(p, 10, "%s", name); // strlen("qos_flows ");
- p += strlen(name);
-
- if (tos_local_hit >0) {
- snprintf(p, 15, " local-hit=%2x", tos_local_hit);
- p += 15;
- }
-
- if (tos_sibling_hit >0) {
- snprintf(p, 17, " sibling-hit=%2x", tos_sibling_hit);
- p += 17;
- }
- if (tos_parent_hit >0) {
- snprintf(p, 16, " parent-hit=%2x", tos_parent_hit);
- p += 16;
- }
- if (preserve_miss_tos != 0) {
- snprintf(p, 22, " disable-preserve-miss");
- p += 22;
- }
- if (preserve_miss_tos && preserve_miss_tos_mask != 0) {
- snprintf(p, 15, " miss-mask=%2x", preserve_miss_tos_mask);
- p += 15;
- }
- snprintf(p, 1, "\n");
-// p += 1;
+ if (isTosActive()) {
+
+ p += snprintf(p, 11, "%s", name); // strlen("qos_flows ");
+ p += snprintf(p, 4, "%s", "tos");
+
+ if (tosLocalHit > 0) {
+ p += snprintf(p, 16, " local-hit=0x%02X", tosLocalHit);
+ }
+ if (tosSiblingHit > 0) {
+ p += snprintf(p, 18, " sibling-hit=0x%02X", tosSiblingHit);
+ }
+ if (tosParentHit > 0) {
+ p += snprintf(p, 17, " parent-hit=0x%02X", tosParentHit);
+ }
+ if (preserveMissTos == 0) {
+ p += snprintf(p, 23, " disable-preserve-miss");
+ }
+ if (preserveMissTos && preserveMissTosMask != 0) {
+ p += snprintf(p, 16, " miss-mask=0x%02X", preserveMissTosMask);
+ }
+ p += snprintf(p, 2, "\n");
+ }
+
+ if (isMarkActive()) {
+ p += snprintf(p, 11, "%s", name); // strlen("qos_flows ");
+ p += snprintf(p, 5, "%s", "mark");
+
+ if (markLocalHit > 0) {
+ p += snprintf(p, 22, " local-hit=0x%02X", markLocalHit);
+ }
+ if (markSiblingHit > 0) {
+ p += snprintf(p, 24, " sibling-hit=0x%02X", markSiblingHit);
+ }
+ if (markParentHit > 0) {
+ p += snprintf(p, 23, " parent-hit=0x%02X", markParentHit);
+ }
+ if (preserveMissMark == 0) {
+ p += snprintf(p, 23, " disable-preserve-miss");
+ }
+ if (preserveMissMark && preserveMissMarkMask != 0) {
+ p += snprintf(p, 22, " miss-mask=0x%02X", preserveMissMarkMask);
+ }
+ p += snprintf(p, 2, "\n");
+ }
}
-#endif /* USE_ZPH_QOS */
+#if !_USE_INLINE_
+#include "Qos.cci"
+#endif
=== added file 'src/ip/Qos.cci'
--- src/ip/Qos.cci 1970-01-01 00:00:00 +0000
+++ src/ip/Qos.cci 2010-09-04 14:47:13 +0000
@@ -0,0 +1,41 @@
+/* Inline QOS functions */
+
+int
+Ip::Qos::setSockTos(int fd, unsigned char tos)
+{
+#ifdef IP_TOS
+ int x = setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(unsigned char));
+ if (x < 0)
+ debugs(50, 2, "Ip::Qos::setSockTos: setsockopt(IP_TOS) on FD " << fd << ": " << xstrerror());
+ return x;
+#else
+ debugs(50, DBG_IMPORTANT, "WARNING: setsockopt(IP_TOS) not supported on this platform");
+ return -1;
+#endif
+}
+
+int
+Ip::Qos::setSockNfmark(int fd, uint32_t mark)
+{
+#ifdef SO_MARK
+ int x = setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(uint32_t));
+ if (x < 0)
+ debugs(50, 2, "setSockNfmark: setsockopt(SO_MARK) on FD " << fd << ": " << xstrerror());
+ return x;
+#else
+ debugs(50, DBG_IMPORTANT, "WARNING: setsockopt(SO_MARK) not supported on this platform");
+ return -1;
+#endif
+}
+
+bool
+Ip::Qos::Config::isTosActive() const
+{
+ return (tosLocalHit || tosSiblingHit || tosParentHit || preserveMissTos);
+}
+
+bool
+Ip::Qos::Config::isMarkActive() const
+{
+ return (markLocalHit || markSiblingHit || markParentHit || preserveMissMark);
+}
=== renamed file 'src/ip/QosConfig.h' => 'src/ip/Qos.h'
--- src/ip/QosConfig.h 2010-04-18 00:13:00 +0000
+++ src/ip/Qos.h 2010-09-04 15:15:00 +0000
@@ -2,33 +2,165 @@
#define SQUID_QOSCONFIG_H
#include "config.h"
+#if HAVE_LIBNETFILTER_CONNTRACK_LIBNETFILTER_CONNTRACK_H
+#include
+#include
+#endif
-#if USE_ZPH_QOS
+// Forward-declarations
+class fde;
+class HierarchyLogEntry;
namespace Ip
{
+/**
+ * QOS namespace contains all the QOS functionality: global functions within
+ * the namespace and the configuration parameters within a config class.
+ */
namespace Qos
{
-class QosConfig
+ /**
+ * Function to retrieve the TOS value of the inbound packet.
+ * Called by FwdState::dispatch if QOS options are enabled.
+ * @param server_fd Server side descriptor of connection to get TOS for
+ * @param clientFde Pointer to client side fde instance to set upstreamTOS in
+ */
+ void getUpstreamTOS(const int server_fd, fde *clientFde);
+
+ /**
+ * Function to retrieve the netfilter mark value of the connection
+ * to the upstream server. Called by FwdState::dispatch if QOS
+ * options are enabled.
+ * @param server_fd Server side descriptor of connection to get mark for
+ * @param servFde Pointer to server side fde instance to get mark for
+ * @param clientFde Pointer to client side fde instance to set upstreamNfmark in
+ */
+ void getUpstreamNfMark(const int server_fd, const fde *servFde, const fde *clientFde);
+
+#if USE_QOS_NFMARK
+ /**
+ * Callback function to mark connection once it's been found.
+ * This function is called by the libnetfilter_conntrack
+ * libraries, during nfct_query in Ip::Qos::getUpstreamNfMark.
+ * nfct_callback_register is used to register this function.
+ * @param nf_conntrack_msg_type Type of conntrack message
+ * @param nf_conntrack Pointer to the conntrack structure
+ * @param clientFde Pointer to client side fde instance to set upstreamNfmark in
+ */
+ int getNfMarkCallback(enum nf_conntrack_msg_type type, struct nf_conntrack *ct, void *clientFde);
+#endif
+
+ /**
+ * Function to work out and then apply to the socket the appropriate
+ * TOS value to set on packets when items have not been retrieved from
+ * local cache. Called by clientReplyContext::sendMoreData if QOS is
+ * enabled for TOS.
+ * @param fd Descriptor of socket to set the TOS for
+ * @param HierarchyLogEntry Pointer to hier structure of request
+ */
+ int doTosLocalMiss(const int fd, const HierarchyLogEntry *hier);
+
+ /**
+ * Function to work out and then apply to the socket the appropriate
+ * netfilter mark value to set on packets when items have not been
+ * retrieved from local cache. Called by clientReplyContext::sendMoreData
+ * if QOS is enabled for TOS.
+ * @param fd Descriptor of socket to set the mark for
+ * @param HierarchyLogEntry Pointer to hier structure of request
+ */
+ int doNfmarkLocalMiss(const int fd, const HierarchyLogEntry *hier);
+
+ /**
+ * Function to work out and then apply to the socket the appropriate
+ * TOS value to set on packets when items *have* been retrieved from
+ * local cache. Called by clientReplyContext::doGetMoreData if QOS is
+ * enabled for TOS.
+ * @param fd Descriptor of socket to set the TOS for
+ */
+ int doTosLocalHit(const int fd);
+
+ /**
+ * Function to work out and then apply to the socket the appropriate
+ * netfilter mark value to set on packets when items *have* been
+ * retrieved from local cache. Called by clientReplyContext::doGetMoreData
+ * if QOS is enabled for TOS.
+ * @param fd Descriptor of socket to set the mark for
+ */
+ int doNfmarkLocalHit(const int fd);
+
+ /**
+ * Function to set the TOS value of packets. Sets the value on the socket
+ * which then gets copied to the packets.
+ * @param fd Descriptor of socket to set the TOS for
+ */
+ _SQUID_INLINE_ int setSockTos(int fd, unsigned char tos);
+
+ /**
+ * Function to set the netfilter mark value of packets. Sets the value on the
+ * socket which then gets copied to the packets. Called from Ip::Qos::doNfmarkLocalMiss
+ * @param fd Descriptor of socket to set the mark for
+ */
+ _SQUID_INLINE_ int setSockNfmark(int fd, uint32_t mark);
+
+/**
+ * QOS configuration class. Contains all the parameters for QOS functions as well
+ * as functions to check whether either TOS or MARK QOS is enabled.
+ */
+class Config
{
public:
- int tos_local_hit;
- int tos_sibling_hit;
- int tos_parent_hit;
- int preserve_miss_tos;
- int preserve_miss_tos_mask;
-
-public:
- QosConfig();
- ~QosConfig() {};
+
+ /**
+ * Constructor
+ */
+ Config();
+
+ /**
+ * Destructor
+ */
+ ~Config() {};
void parseConfigLine();
+
+ /**
+ * Dump all the configuration values
+ *
+ * NOTE: Due to the low-level nature of the library these
+ * objects are part of the dump function must be self-contained.
+ * which means no StoreEntry references. Just a basic char* buffer.
+ */
void dumpConfigLine(char *entry, const char *name) const;
+
+ /**
+ * Returns true or false depending on whether we should carry out the
+ * QOS functions for the TOS flags
+ */
+ _SQUID_INLINE_ bool isTosActive() const;
+
+ /**
+ * Returns true or false depending on whether we should carry out the
+ * QOS functions for netfilter marks
+ */
+ _SQUID_INLINE_ bool isMarkActive() const;
+
+ unsigned char tosLocalHit; ///< TOS value to apply to local cache hits
+ unsigned char tosSiblingHit; ///< TOS value to apply to hits from siblings
+ unsigned char tosParentHit; ///< TOS value to apply to hits from parent
+ bool preserveMissTos; ///< Whether to preserve the TOS value of the inbound packet for misses
+ unsigned char preserveMissTosMask; ///< The mask to apply when preserving the TOS of misses
+ uint32_t markLocalHit; ///< Netfilter mark value to apply to local cache hits
+ uint32_t markSiblingHit; ///< Netfilter mark value to apply to hits from siblings
+ uint32_t markParentHit; ///< Netfilter mark value to apply to hits from parent
+ bool preserveMissMark; ///< Whether to preserve netfilter mark value of inbound connection
+ uint32_t preserveMissMarkMask; ///< The mask to apply when preserving the netfilter mark of misses
};
-extern QosConfig TheConfig;
+/**
+ * Globally available instance of Qos::Config
+ */
+extern Config TheConfig;
/* legacy parser access wrappers */
#define parse_QosConfig(X) (X)->parseConfigLine()
@@ -40,7 +172,11 @@
} while(0);
}; // namespace Qos
+
}; // namespace Ip
-#endif /* USE_ZPH_QOS */
+#if _USE_INLINE_
+#include "Qos.cci"
+#endif
+
#endif /* SQUID_QOSCONFIG_H */
=== renamed file 'src/ip/stubQosConfig.cc' => 'src/ip/stubQos.cc'
--- src/ip/stubQosConfig.cc 2010-04-25 07:07:14 +0000
+++ src/ip/stubQos.cc 2010-09-04 13:15:24 +0000
@@ -1,47 +1,80 @@
#include "squid.h"
-#if USE_ZPH_QOS
-
-#include "ip/QosConfig.h"
+#include "ip/Qos.h"
#include "Store.h"
-Ip::QosConfig::QosConfig() :
- tos_local_hit(0),
- tos_sibling_hit(0),
- tos_parent_hit(0),
- preserve_miss_tos(1),
- preserve_miss_tos_mask(255)
-{
- ;
-}
-
-void
-Ip::QosConfig::parseConfigLine()
-{
- // %i honors 0 and 0x prefixes, which are important for things like umask
- /* parse options ... */
- char *token;
- while ( (token = strtok(NULL, w_space)) ) {
-
- if (strncmp(token, "local-hit=",10) == 0) {
- sscanf(&token[10], "%i", &tos_local_hit);
- } else if (strncmp(token, "sibling-hit=",12) == 0) {
- sscanf(&token[12], "%i", &tos_sibling_hit);
- } else if (strncmp(token, "parent-hit=",11) == 0) {
- sscanf(&token[11], "%i", &tos_parent_hit);
- } else if (strcmp(token, "disable-preserve-miss") == 0) {
- preserve_miss_tos = 0;
- preserve_miss_tos_mask = 0;
- } else if (preserve_miss_tos && strncmp(token, "miss-mask=",10) == 0) {
- sscanf(&token[10], "%i", &preserve_miss_tos_mask);
- }
- }
-}
-
-void
-Ip::QosConfig::dumpConfigLine(char *entry, const char *name) const
-{
- ; /* Not needed in stub */
-}
-
-#endif /* USE_ZPH_QOS */
+
+void
+Ip::Qos::getUpstreamTOS(fde *clientFde, const int server_fd)
+{
+#if USE_QOS_TOS
+ fatal ("Not implemented");
+#endif
+}
+
+void Ip::Qos::getUpstreamNfMark(const fde *clientFde, const fde *servFde, const int server_fd)
+{
+#if USE_QOS_NFMARK
+ fatal ("Not implemented");
+#endif
+}
+
+#if USE_QOS_NFMARK
+int
+Ip::Qos::GetNfMarkCallback(enum nf_conntrack_msg_type type,
+ struct nf_conntrack *ct,
+ void *data)
+{
+ fatal ("Not implemented");
+}
+#endif
+
+unsigned char
+Ip::Qos::doTosLocalMiss(const int fd, const HierarchyLogEntry *hier) const
+{
+ fatal ("Not implemented");
+}
+
+int
+Ip::Qos::doNfmarkLocalMiss(const int fd, const HierarchyLogEntry hier) const
+{
+ fatal ("Not implemented");
+}
+
+int
+Ip::Qos::doTosLocalHit(const int fd) const
+{
+ fatal ("Not implemented");
+}
+
+int
+Ip::Qos::doNfmarkLocalHit(const int fd) const
+{
+ fatal ("Not implemented");
+}
+
+Ip::Qos::Config()
+{
+ fatal ("Not implemented");
+}
+
+Ip::Qos::~Config()
+{
+ fatal ("Not implemented");
+}
+
+void
+Ip::Qos::parseConfigLine()
+{
+ fatal ("Not implemented");
+}
+
+void
+Ip::Qos::dumpConfigLine(char *entry, const char *name)
+{
+ fatal ("Not implemented");
+}
+
+#if !_USE_INLINE_
+#include "Qos.cci"
+#endif
=== modified file 'src/protos.h'
--- src/protos.h 2010-08-24 10:35:03 +0000
+++ src/protos.h 2010-09-04 13:26:56 +0000
@@ -408,7 +408,8 @@
SQUIDCEXTERN void peerDigestStatsReport(const PeerDigest * pd, StoreEntry * e);
extern Ip::Address getOutgoingAddr(HttpRequest * request, struct peer *dst_peer);
-unsigned long getOutgoingTOS(HttpRequest * request);
+unsigned char getOutgoingTOS(HttpRequest * request);
+uint32_t getOutgoingNfmark(HttpRequest * request);
SQUIDCEXTERN void urnStart(HttpRequest *, StoreEntry *);
=== modified file 'src/structs.h'
--- src/structs.h 2010-08-24 00:02:15 +0000
+++ src/structs.h 2010-09-04 15:07:25 +0000
@@ -98,7 +98,13 @@
struct acl_tos {
acl_tos *next;
ACLList *aclList;
- int tos;
+ unsigned char tos;
+};
+
+struct acl_nfmark {
+ acl_nfmark *next;
+ ACLList *aclList;
+ uint32_t nfmark;
};
struct acl_size_t {
@@ -466,8 +472,10 @@
acl_access *redirector;
acl_access *reply;
acl_address *outgoing_address;
- acl_tos *outgoing_tos;
- acl_tos *clientside_tos;
+ acl_tos *outgoingTos;
+ acl_tos *clientsideTos;
+ acl_nfmark *outgoingNfmark;
+ acl_nfmark *clientsideNfmark;
#if USE_HTCP
acl_access *htcp;
=== modified file 'src/tools.cc'
--- src/tools.cc 2010-08-09 11:06:36 +0000
+++ src/tools.cc 2010-09-05 08:06:28 +0000
@@ -43,6 +43,7 @@
#include "ProtoPort.h"
#include "SquidMath.h"
#include "SquidTime.h"
+#include "ip/Qos.h"
#include "ipc/Kids.h"
#include "ipc/Coordinator.h"
#include "SwapDir.h"
@@ -1306,8 +1307,7 @@
int rc = 0;
cap_value_t cap_list[10];
cap_list[ncaps++] = CAP_NET_BIND_SERVICE;
-
- if (Ip::Interceptor.TransparentActive()) {
+ if (Ip::Interceptor.TransparentActive() || Ip::Qos::TheConfig.isMarkActive() || isAclNfmarkActive()) {
cap_list[ncaps++] = CAP_NET_ADMIN;
}
=== modified file 'src/tunnel.cc'
--- src/tunnel.cc 2010-08-24 20:19:30 +0000
+++ src/tunnel.cc 2010-09-02 14:19:34 +0000
@@ -669,6 +669,7 @@
temp,
flags,
getOutgoingTOS(request),
+ getOutgoingNfmark(request),
url);
if (sock == COMM_ERROR) {