2 * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
11 #include "acl/Gadgets.h"
13 #include "comm/Connection.h"
14 #include "compat/cmsg.h"
15 #include "ConfigParser.h"
18 #include "hier_code.h"
19 #include "ip/QosConfig.h"
25 CBDATA_CLASS_INIT(acl_tos
);
29 aclDestroyAclList(&aclList
);
33 CBDATA_CLASS_INIT(acl_nfmark
);
35 acl_nfmark::~acl_nfmark()
37 aclDestroyAclList(&aclList
);
42 Ip::Qos::getTosFromServer(const Comm::ConnectionPointer
&server
, fde
*clientFde
)
44 #if USE_QOS_TOS && _SQUID_LINUX_
45 /* Bug 2537: This part of ZPH only applies to patched Linux kernels. */
47 int tos_len
= sizeof(tos
);
48 clientFde
->tosFromServer
= 0;
49 if (setsockopt(server
->fd
,SOL_IP
,IP_RECVTOS
,&tos
,tos_len
)==0) {
50 unsigned char buf
[512];
52 if (getsockopt(server
->fd
,SOL_IP
,IP_PKTOPTIONS
,buf
,(socklen_t
*)&len
) == 0) {
53 /* Parse the PKTOPTIONS structure to locate the TOS data message
54 * prepared in the kernel by the ZPH incoming TCP TOS preserving
57 unsigned char * pbuf
= buf
;
58 while (pbuf
-buf
< len
) {
59 struct cmsghdr
*o
= (struct cmsghdr
*)pbuf
;
63 if (o
->cmsg_level
== SOL_IP
&& o
->cmsg_type
== IP_TOS
) {
64 int *tmp
= (int*)SQUID_CMSG_DATA(o
);
65 clientFde
->tosFromServer
= (tos_t
)*tmp
;
68 pbuf
+= CMSG_LEN(o
->cmsg_len
);
72 debugs(33, DBG_IMPORTANT
, "QOS: error in getsockopt(IP_PKTOPTIONS) on " << server
<< " " << xstrerr(xerrno
));
76 debugs(33, DBG_IMPORTANT
, "QOS: error in setsockopt(IP_RECVTOS) on " << server
<< " " << xstrerr(xerrno
));
81 void Ip::Qos::getNfmarkFromServer(const Comm::ConnectionPointer
&server
, const fde
*clientFde
)
83 #if USE_LIBNETFILTERCONNTRACK
84 /* Allocate a new conntrack */
85 if (struct nf_conntrack
*ct
= nfct_new()) {
87 /* Prepare data needed to find the connection in the conntrack table.
88 * We need the local and remote IP address, and the local and remote
92 if (Ip::EnableIpv6
&& server
->local
.isIPv6()) {
93 nfct_set_attr_u8(ct
, ATTR_L3PROTO
, AF_INET6
);
94 struct in6_addr serv_fde_remote_ip6
;
95 server
->remote
.getInAddr(serv_fde_remote_ip6
);
96 nfct_set_attr(ct
, ATTR_IPV6_DST
, serv_fde_remote_ip6
.s6_addr
);
97 struct in6_addr serv_fde_local_ip6
;
98 server
->local
.getInAddr(serv_fde_local_ip6
);
99 nfct_set_attr(ct
, ATTR_IPV6_SRC
, serv_fde_local_ip6
.s6_addr
);
101 nfct_set_attr_u8(ct
, ATTR_L3PROTO
, AF_INET
);
102 struct in_addr serv_fde_remote_ip
;
103 server
->remote
.getInAddr(serv_fde_remote_ip
);
104 nfct_set_attr_u32(ct
, ATTR_IPV4_DST
, serv_fde_remote_ip
.s_addr
);
105 struct in_addr serv_fde_local_ip
;
106 server
->local
.getInAddr(serv_fde_local_ip
);
107 nfct_set_attr_u32(ct
, ATTR_IPV4_SRC
, serv_fde_local_ip
.s_addr
);
110 nfct_set_attr_u8(ct
, ATTR_L4PROTO
, IPPROTO_TCP
);
111 nfct_set_attr_u16(ct
, ATTR_PORT_DST
, htons(server
->remote
.port()));
112 nfct_set_attr_u16(ct
, ATTR_PORT_SRC
, htons(server
->local
.port()));
114 /* Open a handle to the conntrack */
115 if (struct nfct_handle
*h
= nfct_open(CONNTRACK
, 0)) {
116 /* Register the callback. The callback function will record the mark value. */
117 nfct_callback_register(h
, NFCT_T_ALL
, getNfMarkCallback
, (void *)clientFde
);
118 /* Query the conntrack table using the data previously set */
119 int x
= nfct_query(h
, NFCT_Q_GET
, ct
);
121 debugs(17, 2, "QOS: Failed to retrieve connection mark: (" << x
<< ") " << strerror(errno
)
122 << " (Destination " << server
->remote
<< ", source " << server
->local
<< ")" );
126 debugs(17, 2, "QOS: Failed to open conntrack handle for upstream netfilter mark retrieval.");
131 debugs(17, 2, "QOS: Failed to allocate new conntrack for upstream netfilter mark retrieval.");
136 #if USE_LIBNETFILTERCONNTRACK
138 Ip::Qos::getNfMarkCallback(enum nf_conntrack_msg_type
,
139 struct nf_conntrack
*ct
,
142 fde
*clientFde
= (fde
*)data
;
143 clientFde
->nfmarkFromServer
= nfct_get_attr_u32(ct
, ATTR_MARK
);
144 debugs(17, 3, "QOS: Retrieved connection mark value: " << clientFde
->nfmarkFromServer
);
146 return NFCT_CB_CONTINUE
;
151 Ip::Qos::doTosLocalMiss(const Comm::ConnectionPointer
&conn
, const hier_code hierCode
)
154 if (Ip::Qos::TheConfig
.tosSiblingHit
&& hierCode
==SIBLING_HIT
) {
155 tos
= Ip::Qos::TheConfig
.tosSiblingHit
;
156 debugs(33, 2, "QOS: Sibling Peer hit with hier code=" << hierCode
<< ", TOS=" << int(tos
));
157 } else if (Ip::Qos::TheConfig
.tosParentHit
&& hierCode
==PARENT_HIT
) {
158 tos
= Ip::Qos::TheConfig
.tosParentHit
;
159 debugs(33, 2, "QOS: Parent Peer hit with hier code=" << hierCode
<< ", TOS=" << int(tos
));
160 } else if (Ip::Qos::TheConfig
.preserveMissTos
) {
161 tos
= fd_table
[conn
->fd
].tosFromServer
& Ip::Qos::TheConfig
.preserveMissTosMask
;
162 tos
= (tos
& ~Ip::Qos::TheConfig
.tosMissMask
) | (Ip::Qos::TheConfig
.tosMiss
& Ip::Qos::TheConfig
.tosMissMask
);
163 debugs(33, 2, "QOS: Preserving TOS on miss, TOS=" << int(tos
));
164 } else if (Ip::Qos::TheConfig
.tosMiss
) {
165 tos
= Ip::Qos::TheConfig
.tosMiss
& Ip::Qos::TheConfig
.tosMissMask
;
166 debugs(33, 2, "QOS: Cache miss, setting TOS=" << int(tos
));
168 return setSockTos(conn
, tos
);
172 Ip::Qos::doNfmarkLocalMiss(const Comm::ConnectionPointer
&conn
, const hier_code hierCode
)
175 if (Ip::Qos::TheConfig
.markSiblingHit
&& hierCode
==SIBLING_HIT
) {
176 mark
= Ip::Qos::TheConfig
.markSiblingHit
;
177 debugs(33, 2, "QOS: Sibling Peer hit with hier code=" << hierCode
<< ", Mark=" << mark
);
178 } else if (Ip::Qos::TheConfig
.markParentHit
&& hierCode
==PARENT_HIT
) {
179 mark
= Ip::Qos::TheConfig
.markParentHit
;
180 debugs(33, 2, "QOS: Parent Peer hit with hier code=" << hierCode
<< ", Mark=" << mark
);
181 } else if (Ip::Qos::TheConfig
.preserveMissMark
) {
182 mark
= fd_table
[conn
->fd
].nfmarkFromServer
& Ip::Qos::TheConfig
.preserveMissMarkMask
;
183 mark
= (mark
& ~Ip::Qos::TheConfig
.markMissMask
) | (Ip::Qos::TheConfig
.markMiss
& Ip::Qos::TheConfig
.markMissMask
);
184 debugs(33, 2, "QOS: Preserving mark on miss, Mark=" << mark
);
185 } else if (Ip::Qos::TheConfig
.markMiss
) {
186 mark
= Ip::Qos::TheConfig
.markMiss
& Ip::Qos::TheConfig
.markMissMask
;
187 debugs(33, 2, "QOS: Cache miss, setting Mark=" << mark
);
189 return setSockNfmark(conn
, mark
);
193 Ip::Qos::doTosLocalHit(const Comm::ConnectionPointer
&conn
)
195 debugs(33, 2, "QOS: Setting TOS for local hit, TOS=" << int(Ip::Qos::TheConfig
.tosLocalHit
));
196 return setSockTos(conn
, Ip::Qos::TheConfig
.tosLocalHit
);
200 Ip::Qos::doNfmarkLocalHit(const Comm::ConnectionPointer
&conn
)
202 debugs(33, 2, "QOS: Setting netfilter mark for local hit, mark=" << Ip::Qos::TheConfig
.markLocalHit
);
203 return setSockNfmark(conn
, Ip::Qos::TheConfig
.markLocalHit
);
206 /* Qos::Config class */
208 Ip::Qos::Config
Ip::Qos::TheConfig
;
210 Ip::Qos::Config::Config() : tosLocalHit(0), tosSiblingHit(0), tosParentHit(0),
211 tosMiss(0), tosMissMask(0), preserveMissTos(false),
212 preserveMissTosMask(0xFF), markLocalHit(0), markSiblingHit(0),
213 markParentHit(0), markMiss(0), markMissMask(0),
214 preserveMissMark(false), preserveMissMarkMask(0xFFFFFFFF),
215 tosToServer(NULL
), tosToClient(NULL
), nfmarkToServer(NULL
),
221 Ip::Qos::Config::parseConfigLine()
223 /* parse options ... */
225 /* These are set as appropriate and then used to check whether the initial loop has been done */
228 /* Assume preserve is true. We don't set at initialisation as this affects isHitTosActive().
229 We have to do this now, as we may never match the 'tos' parameter below */
231 debugs(3, DBG_CRITICAL
, "ERROR: Invalid option 'qos_flows'. QOS features not enabled in this build");
235 while ( (token
= ConfigParser::NextToken()) ) {
237 // Work out TOS or mark. Default to TOS for backwards compatibility
238 if (!(mark
|| tos
)) {
239 if (strncmp(token
, "mark",4) == 0) {
240 #if SO_MARK && USE_LIBCAP
242 // Assume preserve is true. We don't set at initialisation as this affects isHitNfmarkActive()
243 #if USE_LIBNETFILTERCONNTRACK
244 preserveMissMark
= true;
245 # else // USE_LIBNETFILTERCONNTRACK
246 preserveMissMark
= false;
247 debugs(3, DBG_IMPORTANT
, "WARNING: Squid not compiled with Netfilter conntrack library. "
248 << "Netfilter mark preservation not available.");
249 #endif // USE_LIBNETFILTERCONNTRACK
250 #elif SO_MARK // SO_MARK && USE_LIBCAP
251 debugs(3, DBG_CRITICAL
, "ERROR: Invalid parameter 'mark' in qos_flows option. "
252 << "Linux Netfilter marking not available without LIBCAP support.");
254 #else // SO_MARK && USE_LIBCAP
255 debugs(3, DBG_CRITICAL
, "ERROR: Invalid parameter 'mark' in qos_flows option. "
256 << "Linux Netfilter marking not available on this platform.");
258 #endif // SO_MARK && USE_LIBCAP
259 } else if (strncmp(token
, "tos",3) == 0) {
260 preserveMissTos
= true;
263 preserveMissTos
= true;
268 if (strncmp(token
, "local-hit=",10) == 0) {
271 if (!xstrtoui(&token
[10], NULL
, &markLocalHit
, 0, std::numeric_limits
<nfmark_t
>::max())) {
272 debugs(3, DBG_CRITICAL
, "ERROR: Bad mark local-hit value " << &token
[10]);
277 if (!xstrtoui(&token
[10], NULL
, &v
, 0, std::numeric_limits
<tos_t
>::max())) {
278 debugs(3, DBG_CRITICAL
, "ERROR: Bad TOS local-hit value " << &token
[10]);
281 tosLocalHit
= (tos_t
)v
;
284 } else if (strncmp(token
, "sibling-hit=",12) == 0) {
287 if (!xstrtoui(&token
[12], NULL
, &markSiblingHit
, 0, std::numeric_limits
<nfmark_t
>::max())) {
288 debugs(3, DBG_CRITICAL
, "ERROR: Bad mark sibling-hit value " << &token
[12]);
293 if (!xstrtoui(&token
[12], NULL
, &v
, 0, std::numeric_limits
<tos_t
>::max())) {
294 debugs(3, DBG_CRITICAL
, "ERROR: Bad TOS sibling-hit value " << &token
[12]);
297 tosSiblingHit
= (tos_t
)v
;
300 } else if (strncmp(token
, "parent-hit=",11) == 0) {
303 if (!xstrtoui(&token
[11], NULL
, &markParentHit
, 0, std::numeric_limits
<nfmark_t
>::max())) {
304 debugs(3, DBG_CRITICAL
, "ERROR: Bad mark parent-hit value " << &token
[11]);
309 if (!xstrtoui(&token
[11], NULL
, &v
, 0, std::numeric_limits
<tos_t
>::max())) {
310 debugs(3, DBG_CRITICAL
, "ERROR: Bad TOS parent-hit value " << &token
[11]);
313 tosParentHit
= (tos_t
)v
;
316 } else if (strncmp(token
, "miss=",5) == 0) {
320 if (!xstrtoui(&token
[5], &end
, &markMiss
, 0, std::numeric_limits
<nfmark_t
>::max())) {
321 debugs(3, DBG_CRITICAL
, "ERROR: Bad mark miss value " << &token
[5]);
325 if (!xstrtoui(end
+ 1, NULL
, &markMissMask
, 0, std::numeric_limits
<nfmark_t
>::max())) {
326 debugs(3, DBG_CRITICAL
, "ERROR: Bad mark miss mask value " << (end
+ 1) << ". Using 0xFFFFFFFF instead.");
327 markMissMask
= 0xFFFFFFFF;
330 markMissMask
= 0xFFFFFFFF;
334 if (!xstrtoui(&token
[5], &end
, &v
, 0, std::numeric_limits
<tos_t
>::max())) {
335 debugs(3, DBG_CRITICAL
, "ERROR: Bad TOS miss value " << &token
[5]);
340 if (!xstrtoui(end
+ 1, NULL
, &v
, 0, std::numeric_limits
<tos_t
>::max())) {
341 debugs(3, DBG_CRITICAL
, "ERROR: Bad TOS miss mask value " << (end
+ 1) << ". Using 0xFF instead.");
344 tosMissMask
= (tos_t
)v
;
350 } else if (strcmp(token
, "disable-preserve-miss") == 0) {
352 if (preserveMissTosMask
!=0xFFU
|| preserveMissMarkMask
!=0xFFFFFFFFU
) {
353 debugs(3, DBG_CRITICAL
, "ERROR: miss-mask feature cannot be set with disable-preserve-miss");
357 preserveMissMark
= false;
358 preserveMissMarkMask
= 0;
360 preserveMissTos
= false;
361 preserveMissTosMask
= 0;
364 } else if (strncmp(token
, "miss-mask=",10) == 0) {
366 if (mark
&& preserveMissMark
) {
367 if (!xstrtoui(&token
[10], NULL
, &preserveMissMarkMask
, 0, std::numeric_limits
<nfmark_t
>::max())) {
368 debugs(3, DBG_CRITICAL
, "ERROR: Bad mark miss-mark value " << &token
[10]);
371 } else if (preserveMissTos
) {
373 if (!xstrtoui(&token
[10], NULL
, &v
, 0, std::numeric_limits
<tos_t
>::max())) {
374 debugs(3, DBG_CRITICAL
, "ERROR: Bad TOS miss-mark value " << &token
[10]);
377 preserveMissTosMask
= (tos_t
)v
;
379 debugs(3, DBG_CRITICAL
, "ERROR: miss-mask feature cannot be set without miss-preservation enabled");
388 * NOTE: Due to the low-level nature of the library these
389 * objects are part of the dump function must be self-contained.
390 * which means no StoreEntry refrences. Just a basic char* buffer.
393 Ip::Qos::Config::dumpConfigLine(char *entry
, const char *name
) const
396 if (isHitTosActive()) {
398 p
+= snprintf(p
, 11, "%s", name
); // strlen("qos_flows ");
399 p
+= snprintf(p
, 4, "%s", "tos");
401 if (tosLocalHit
> 0) {
402 p
+= snprintf(p
, 16, " local-hit=0x%02X", tosLocalHit
);
404 if (tosSiblingHit
> 0) {
405 p
+= snprintf(p
, 18, " sibling-hit=0x%02X", tosSiblingHit
);
407 if (tosParentHit
> 0) {
408 p
+= snprintf(p
, 17, " parent-hit=0x%02X", tosParentHit
);
411 p
+= snprintf(p
, 11, " miss=0x%02X", tosMiss
);
412 if (tosMissMask
!=0xFFU
) {
413 p
+= snprintf(p
, 6, "/0x%02X", tosMissMask
);
416 if (preserveMissTos
== 0) {
417 p
+= snprintf(p
, 23, " disable-preserve-miss");
419 if (preserveMissTos
&& preserveMissTosMask
!= 0) {
420 p
+= snprintf(p
, 16, " miss-mask=0x%02X", preserveMissTosMask
);
422 p
+= snprintf(p
, 2, "\n");
425 if (isHitNfmarkActive()) {
426 p
+= snprintf(p
, 11, "%s", name
); // strlen("qos_flows ");
427 p
+= snprintf(p
, 5, "%s", "mark");
429 if (markLocalHit
> 0) {
430 p
+= snprintf(p
, 22, " local-hit=0x%02X", markLocalHit
);
432 if (markSiblingHit
> 0) {
433 p
+= snprintf(p
, 24, " sibling-hit=0x%02X", markSiblingHit
);
435 if (markParentHit
> 0) {
436 p
+= snprintf(p
, 23, " parent-hit=0x%02X", markParentHit
);
439 p
+= snprintf(p
, 17, " miss=0x%02X", markMiss
);
440 if (markMissMask
!=0xFFFFFFFFU
) {
441 p
+= snprintf(p
, 12, "/0x%02X", markMissMask
);
444 if (preserveMissMark
== false) {
445 p
+= snprintf(p
, 23, " disable-preserve-miss");
447 if (preserveMissMark
&& preserveMissMarkMask
!= 0) {
448 p
+= snprintf(p
, 22, " miss-mask=0x%02X", preserveMissMarkMask
);
450 p
+= snprintf(p
, 2, "\n");