]> git.ipfire.org Git - thirdparty/squid.git/blame - src/ip/QosConfig.cc
Source Format Enforcement (#532)
[thirdparty/squid.git] / src / ip / QosConfig.cc
CommitLineData
bbc27441 1/*
77b1029d 2 * Copyright (C) 1996-2020 The Squid Software Foundation and contributors
bbc27441
AJ
3 *
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.
7 */
8
582c2af2 9#include "squid.h"
425de4c8 10#include "acl/Gadgets.h"
8a01b99e 11#include "cache_cf.h"
b5523edc 12#include "comm/Connection.h"
e9601ed0 13#include "compat/cmsg.h"
425de4c8
AJ
14#include "ConfigParser.h"
15#include "fde.h"
582c2af2 16#include "globals.h"
425de4c8 17#include "hier_code.h"
f4f6c2e0 18#include "ip/QosConfig.h"
602d9612 19#include "ip/tools.h"
425de4c8 20#include "Parsing.h"
575cb927 21
1a30fdf5 22#include <cerrno>
575cb927 23
60019fea
AJ
24CBDATA_CLASS_INIT(acl_tos);
25
26acl_tos::~acl_tos()
27{
28 aclDestroyAclList(&aclList);
29 delete next;
30}
31
32CBDATA_CLASS_INIT(acl_nfmark);
33
34acl_nfmark::~acl_nfmark()
35{
36 aclDestroyAclList(&aclList);
37 delete next;
38}
39
425de4c8 40void
b5523edc 41Ip::Qos::getTosFromServer(const Comm::ConnectionPointer &server, fde *clientFde)
425de4c8 42{
f4f6c2e0
AJ
43#if USE_QOS_TOS && _SQUID_LINUX_
44 /* Bug 2537: This part of ZPH only applies to patched Linux kernels. */
425de4c8 45 tos_t tos = 1;
b5523edc 46 int tos_len = sizeof(tos);
425de4c8 47 clientFde->tosFromServer = 0;
b5523edc 48 if (setsockopt(server->fd,SOL_IP,IP_RECVTOS,&tos,tos_len)==0) {
425de4c8
AJ
49 unsigned char buf[512];
50 int len = 512;
b5523edc 51 if (getsockopt(server->fd,SOL_IP,IP_PKTOPTIONS,buf,(socklen_t*)&len) == 0) {
425de4c8
AJ
52 /* Parse the PKTOPTIONS structure to locate the TOS data message
53 * prepared in the kernel by the ZPH incoming TCP TOS preserving
54 * patch.
55 */
56 unsigned char * pbuf = buf;
57 while (pbuf-buf < len) {
58 struct cmsghdr *o = (struct cmsghdr*)pbuf;
59 if (o->cmsg_len<=0)
60 break;
575cb927 61
425de4c8 62 if (o->cmsg_level == SOL_IP && o->cmsg_type == IP_TOS) {
2ff9dfaa 63 int *tmp = (int*)SQUID_CMSG_DATA(o);
425de4c8
AJ
64 clientFde->tosFromServer = (tos_t)*tmp;
65 break;
66 }
67 pbuf += CMSG_LEN(o->cmsg_len);
68 }
69 } else {
b69e9ffa
AJ
70 int xerrno = errno;
71 debugs(33, DBG_IMPORTANT, "QOS: error in getsockopt(IP_PKTOPTIONS) on " << server << " " << xstrerr(xerrno));
425de4c8
AJ
72 }
73 } else {
b69e9ffa
AJ
74 int xerrno = errno;
75 debugs(33, DBG_IMPORTANT, "QOS: error in setsockopt(IP_RECVTOS) on " << server << " " << xstrerr(xerrno));
425de4c8
AJ
76 }
77#endif
78}
b7ac5457 79
425de4c8 80#if USE_LIBNETFILTERCONNTRACK
244da4ad
AG
81/**
82* Callback function to mark connection once it's been found.
83* This function is called by the libnetfilter_conntrack
84* libraries, during nfct_query in Ip::Qos::getNfConnmark.
85* nfct_callback_register is used to register this function.
86* @param nf_conntrack_msg_type Type of conntrack message
87* @param nf_conntrack Pointer to the conntrack structure
88* @param mark Pointer to nfmark_t mark
89*/
90static int
91getNfmarkCallback(enum nf_conntrack_msg_type, struct nf_conntrack *ct, void *connmark)
92{
93 auto *mark = static_cast<nfmark_t *>(connmark);
94 *mark = nfct_get_attr_u32(ct, ATTR_MARK);
95 debugs(17, 3, asHex(*mark));
96 return NFCT_CB_CONTINUE;
97}
425de4c8 98
244da4ad
AG
99/**
100* Prepares a conntrack query for specified source and destination.
101* This can be used for querying or modifying attributes.
102*/
103static nf_conntrack *
104prepareConntrackQuery(const Ip::Address &src, const Ip::Address &dst)
105{
106 /* Allocate a new conntrack */
107 if (auto ct = nfct_new()) {
108 // Prepare data needed to find the connection in the conntrack table.
109 // We need the local and remote IP address, and the local and remote
110 // port numbers.
653d9927 111 if (Ip::EnableIpv6 && src.isIPv6()) {
425de4c8 112 nfct_set_attr_u8(ct, ATTR_L3PROTO, AF_INET6);
653d9927
A
113 struct in6_addr conn_fde_dst_ip6;
114 dst.getInAddr(conn_fde_dst_ip6);
115 nfct_set_attr(ct, ATTR_ORIG_IPV6_DST, conn_fde_dst_ip6.s6_addr);
116 struct in6_addr conn_fde_src_ip6;
117 src.getInAddr(conn_fde_src_ip6);
118 nfct_set_attr(ct, ATTR_ORIG_IPV6_SRC, conn_fde_src_ip6.s6_addr);
425de4c8 119 } else {
425de4c8 120 nfct_set_attr_u8(ct, ATTR_L3PROTO, AF_INET);
653d9927
A
121 struct in_addr conn_fde_dst_ip;
122 dst.getInAddr(conn_fde_dst_ip);
123 nfct_set_attr_u32(ct, ATTR_ORIG_IPV4_DST, conn_fde_dst_ip.s_addr);
124 struct in_addr conn_fde_src_ip;
125 src.getInAddr(conn_fde_src_ip);
126 nfct_set_attr_u32(ct, ATTR_ORIG_IPV4_SRC, conn_fde_src_ip.s_addr);
425de4c8
AJ
127 }
128
129 nfct_set_attr_u8(ct, ATTR_L4PROTO, IPPROTO_TCP);
653d9927
A
130 nfct_set_attr_u16(ct, ATTR_ORIG_PORT_DST, htons(dst.port()));
131 nfct_set_attr_u16(ct, ATTR_ORIG_PORT_SRC, htons(src.port()));
425de4c8 132
244da4ad
AG
133 return ct;
134 }
135
136 return nullptr;
137}
138#endif
139
140nfmark_t
141Ip::Qos::getNfConnmark(const Comm::ConnectionPointer &conn, const Ip::Qos::ConnectionDirection connDir)
142{
143 nfmark_t mark = 0;
144#if USE_LIBNETFILTERCONNTRACK
145 const auto src = (connDir == Ip::Qos::dirAccepted) ? conn->remote : conn->local;
146 const auto dst = (connDir == Ip::Qos::dirAccepted) ? conn->local : conn->remote;
147
148 if (const auto ct = prepareConntrackQuery(src, dst)) {
149 // Open a handle to the conntrack
425de4c8 150 if (struct nfct_handle *h = nfct_open(CONNTRACK, 0)) {
244da4ad 151 // Register the callback. The callback function will record the mark value.
653d9927 152 nfct_callback_register(h, NFCT_T_ALL, getNfmarkCallback, static_cast<void *>(&mark));
244da4ad 153 // Query the conntrack table using the data previously set
425de4c8
AJ
154 int x = nfct_query(h, NFCT_Q_GET, ct);
155 if (x == -1) {
653d9927
A
156 const int xerrno = errno;
157 debugs(17, 2, "QOS: Failed to retrieve connection mark: (" << x << ") " << xstrerr(xerrno)
158 << " (Destination " << dst << ", source " << src << ")" );
425de4c8 159 }
425de4c8
AJ
160 nfct_close(h);
161 } else {
244da4ad 162 debugs(17, 2, "QOS: Failed to open conntrack handle for netfilter CONNMARK retrieval.");
425de4c8 163 }
425de4c8 164 nfct_destroy(ct);
425de4c8 165 } else {
244da4ad 166 debugs(17, 2, "QOS: Failed to allocate new conntrack for netfilter CONNMARK retrieval.");
425de4c8
AJ
167 }
168#endif
653d9927 169 return mark;
425de4c8
AJ
170}
171
244da4ad
AG
172bool
173Ip::Qos::setNfConnmark(Comm::ConnectionPointer &conn, const Ip::Qos::ConnectionDirection connDir, const Ip::NfMarkConfig &cm)
425de4c8 174{
244da4ad
AG
175 bool ret = false;
176
177#if USE_LIBNETFILTERCONNTRACK
178 const auto src = (connDir == Ip::Qos::dirAccepted) ? conn->remote : conn->local;
179 const auto dst = (connDir == Ip::Qos::dirAccepted) ? conn->local : conn->remote;
180
181 const nfmark_t newMark = cm.applyToMark(conn->nfConnmark);
182
183 // No need to do anything if a CONNMARK has not changed.
184 if (newMark == conn->nfConnmark)
185 return true;
186
187 if (const auto ct = prepareConntrackQuery(src, dst)) {
188 // Open a handle to the conntrack
189 if (struct nfct_handle *h = nfct_open(CONNTRACK, 0)) {
190 nfct_set_attr_u32(ct, ATTR_MARK, newMark);
191 // Update the conntrack table using the new mark. We do not need a callback here.
192 const int queryResult = nfct_query(h, NFCT_Q_UPDATE, ct);
193 if (queryResult == 0) {
194 conn->nfConnmark = newMark;
195 ret = true;
196 } else {
197 const int xerrno = errno;
198 debugs(17, 2, "QOS: Failed to modify connection mark: (" << queryResult << ") " << xstrerr(xerrno)
199 << " (Destination " << dst << ", source " << src << ")" );
200 }
201 nfct_close(h);
202 } else {
203 debugs(17, 2, "QOS: Failed to open conntrack handle for netfilter CONNMARK modification.");
204 }
205 nfct_destroy(ct);
206 } else {
207 debugs(17, 2, "QOS: Failed to allocate new conntrack for netfilter CONNMARK modification.");
208 }
425de4c8 209#endif
244da4ad
AG
210 return ret;
211}
425de4c8
AJ
212
213int
b5523edc 214Ip::Qos::doTosLocalMiss(const Comm::ConnectionPointer &conn, const hier_code hierCode)
425de4c8
AJ
215{
216 tos_t tos = 0;
217 if (Ip::Qos::TheConfig.tosSiblingHit && hierCode==SIBLING_HIT) {
218 tos = Ip::Qos::TheConfig.tosSiblingHit;
219 debugs(33, 2, "QOS: Sibling Peer hit with hier code=" << hierCode << ", TOS=" << int(tos));
220 } else if (Ip::Qos::TheConfig.tosParentHit && hierCode==PARENT_HIT) {
221 tos = Ip::Qos::TheConfig.tosParentHit;
222 debugs(33, 2, "QOS: Parent Peer hit with hier code=" << hierCode << ", TOS=" << int(tos));
a29d2a95 223 } else if (Ip::Qos::TheConfig.preserveMissTos) {
b5523edc 224 tos = fd_table[conn->fd].tosFromServer & Ip::Qos::TheConfig.preserveMissTosMask;
a29d2a95 225 tos = (tos & ~Ip::Qos::TheConfig.tosMissMask) | (Ip::Qos::TheConfig.tosMiss & Ip::Qos::TheConfig.tosMissMask);
425de4c8 226 debugs(33, 2, "QOS: Preserving TOS on miss, TOS=" << int(tos));
a29d2a95
AB
227 } else if (Ip::Qos::TheConfig.tosMiss) {
228 tos = Ip::Qos::TheConfig.tosMiss & Ip::Qos::TheConfig.tosMissMask;
229 debugs(33, 2, "QOS: Cache miss, setting TOS=" << int(tos));
425de4c8 230 }
b5523edc 231 return setSockTos(conn, tos);
425de4c8
AJ
232}
233
234int
b5523edc 235Ip::Qos::doNfmarkLocalMiss(const Comm::ConnectionPointer &conn, const hier_code hierCode)
425de4c8
AJ
236{
237 nfmark_t mark = 0;
238 if (Ip::Qos::TheConfig.markSiblingHit && hierCode==SIBLING_HIT) {
239 mark = Ip::Qos::TheConfig.markSiblingHit;
240 debugs(33, 2, "QOS: Sibling Peer hit with hier code=" << hierCode << ", Mark=" << mark);
241 } else if (Ip::Qos::TheConfig.markParentHit && hierCode==PARENT_HIT) {
242 mark = Ip::Qos::TheConfig.markParentHit;
243 debugs(33, 2, "QOS: Parent Peer hit with hier code=" << hierCode << ", Mark=" << mark);
425de4c8 244 } else if (Ip::Qos::TheConfig.preserveMissMark) {
244da4ad 245 mark = fd_table[conn->fd].nfConnmarkFromServer & Ip::Qos::TheConfig.preserveMissMarkMask;
a29d2a95 246 mark = (mark & ~Ip::Qos::TheConfig.markMissMask) | (Ip::Qos::TheConfig.markMiss & Ip::Qos::TheConfig.markMissMask);
425de4c8 247 debugs(33, 2, "QOS: Preserving mark on miss, Mark=" << mark);
a29d2a95
AB
248 } else if (Ip::Qos::TheConfig.markMiss) {
249 mark = Ip::Qos::TheConfig.markMiss & Ip::Qos::TheConfig.markMissMask;
250 debugs(33, 2, "QOS: Cache miss, setting Mark=" << mark);
425de4c8 251 }
b5523edc 252 return setSockNfmark(conn, mark);
425de4c8
AJ
253}
254
255int
b5523edc 256Ip::Qos::doTosLocalHit(const Comm::ConnectionPointer &conn)
425de4c8
AJ
257{
258 debugs(33, 2, "QOS: Setting TOS for local hit, TOS=" << int(Ip::Qos::TheConfig.tosLocalHit));
b5523edc 259 return setSockTos(conn, Ip::Qos::TheConfig.tosLocalHit);
425de4c8
AJ
260}
261
262int
b5523edc 263Ip::Qos::doNfmarkLocalHit(const Comm::ConnectionPointer &conn)
425de4c8
AJ
264{
265 debugs(33, 2, "QOS: Setting netfilter mark for local hit, mark=" << Ip::Qos::TheConfig.markLocalHit);
b5523edc 266 return setSockNfmark(conn, Ip::Qos::TheConfig.markLocalHit);
425de4c8
AJ
267}
268
269/* Qos::Config class */
270
271Ip::Qos::Config Ip::Qos::TheConfig;
272
fad2588a 273Ip::Qos::Config::Config() : tosLocalHit(0), tosSiblingHit(0), tosParentHit(0),
f53969cc
SM
274 tosMiss(0), tosMissMask(0), preserveMissTos(false),
275 preserveMissTosMask(0xFF), markLocalHit(0), markSiblingHit(0),
276 markParentHit(0), markMiss(0), markMissMask(0),
277 preserveMissMark(false), preserveMissMarkMask(0xFFFFFFFF),
278 tosToServer(NULL), tosToClient(NULL), nfmarkToServer(NULL),
279 nfmarkToClient(NULL)
425de4c8 280{
575cb927
AJ
281}
282
283void
425de4c8 284Ip::Qos::Config::parseConfigLine()
575cb927 285{
575cb927
AJ
286 /* parse options ... */
287 char *token;
425de4c8
AJ
288 /* These are set as appropriate and then used to check whether the initial loop has been done */
289 bool mark = false;
290 bool tos = false;
291 /* Assume preserve is true. We don't set at initialisation as this affects isHitTosActive().
292 We have to do this now, as we may never match the 'tos' parameter below */
293#if !USE_QOS_TOS
294 debugs(3, DBG_CRITICAL, "ERROR: Invalid option 'qos_flows'. QOS features not enabled in this build");
295 self_destruct();
296#endif
297
2eceb328 298 while ( (token = ConfigParser::NextToken()) ) {
575cb927 299
425de4c8
AJ
300 // Work out TOS or mark. Default to TOS for backwards compatibility
301 if (!(mark || tos)) {
302 if (strncmp(token, "mark",4) == 0) {
11e8cfe3 303#if SO_MARK && USE_LIBCAP
425de4c8
AJ
304 mark = true;
305 // Assume preserve is true. We don't set at initialisation as this affects isHitNfmarkActive()
306#if USE_LIBNETFILTERCONNTRACK
307 preserveMissMark = true;
308# else // USE_LIBNETFILTERCONNTRACK
309 preserveMissMark = false;
310 debugs(3, DBG_IMPORTANT, "WARNING: Squid not compiled with Netfilter conntrack library. "
ab745b44 311 << "Netfilter mark preservation not available.");
425de4c8 312#endif // USE_LIBNETFILTERCONNTRACK
11e8cfe3 313#elif SO_MARK // SO_MARK && USE_LIBCAP
425de4c8 314 debugs(3, DBG_CRITICAL, "ERROR: Invalid parameter 'mark' in qos_flows option. "
11e8cfe3 315 << "Linux Netfilter marking not available without LIBCAP support.");
425de4c8 316 self_destruct();
11e8cfe3
AB
317#else // SO_MARK && USE_LIBCAP
318 debugs(3, DBG_CRITICAL, "ERROR: Invalid parameter 'mark' in qos_flows option. "
319 << "Linux Netfilter marking not available on this platform.");
320 self_destruct();
321#endif // SO_MARK && USE_LIBCAP
425de4c8
AJ
322 } else if (strncmp(token, "tos",3) == 0) {
323 preserveMissTos = true;
324 tos = true;
325 } else {
326 preserveMissTos = true;
327 tos = true;
328 }
329 }
330
af6a12ee 331 if (strncmp(token, "local-hit=",10) == 0) {
425de4c8
AJ
332
333 if (mark) {
334 if (!xstrtoui(&token[10], NULL, &markLocalHit, 0, std::numeric_limits<nfmark_t>::max())) {
ab745b44
A
335 debugs(3, DBG_CRITICAL, "ERROR: Bad mark local-hit value " << &token[10]);
336 self_destruct();
425de4c8
AJ
337 }
338 } else {
339 unsigned int v = 0;
340 if (!xstrtoui(&token[10], NULL, &v, 0, std::numeric_limits<tos_t>::max())) {
ab745b44
A
341 debugs(3, DBG_CRITICAL, "ERROR: Bad TOS local-hit value " << &token[10]);
342 self_destruct();
425de4c8
AJ
343 }
344 tosLocalHit = (tos_t)v;
345 }
346
af6a12ee 347 } else if (strncmp(token, "sibling-hit=",12) == 0) {
425de4c8
AJ
348
349 if (mark) {
350 if (!xstrtoui(&token[12], NULL, &markSiblingHit, 0, std::numeric_limits<nfmark_t>::max())) {
ab745b44
A
351 debugs(3, DBG_CRITICAL, "ERROR: Bad mark sibling-hit value " << &token[12]);
352 self_destruct();
425de4c8
AJ
353 }
354 } else {
355 unsigned int v = 0;
356 if (!xstrtoui(&token[12], NULL, &v, 0, std::numeric_limits<tos_t>::max())) {
ab745b44
A
357 debugs(3, DBG_CRITICAL, "ERROR: Bad TOS sibling-hit value " << &token[12]);
358 self_destruct();
425de4c8
AJ
359 }
360 tosSiblingHit = (tos_t)v;
361 }
362
af6a12ee 363 } else if (strncmp(token, "parent-hit=",11) == 0) {
425de4c8
AJ
364
365 if (mark) {
366 if (!xstrtoui(&token[11], NULL, &markParentHit, 0, std::numeric_limits<nfmark_t>::max())) {
ab745b44
A
367 debugs(3, DBG_CRITICAL, "ERROR: Bad mark parent-hit value " << &token[11]);
368 self_destruct();
425de4c8
AJ
369 }
370 } else {
371 unsigned int v = 0;
372 if (!xstrtoui(&token[11], NULL, &v, 0, std::numeric_limits<tos_t>::max())) {
ab745b44
A
373 debugs(3, DBG_CRITICAL, "ERROR: Bad TOS parent-hit value " << &token[11]);
374 self_destruct();
425de4c8
AJ
375 }
376 tosParentHit = (tos_t)v;
377 }
378
379 } else if (strncmp(token, "miss=",5) == 0) {
380
a29d2a95 381 char *end;
425de4c8 382 if (mark) {
a29d2a95 383 if (!xstrtoui(&token[5], &end, &markMiss, 0, std::numeric_limits<nfmark_t>::max())) {
ab745b44
A
384 debugs(3, DBG_CRITICAL, "ERROR: Bad mark miss value " << &token[5]);
385 self_destruct();
425de4c8 386 }
a29d2a95
AB
387 if (*end == '/') {
388 if (!xstrtoui(end + 1, NULL, &markMissMask, 0, std::numeric_limits<nfmark_t>::max())) {
389 debugs(3, DBG_CRITICAL, "ERROR: Bad mark miss mask value " << (end + 1) << ". Using 0xFFFFFFFF instead.");
390 markMissMask = 0xFFFFFFFF;
391 }
392 } else {
393 markMissMask = 0xFFFFFFFF;
394 }
425de4c8
AJ
395 } else {
396 unsigned int v = 0;
a29d2a95 397 if (!xstrtoui(&token[5], &end, &v, 0, std::numeric_limits<tos_t>::max())) {
ab745b44
A
398 debugs(3, DBG_CRITICAL, "ERROR: Bad TOS miss value " << &token[5]);
399 self_destruct();
425de4c8
AJ
400 }
401 tosMiss = (tos_t)v;
a29d2a95
AB
402 if (*end == '/') {
403 if (!xstrtoui(end + 1, NULL, &v, 0, std::numeric_limits<tos_t>::max())) {
404 debugs(3, DBG_CRITICAL, "ERROR: Bad TOS miss mask value " << (end + 1) << ". Using 0xFF instead.");
405 tosMissMask = 0xFF;
406 } else
407 tosMissMask = (tos_t)v;
408 } else {
409 tosMissMask = 0xFF;
410 }
425de4c8
AJ
411 }
412
af6a12ee 413 } else if (strcmp(token, "disable-preserve-miss") == 0) {
425de4c8
AJ
414
415 if (preserveMissTosMask!=0xFFU || preserveMissMarkMask!=0xFFFFFFFFU) {
416 debugs(3, DBG_CRITICAL, "ERROR: miss-mask feature cannot be set with disable-preserve-miss");
417 self_destruct();
418 }
419 if (mark) {
420 preserveMissMark = false;
421 preserveMissMarkMask = 0;
422 } else {
423 preserveMissTos = false;
424 preserveMissTosMask = 0;
425 }
426
427 } else if (strncmp(token, "miss-mask=",10) == 0) {
428
429 if (mark && preserveMissMark) {
430 if (!xstrtoui(&token[10], NULL, &preserveMissMarkMask, 0, std::numeric_limits<nfmark_t>::max())) {
ab745b44
A
431 debugs(3, DBG_CRITICAL, "ERROR: Bad mark miss-mark value " << &token[10]);
432 self_destruct();
425de4c8
AJ
433 }
434 } else if (preserveMissTos) {
435 unsigned int v = 0;
436 if (!xstrtoui(&token[10], NULL, &v, 0, std::numeric_limits<tos_t>::max())) {
ab745b44
A
437 debugs(3, DBG_CRITICAL, "ERROR: Bad TOS miss-mark value " << &token[10]);
438 self_destruct();
425de4c8
AJ
439 }
440 preserveMissTosMask = (tos_t)v;
441 } else {
442 debugs(3, DBG_CRITICAL, "ERROR: miss-mask feature cannot be set without miss-preservation enabled");
443 self_destruct();
444 }
445
575cb927
AJ
446 }
447 }
448}
449
dbe6f864
AJ
450/**
451 * NOTE: Due to the low-level nature of the library these
452 * objects are part of the dump function must be self-contained.
453 * which means no StoreEntry refrences. Just a basic char* buffer.
425de4c8 454*/
575cb927 455void
425de4c8 456Ip::Qos::Config::dumpConfigLine(char *entry, const char *name) const
575cb927 457{
dbe6f864 458 char *p = entry;
425de4c8 459 if (isHitTosActive()) {
575cb927 460
425de4c8
AJ
461 p += snprintf(p, 11, "%s", name); // strlen("qos_flows ");
462 p += snprintf(p, 4, "%s", "tos");
575cb927 463
425de4c8
AJ
464 if (tosLocalHit > 0) {
465 p += snprintf(p, 16, " local-hit=0x%02X", tosLocalHit);
466 }
467 if (tosSiblingHit > 0) {
468 p += snprintf(p, 18, " sibling-hit=0x%02X", tosSiblingHit);
469 }
470 if (tosParentHit > 0) {
471 p += snprintf(p, 17, " parent-hit=0x%02X", tosParentHit);
472 }
473 if (tosMiss > 0) {
474 p += snprintf(p, 11, " miss=0x%02X", tosMiss);
a29d2a95 475 if (tosMissMask!=0xFFU) {
d123935a 476 p += snprintf(p, 6, "/0x%02X", tosMissMask);
a29d2a95 477 }
425de4c8
AJ
478 }
479 if (preserveMissTos == 0) {
480 p += snprintf(p, 23, " disable-preserve-miss");
481 }
482 if (preserveMissTos && preserveMissTosMask != 0) {
483 p += snprintf(p, 16, " miss-mask=0x%02X", preserveMissTosMask);
484 }
485 p += snprintf(p, 2, "\n");
575cb927 486 }
b5523edc 487
425de4c8
AJ
488 if (isHitNfmarkActive()) {
489 p += snprintf(p, 11, "%s", name); // strlen("qos_flows ");
490 p += snprintf(p, 5, "%s", "mark");
491
492 if (markLocalHit > 0) {
493 p += snprintf(p, 22, " local-hit=0x%02X", markLocalHit);
494 }
495 if (markSiblingHit > 0) {
496 p += snprintf(p, 24, " sibling-hit=0x%02X", markSiblingHit);
497 }
498 if (markParentHit > 0) {
499 p += snprintf(p, 23, " parent-hit=0x%02X", markParentHit);
500 }
501 if (markMiss > 0) {
502 p += snprintf(p, 17, " miss=0x%02X", markMiss);
a29d2a95
AB
503 if (markMissMask!=0xFFFFFFFFU) {
504 p += snprintf(p, 12, "/0x%02X", markMissMask);
505 }
425de4c8
AJ
506 }
507 if (preserveMissMark == false) {
508 p += snprintf(p, 23, " disable-preserve-miss");
509 }
510 if (preserveMissMark && preserveMissMarkMask != 0) {
511 p += snprintf(p, 22, " miss-mask=0x%02X", preserveMissMarkMask);
512 }
513 p += snprintf(p, 2, "\n");
575cb927 514 }
575cb927
AJ
515}
516
912864c2
AJ
517int
518Ip::Qos::setSockTos(const int fd, tos_t tos, int type)
519{
520 // Bug 3731: FreeBSD produces 'invalid option'
521 // unless we pass it a 32-bit variable storing 8-bits of data.
522 // NP: it is documented as 'int' for all systems, even those like Linux which accept 8-bit char
523 // so we convert to a int before setting.
524 int bTos = tos;
525
526 debugs(50, 3, "for FD " << fd << " to " << bTos);
527
528 if (type == AF_INET) {
529#if defined(IP_TOS)
530 const int x = setsockopt(fd, IPPROTO_IP, IP_TOS, &bTos, sizeof(bTos));
531 if (x < 0) {
532 int xerrno = errno;
533 debugs(50, 2, "setsockopt(IP_TOS) on " << fd << ": " << xstrerr(xerrno));
534 }
535 return x;
536#else
537 debugs(50, DBG_IMPORTANT, "WARNING: setsockopt(IP_TOS) not supported on this platform");
538 return -1;
425de4c8 539#endif
912864c2
AJ
540 } else { // type == AF_INET6
541#if defined(IPV6_TCLASS)
542 const int x = setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &bTos, sizeof(bTos));
543 if (x < 0) {
544 int xerrno = errno;
545 debugs(50, 2, "setsockopt(IPV6_TCLASS) on " << fd << ": " << xstrerr(xerrno));
546 }
547 return x;
548#else
549 debugs(50, DBG_IMPORTANT, "WARNING: setsockopt(IPV6_TCLASS) not supported on this platform");
550 return -1;
551#endif
552 }
553
554 /* CANNOT REACH HERE */
555}
556
557int
558Ip::Qos::setSockTos(const Comm::ConnectionPointer &conn, tos_t tos)
559{
560 const int x = Ip::Qos::setSockTos(conn->fd, tos, conn->remote.isIPv4() ? AF_INET : AF_INET6);
561 conn->tos = (x >= 0) ? tos : 0;
562 return x;
563}
564
565int
566Ip::Qos::setSockNfmark(const int fd, nfmark_t mark)
567{
568#if SO_MARK && USE_LIBCAP
569 debugs(50, 3, "for FD " << fd << " to " << mark);
570 const int x = setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(nfmark_t));
571 if (x < 0) {
572 int xerrno = errno;
573 debugs(50, 2, "setsockopt(SO_MARK) on " << fd << ": " << xstrerr(xerrno));
574 }
575 return x;
576#elif USE_LIBCAP
577 debugs(50, DBG_IMPORTANT, "WARNING: setsockopt(SO_MARK) not supported on this platform");
578 return -1;
579#else
580 debugs(50, DBG_IMPORTANT, "WARNING: Netfilter marking disabled (netfilter marking requires build with LIBCAP)");
581 return -1;
582#endif
583}
584
585int
586Ip::Qos::setSockNfmark(const Comm::ConnectionPointer &conn, nfmark_t mark)
587{
588 const int x = Ip::Qos::setSockNfmark(conn->fd, mark);
589 conn->nfmark = (x >= 0) ? mark : 0;
590 return x;
591}
592
593bool
594Ip::Qos::Config::isAclNfmarkActive() const
595{
596 acl_nfmark * nfmarkAcls [] = { nfmarkToServer, nfmarkToClient };
597
598 for (int i=0; i<2; ++i) {
599 while (nfmarkAcls[i]) {
600 acl_nfmark *l = nfmarkAcls[i];
244da4ad 601 if (!l->markConfig.isEmpty())
912864c2
AJ
602 return true;
603 nfmarkAcls[i] = l->next;
604 }
605 }
606
607 return false;
608}
609
610bool
611Ip::Qos::Config::isAclTosActive() const
612{
613 acl_tos * tosAcls [] = { tosToServer, tosToClient };
614
615 for (int i=0; i<2; ++i) {
616 while (tosAcls[i]) {
617 acl_tos *l = tosAcls[i];
618 if (l->tos > 0)
619 return true;
620 tosAcls[i] = l->next;
621 }
622 }
623
624 return false;
625}
f53969cc 626