2 * Copyright (C) 1996-2022 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.
10 #include "acl/Gadgets.h"
11 #include "base/IoManip.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"
26 CBDATA_CLASS_INIT(acl_tos
);
30 aclDestroyAclList(&aclList
);
34 CBDATA_CLASS_INIT(acl_nfmark
);
36 acl_nfmark::~acl_nfmark()
38 aclDestroyAclList(&aclList
);
43 Ip::Qos::getTosFromServer(const Comm::ConnectionPointer
&server
, fde
*clientFde
)
45 #if USE_QOS_TOS && _SQUID_LINUX_
46 /* Bug 2537: This part of ZPH only applies to patched Linux kernels. */
48 int tos_len
= sizeof(tos
);
49 clientFde
->tosFromServer
= 0;
50 if (setsockopt(server
->fd
,SOL_IP
,IP_RECVTOS
,&tos
,tos_len
)==0) {
51 unsigned char buf
[512];
53 if (getsockopt(server
->fd
,SOL_IP
,IP_PKTOPTIONS
,buf
,(socklen_t
*)&len
) == 0) {
54 /* Parse the PKTOPTIONS structure to locate the TOS data message
55 * prepared in the kernel by the ZPH incoming TCP TOS preserving
58 unsigned char * pbuf
= buf
;
59 while (pbuf
-buf
< len
) {
60 struct cmsghdr
*o
= (struct cmsghdr
*)pbuf
;
64 if (o
->cmsg_level
== SOL_IP
&& o
->cmsg_type
== IP_TOS
) {
65 int *tmp
= (int*)SQUID_CMSG_DATA(o
);
66 clientFde
->tosFromServer
= (tos_t
)*tmp
;
69 pbuf
+= CMSG_LEN(o
->cmsg_len
);
73 debugs(33, DBG_IMPORTANT
, "ERROR: QOS: getsockopt(IP_PKTOPTIONS) failure on " << server
<< " " << xstrerr(xerrno
));
77 debugs(33, DBG_IMPORTANT
, "ERROR: QOS: setsockopt(IP_RECVTOS) failure on " << server
<< " " << xstrerr(xerrno
));
85 #if USE_LIBNETFILTERCONNTRACK
87 * Callback function to mark connection once it's been found.
88 * This function is called by the libnetfilter_conntrack
89 * libraries, during nfct_query in Ip::Qos::getNfConnmark.
90 * nfct_callback_register is used to register this function.
91 * @param nf_conntrack_msg_type Type of conntrack message
92 * @param nf_conntrack Pointer to the conntrack structure
93 * @param mark Pointer to nfmark_t mark
96 getNfmarkCallback(enum nf_conntrack_msg_type
, struct nf_conntrack
*ct
, void *connmark
)
98 auto *mark
= static_cast<nfmark_t
*>(connmark
);
99 *mark
= nfct_get_attr_u32(ct
, ATTR_MARK
);
100 debugs(17, 3, asHex(*mark
));
101 return NFCT_CB_CONTINUE
;
105 * Prepares a conntrack query for specified source and destination.
106 * This can be used for querying or modifying attributes.
108 static nf_conntrack
*
109 prepareConntrackQuery(const Ip::Address
&src
, const Ip::Address
&dst
)
111 /* Allocate a new conntrack */
112 if (auto ct
= nfct_new()) {
113 // Prepare data needed to find the connection in the conntrack table.
114 // We need the local and remote IP address, and the local and remote
116 if (Ip::EnableIpv6
&& src
.isIPv6()) {
117 nfct_set_attr_u8(ct
, ATTR_L3PROTO
, AF_INET6
);
118 struct in6_addr conn_fde_dst_ip6
;
119 dst
.getInAddr(conn_fde_dst_ip6
);
120 nfct_set_attr(ct
, ATTR_ORIG_IPV6_DST
, conn_fde_dst_ip6
.s6_addr
);
121 struct in6_addr conn_fde_src_ip6
;
122 src
.getInAddr(conn_fde_src_ip6
);
123 nfct_set_attr(ct
, ATTR_ORIG_IPV6_SRC
, conn_fde_src_ip6
.s6_addr
);
125 nfct_set_attr_u8(ct
, ATTR_L3PROTO
, AF_INET
);
126 struct in_addr conn_fde_dst_ip
;
127 dst
.getInAddr(conn_fde_dst_ip
);
128 nfct_set_attr_u32(ct
, ATTR_ORIG_IPV4_DST
, conn_fde_dst_ip
.s_addr
);
129 struct in_addr conn_fde_src_ip
;
130 src
.getInAddr(conn_fde_src_ip
);
131 nfct_set_attr_u32(ct
, ATTR_ORIG_IPV4_SRC
, conn_fde_src_ip
.s_addr
);
134 nfct_set_attr_u8(ct
, ATTR_L4PROTO
, IPPROTO_TCP
);
135 nfct_set_attr_u16(ct
, ATTR_ORIG_PORT_DST
, htons(dst
.port()));
136 nfct_set_attr_u16(ct
, ATTR_ORIG_PORT_SRC
, htons(src
.port()));
146 Ip::Qos::getNfConnmark(const Comm::ConnectionPointer
&conn
, const Ip::Qos::ConnectionDirection connDir
)
149 #if USE_LIBNETFILTERCONNTRACK
150 const auto src
= (connDir
== Ip::Qos::dirAccepted
) ? conn
->remote
: conn
->local
;
151 const auto dst
= (connDir
== Ip::Qos::dirAccepted
) ? conn
->local
: conn
->remote
;
153 if (const auto ct
= prepareConntrackQuery(src
, dst
)) {
154 // Open a handle to the conntrack
155 if (struct nfct_handle
*h
= nfct_open(CONNTRACK
, 0)) {
156 // Register the callback. The callback function will record the mark value.
157 nfct_callback_register(h
, NFCT_T_ALL
, getNfmarkCallback
, static_cast<void *>(&mark
));
158 // Query the conntrack table using the data previously set
159 int x
= nfct_query(h
, NFCT_Q_GET
, ct
);
161 const int xerrno
= errno
;
162 debugs(17, 2, "QOS: Failed to retrieve connection mark: (" << x
<< ") " << xstrerr(xerrno
)
163 << " (Destination " << dst
<< ", source " << src
<< ")" );
167 debugs(17, 2, "QOS: Failed to open conntrack handle for netfilter CONNMARK retrieval.");
171 debugs(17, 2, "QOS: Failed to allocate new conntrack for netfilter CONNMARK retrieval.");
181 Ip::Qos::setNfConnmark(Comm::ConnectionPointer
&conn
, const Ip::Qos::ConnectionDirection connDir
, const Ip::NfMarkConfig
&cm
)
185 #if USE_LIBNETFILTERCONNTRACK
186 const auto src
= (connDir
== Ip::Qos::dirAccepted
) ? conn
->remote
: conn
->local
;
187 const auto dst
= (connDir
== Ip::Qos::dirAccepted
) ? conn
->local
: conn
->remote
;
189 const nfmark_t newMark
= cm
.applyToMark(conn
->nfConnmark
);
191 // No need to do anything if a CONNMARK has not changed.
192 if (newMark
== conn
->nfConnmark
)
195 if (const auto ct
= prepareConntrackQuery(src
, dst
)) {
196 // Open a handle to the conntrack
197 if (struct nfct_handle
*h
= nfct_open(CONNTRACK
, 0)) {
198 nfct_set_attr_u32(ct
, ATTR_MARK
, newMark
);
199 // Update the conntrack table using the new mark. We do not need a callback here.
200 const int queryResult
= nfct_query(h
, NFCT_Q_UPDATE
, ct
);
201 if (queryResult
== 0) {
202 conn
->nfConnmark
= newMark
;
205 const int xerrno
= errno
;
206 debugs(17, 2, "QOS: Failed to modify connection mark: (" << queryResult
<< ") " << xstrerr(xerrno
)
207 << " (Destination " << dst
<< ", source " << src
<< ")" );
211 debugs(17, 2, "QOS: Failed to open conntrack handle for netfilter CONNMARK modification.");
215 debugs(17, 2, "QOS: Failed to allocate new conntrack for netfilter CONNMARK modification.");
217 #else /* USE_LIBNETFILTERCONNTRACK */
221 #endif /* USE_LIBNETFILTERCONNTRACK */
226 Ip::Qos::doTosLocalMiss(const Comm::ConnectionPointer
&conn
, const hier_code hierCode
)
229 if (Ip::Qos::TheConfig
.tosSiblingHit
&& hierCode
==SIBLING_HIT
) {
230 tos
= Ip::Qos::TheConfig
.tosSiblingHit
;
231 debugs(33, 2, "QOS: Sibling Peer hit with hier code=" << hierCode
<< ", TOS=" << int(tos
));
232 } else if (Ip::Qos::TheConfig
.tosParentHit
&& hierCode
==PARENT_HIT
) {
233 tos
= Ip::Qos::TheConfig
.tosParentHit
;
234 debugs(33, 2, "QOS: Parent Peer hit with hier code=" << hierCode
<< ", TOS=" << int(tos
));
235 } else if (Ip::Qos::TheConfig
.preserveMissTos
) {
236 tos
= fd_table
[conn
->fd
].tosFromServer
& Ip::Qos::TheConfig
.preserveMissTosMask
;
237 tos
= (tos
& ~Ip::Qos::TheConfig
.tosMissMask
) | (Ip::Qos::TheConfig
.tosMiss
& Ip::Qos::TheConfig
.tosMissMask
);
238 debugs(33, 2, "QOS: Preserving TOS on miss, TOS=" << int(tos
));
239 } else if (Ip::Qos::TheConfig
.tosMiss
) {
240 tos
= Ip::Qos::TheConfig
.tosMiss
& Ip::Qos::TheConfig
.tosMissMask
;
241 debugs(33, 2, "QOS: Cache miss, setting TOS=" << int(tos
));
243 return setSockTos(conn
, tos
);
247 Ip::Qos::doNfmarkLocalMiss(const Comm::ConnectionPointer
&conn
, const hier_code hierCode
)
250 if (Ip::Qos::TheConfig
.markSiblingHit
&& hierCode
==SIBLING_HIT
) {
251 mark
= Ip::Qos::TheConfig
.markSiblingHit
;
252 debugs(33, 2, "QOS: Sibling Peer hit with hier code=" << hierCode
<< ", Mark=" << mark
);
253 } else if (Ip::Qos::TheConfig
.markParentHit
&& hierCode
==PARENT_HIT
) {
254 mark
= Ip::Qos::TheConfig
.markParentHit
;
255 debugs(33, 2, "QOS: Parent Peer hit with hier code=" << hierCode
<< ", Mark=" << mark
);
256 } else if (Ip::Qos::TheConfig
.preserveMissMark
) {
257 mark
= fd_table
[conn
->fd
].nfConnmarkFromServer
& Ip::Qos::TheConfig
.preserveMissMarkMask
;
258 mark
= (mark
& ~Ip::Qos::TheConfig
.markMissMask
) | (Ip::Qos::TheConfig
.markMiss
& Ip::Qos::TheConfig
.markMissMask
);
259 debugs(33, 2, "QOS: Preserving mark on miss, Mark=" << mark
);
260 } else if (Ip::Qos::TheConfig
.markMiss
) {
261 mark
= Ip::Qos::TheConfig
.markMiss
& Ip::Qos::TheConfig
.markMissMask
;
262 debugs(33, 2, "QOS: Cache miss, setting Mark=" << mark
);
264 return setSockNfmark(conn
, mark
);
268 Ip::Qos::doTosLocalHit(const Comm::ConnectionPointer
&conn
)
270 debugs(33, 2, "QOS: Setting TOS for local hit, TOS=" << int(Ip::Qos::TheConfig
.tosLocalHit
));
271 return setSockTos(conn
, Ip::Qos::TheConfig
.tosLocalHit
);
275 Ip::Qos::doNfmarkLocalHit(const Comm::ConnectionPointer
&conn
)
277 debugs(33, 2, "QOS: Setting netfilter mark for local hit, mark=" << Ip::Qos::TheConfig
.markLocalHit
);
278 return setSockNfmark(conn
, Ip::Qos::TheConfig
.markLocalHit
);
281 /* Qos::Config class */
283 Ip::Qos::Config
Ip::Qos::TheConfig
;
285 Ip::Qos::Config::Config() : tosLocalHit(0), tosSiblingHit(0), tosParentHit(0),
286 tosMiss(0), tosMissMask(0), preserveMissTos(false),
287 preserveMissTosMask(0xFF), markLocalHit(0), markSiblingHit(0),
288 markParentHit(0), markMiss(0), markMissMask(0),
289 preserveMissMark(false), preserveMissMarkMask(0xFFFFFFFF),
290 tosToServer(nullptr), tosToClient(nullptr), nfmarkToServer(nullptr),
291 nfmarkToClient(nullptr)
296 Ip::Qos::Config::parseConfigLine()
298 /* parse options ... */
300 /* These are set as appropriate and then used to check whether the initial loop has been done */
303 /* Assume preserve is true. We don't set at initialisation as this affects isHitTosActive().
304 We have to do this now, as we may never match the 'tos' parameter below */
306 debugs(3, DBG_CRITICAL
, "ERROR: Invalid option 'qos_flows'. QOS features not enabled in this build");
310 while ( (token
= ConfigParser::NextToken()) ) {
312 // Work out TOS or mark. Default to TOS for backwards compatibility
313 if (!(mark
|| tos
)) {
314 if (strncmp(token
, "mark",4) == 0) {
315 #if SO_MARK && USE_LIBCAP
317 // Assume preserve is true. We don't set at initialisation as this affects isHitNfmarkActive()
318 #if USE_LIBNETFILTERCONNTRACK
319 preserveMissMark
= true;
320 # else // USE_LIBNETFILTERCONNTRACK
321 preserveMissMark
= false;
322 debugs(3, DBG_IMPORTANT
, "WARNING: Squid not compiled with Netfilter conntrack library. "
323 << "Netfilter mark preservation not available.");
324 #endif // USE_LIBNETFILTERCONNTRACK
325 #elif SO_MARK // SO_MARK && USE_LIBCAP
326 debugs(3, DBG_CRITICAL
, "ERROR: Invalid parameter 'mark' in qos_flows option. "
327 << "Linux Netfilter marking not available without LIBCAP support.");
329 #else // SO_MARK && USE_LIBCAP
330 debugs(3, DBG_CRITICAL
, "ERROR: Invalid parameter 'mark' in qos_flows option. "
331 << "Linux Netfilter marking not available on this platform.");
333 #endif // SO_MARK && USE_LIBCAP
334 } else if (strncmp(token
, "tos",3) == 0) {
335 preserveMissTos
= true;
338 preserveMissTos
= true;
343 if (strncmp(token
, "local-hit=",10) == 0) {
346 if (!xstrtoui(&token
[10], nullptr, &markLocalHit
, 0, std::numeric_limits
<nfmark_t
>::max())) {
347 debugs(3, DBG_CRITICAL
, "ERROR: Bad mark local-hit value " << &token
[10]);
352 if (!xstrtoui(&token
[10], nullptr, &v
, 0, std::numeric_limits
<tos_t
>::max())) {
353 debugs(3, DBG_CRITICAL
, "ERROR: Bad TOS local-hit value " << &token
[10]);
356 tosLocalHit
= (tos_t
)v
;
359 } else if (strncmp(token
, "sibling-hit=",12) == 0) {
362 if (!xstrtoui(&token
[12], nullptr, &markSiblingHit
, 0, std::numeric_limits
<nfmark_t
>::max())) {
363 debugs(3, DBG_CRITICAL
, "ERROR: Bad mark sibling-hit value " << &token
[12]);
368 if (!xstrtoui(&token
[12], nullptr, &v
, 0, std::numeric_limits
<tos_t
>::max())) {
369 debugs(3, DBG_CRITICAL
, "ERROR: Bad TOS sibling-hit value " << &token
[12]);
372 tosSiblingHit
= (tos_t
)v
;
375 } else if (strncmp(token
, "parent-hit=",11) == 0) {
378 if (!xstrtoui(&token
[11], nullptr, &markParentHit
, 0, std::numeric_limits
<nfmark_t
>::max())) {
379 debugs(3, DBG_CRITICAL
, "ERROR: Bad mark parent-hit value " << &token
[11]);
384 if (!xstrtoui(&token
[11], nullptr, &v
, 0, std::numeric_limits
<tos_t
>::max())) {
385 debugs(3, DBG_CRITICAL
, "ERROR: Bad TOS parent-hit value " << &token
[11]);
388 tosParentHit
= (tos_t
)v
;
391 } else if (strncmp(token
, "miss=",5) == 0) {
395 if (!xstrtoui(&token
[5], &end
, &markMiss
, 0, std::numeric_limits
<nfmark_t
>::max())) {
396 debugs(3, DBG_CRITICAL
, "ERROR: Bad mark miss value " << &token
[5]);
400 if (!xstrtoui(end
+ 1, nullptr, &markMissMask
, 0, std::numeric_limits
<nfmark_t
>::max())) {
401 debugs(3, DBG_CRITICAL
, "ERROR: Bad mark miss mask value " << (end
+ 1) << ". Using 0xFFFFFFFF instead.");
402 markMissMask
= 0xFFFFFFFF;
405 markMissMask
= 0xFFFFFFFF;
409 if (!xstrtoui(&token
[5], &end
, &v
, 0, std::numeric_limits
<tos_t
>::max())) {
410 debugs(3, DBG_CRITICAL
, "ERROR: Bad TOS miss value " << &token
[5]);
415 if (!xstrtoui(end
+ 1, nullptr, &v
, 0, std::numeric_limits
<tos_t
>::max())) {
416 debugs(3, DBG_CRITICAL
, "ERROR: Bad TOS miss mask value " << (end
+ 1) << ". Using 0xFF instead.");
419 tosMissMask
= (tos_t
)v
;
425 } else if (strcmp(token
, "disable-preserve-miss") == 0) {
427 if (preserveMissTosMask
!=0xFFU
|| preserveMissMarkMask
!=0xFFFFFFFFU
) {
428 debugs(3, DBG_CRITICAL
, "ERROR: miss-mask feature cannot be set with disable-preserve-miss");
432 preserveMissMark
= false;
433 preserveMissMarkMask
= 0;
435 preserveMissTos
= false;
436 preserveMissTosMask
= 0;
439 } else if (strncmp(token
, "miss-mask=",10) == 0) {
441 if (mark
&& preserveMissMark
) {
442 if (!xstrtoui(&token
[10], nullptr, &preserveMissMarkMask
, 0, std::numeric_limits
<nfmark_t
>::max())) {
443 debugs(3, DBG_CRITICAL
, "ERROR: Bad mark miss-mark value " << &token
[10]);
446 } else if (preserveMissTos
) {
448 if (!xstrtoui(&token
[10], nullptr, &v
, 0, std::numeric_limits
<tos_t
>::max())) {
449 debugs(3, DBG_CRITICAL
, "ERROR: Bad TOS miss-mark value " << &token
[10]);
452 preserveMissTosMask
= (tos_t
)v
;
454 debugs(3, DBG_CRITICAL
, "ERROR: miss-mask feature cannot be set without miss-preservation enabled");
463 * NOTE: Due to the low-level nature of the library these
464 * objects are part of the dump function must be self-contained.
465 * which means no StoreEntry references. Just a basic char* buffer.
468 Ip::Qos::Config::dumpConfigLine(char *entry
, const char *name
) const
471 if (isHitTosActive()) {
473 p
+= snprintf(p
, 11, "%s", name
); // strlen("qos_flows ");
474 p
+= snprintf(p
, 4, "%s", "tos");
476 if (tosLocalHit
> 0) {
477 p
+= snprintf(p
, 16, " local-hit=0x%02X", tosLocalHit
);
479 if (tosSiblingHit
> 0) {
480 p
+= snprintf(p
, 18, " sibling-hit=0x%02X", tosSiblingHit
);
482 if (tosParentHit
> 0) {
483 p
+= snprintf(p
, 17, " parent-hit=0x%02X", tosParentHit
);
486 p
+= snprintf(p
, 11, " miss=0x%02X", tosMiss
);
487 if (tosMissMask
!=0xFFU
) {
488 p
+= snprintf(p
, 6, "/0x%02X", tosMissMask
);
491 if (preserveMissTos
== 0) {
492 p
+= snprintf(p
, 23, " disable-preserve-miss");
494 if (preserveMissTos
&& preserveMissTosMask
!= 0) {
495 p
+= snprintf(p
, 16, " miss-mask=0x%02X", preserveMissTosMask
);
497 p
+= snprintf(p
, 2, "\n");
500 if (isHitNfmarkActive()) {
501 p
+= snprintf(p
, 11, "%s", name
); // strlen("qos_flows ");
502 p
+= snprintf(p
, 5, "%s", "mark");
504 if (markLocalHit
> 0) {
505 p
+= snprintf(p
, 22, " local-hit=0x%02X", markLocalHit
);
507 if (markSiblingHit
> 0) {
508 p
+= snprintf(p
, 24, " sibling-hit=0x%02X", markSiblingHit
);
510 if (markParentHit
> 0) {
511 p
+= snprintf(p
, 23, " parent-hit=0x%02X", markParentHit
);
514 p
+= snprintf(p
, 17, " miss=0x%02X", markMiss
);
515 if (markMissMask
!=0xFFFFFFFFU
) {
516 p
+= snprintf(p
, 12, "/0x%02X", markMissMask
);
519 if (preserveMissMark
== false) {
520 p
+= snprintf(p
, 23, " disable-preserve-miss");
522 if (preserveMissMark
&& preserveMissMarkMask
!= 0) {
523 p
+= snprintf(p
, 22, " miss-mask=0x%02X", preserveMissMarkMask
);
525 p
+= snprintf(p
, 2, "\n");
530 Ip::Qos::setSockTos(const int fd
, tos_t tos
, int type
)
532 // Bug 3731: FreeBSD produces 'invalid option'
533 // unless we pass it a 32-bit variable storing 8-bits of data.
534 // NP: it is documented as 'int' for all systems, even those like Linux which accept 8-bit char
535 // so we convert to a int before setting.
538 debugs(50, 3, "for FD " << fd
<< " to " << bTos
);
540 if (type
== AF_INET
) {
542 const int x
= setsockopt(fd
, IPPROTO_IP
, IP_TOS
, &bTos
, sizeof(bTos
));
545 debugs(50, 2, "setsockopt(IP_TOS) on " << fd
<< ": " << xstrerr(xerrno
));
549 debugs(50, DBG_IMPORTANT
, "WARNING: setsockopt(IP_TOS) not supported on this platform");
552 } else { // type == AF_INET6
553 #if defined(IPV6_TCLASS)
554 const int x
= setsockopt(fd
, IPPROTO_IPV6
, IPV6_TCLASS
, &bTos
, sizeof(bTos
));
557 debugs(50, 2, "setsockopt(IPV6_TCLASS) on " << fd
<< ": " << xstrerr(xerrno
));
561 debugs(50, DBG_IMPORTANT
, "WARNING: setsockopt(IPV6_TCLASS) not supported on this platform");
566 /* CANNOT REACH HERE */
570 Ip::Qos::setSockTos(const Comm::ConnectionPointer
&conn
, tos_t tos
)
572 const int x
= Ip::Qos::setSockTos(conn
->fd
, tos
, conn
->remote
.isIPv4() ? AF_INET
: AF_INET6
);
573 conn
->tos
= (x
>= 0) ? tos
: 0;
578 Ip::Qos::setSockNfmark(const int fd
, nfmark_t mark
)
580 #if SO_MARK && USE_LIBCAP
581 debugs(50, 3, "for FD " << fd
<< " to " << mark
);
582 const int x
= setsockopt(fd
, SOL_SOCKET
, SO_MARK
, &mark
, sizeof(nfmark_t
));
585 debugs(50, 2, "setsockopt(SO_MARK) on " << fd
<< ": " << xstrerr(xerrno
));
591 debugs(50, DBG_IMPORTANT
, "WARNING: setsockopt(SO_MARK) not supported on this platform");
596 debugs(50, DBG_IMPORTANT
, "WARNING: Netfilter marking disabled (netfilter marking requires build with LIBCAP)");
602 Ip::Qos::setSockNfmark(const Comm::ConnectionPointer
&conn
, nfmark_t mark
)
604 const int x
= Ip::Qos::setSockNfmark(conn
->fd
, mark
);
605 conn
->nfmark
= (x
>= 0) ? mark
: 0;
610 Ip::Qos::Config::isAclNfmarkActive() const
612 acl_nfmark
* nfmarkAcls
[] = { nfmarkToServer
, nfmarkToClient
};
614 for (int i
=0; i
<2; ++i
) {
615 while (nfmarkAcls
[i
]) {
616 acl_nfmark
*l
= nfmarkAcls
[i
];
617 if (!l
->markConfig
.isEmpty())
619 nfmarkAcls
[i
] = l
->next
;
627 Ip::Qos::Config::isAclTosActive() const
629 acl_tos
* tosAcls
[] = { tosToServer
, tosToClient
};
631 for (int i
=0; i
<2; ++i
) {
633 acl_tos
*l
= tosAcls
[i
];
636 tosAcls
[i
] = l
->next
;