]> git.ipfire.org Git - thirdparty/squid.git/blame - src/ip/QosConfig.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / ip / QosConfig.cc
CommitLineData
582c2af2 1#include "squid.h"
f4f6c2e0 2
425de4c8 3#include "acl/Gadgets.h"
8a01b99e 4#include "cache_cf.h"
b5523edc 5#include "comm/Connection.h"
425de4c8
AJ
6#include "ConfigParser.h"
7#include "fde.h"
582c2af2 8#include "globals.h"
425de4c8 9#include "hier_code.h"
f4f6c2e0 10#include "ip/QosConfig.h"
602d9612 11#include "ip/tools.h"
425de4c8 12#include "Parsing.h"
575cb927 13
21d845b1
FC
14#if HAVE_ERRNO_H
15#include <errno.h>
16#endif
17
425de4c8 18/* Qos namespace */
575cb927 19
425de4c8 20void
b5523edc 21Ip::Qos::getTosFromServer(const Comm::ConnectionPointer &server, fde *clientFde)
425de4c8 22{
f4f6c2e0
AJ
23#if USE_QOS_TOS && _SQUID_LINUX_
24 /* Bug 2537: This part of ZPH only applies to patched Linux kernels. */
425de4c8 25 tos_t tos = 1;
b5523edc 26 int tos_len = sizeof(tos);
425de4c8 27 clientFde->tosFromServer = 0;
b5523edc 28 if (setsockopt(server->fd,SOL_IP,IP_RECVTOS,&tos,tos_len)==0) {
425de4c8
AJ
29 unsigned char buf[512];
30 int len = 512;
b5523edc 31 if (getsockopt(server->fd,SOL_IP,IP_PKTOPTIONS,buf,(socklen_t*)&len) == 0) {
425de4c8
AJ
32 /* Parse the PKTOPTIONS structure to locate the TOS data message
33 * prepared in the kernel by the ZPH incoming TCP TOS preserving
34 * patch.
35 */
36 unsigned char * pbuf = buf;
37 while (pbuf-buf < len) {
38 struct cmsghdr *o = (struct cmsghdr*)pbuf;
39 if (o->cmsg_len<=0)
40 break;
575cb927 41
425de4c8
AJ
42 if (o->cmsg_level == SOL_IP && o->cmsg_type == IP_TOS) {
43 int *tmp = (int*)CMSG_DATA(o);
44 clientFde->tosFromServer = (tos_t)*tmp;
45 break;
46 }
47 pbuf += CMSG_LEN(o->cmsg_len);
48 }
49 } else {
b5523edc 50 debugs(33, DBG_IMPORTANT, "QOS: error in getsockopt(IP_PKTOPTIONS) on " << server << " " << xstrerror());
425de4c8
AJ
51 }
52 } else {
b5523edc 53 debugs(33, DBG_IMPORTANT, "QOS: error in setsockopt(IP_RECVTOS) on " << server << " " << xstrerror());
425de4c8
AJ
54 }
55#endif
56}
b7ac5457 57
b5523edc 58void Ip::Qos::getNfmarkFromServer(const Comm::ConnectionPointer &server, const fde *clientFde)
575cb927 59{
425de4c8
AJ
60#if USE_LIBNETFILTERCONNTRACK
61 /* Allocate a new conntrack */
62 if (struct nf_conntrack *ct = nfct_new()) {
63
64 /* Prepare data needed to find the connection in the conntrack table.
65 * We need the local and remote IP address, and the local and remote
66 * port numbers.
67 */
68
4dd643d5 69 if (Ip::EnableIpv6 && server->local.isIPv6()) {
425de4c8
AJ
70 nfct_set_attr_u8(ct, ATTR_L3PROTO, AF_INET6);
71 struct in6_addr serv_fde_remote_ip6;
4dd643d5 72 server->remote.getInAddr(serv_fde_remote_ip6);
425de4c8 73 nfct_set_attr(ct, ATTR_IPV6_DST, serv_fde_remote_ip6.s6_addr);
b5523edc 74 struct in6_addr serv_fde_local_ip6;
4dd643d5 75 server->local.getInAddr(serv_fde_local_ip6);
b5523edc 76 nfct_set_attr(ct, ATTR_IPV6_SRC, serv_fde_local_ip6.s6_addr);
425de4c8 77 } else {
425de4c8 78 nfct_set_attr_u8(ct, ATTR_L3PROTO, AF_INET);
8bbb16e3 79 struct in_addr serv_fde_remote_ip;
4dd643d5 80 server->remote.getInAddr(serv_fde_remote_ip);
b5523edc
AJ
81 nfct_set_attr_u32(ct, ATTR_IPV4_DST, serv_fde_remote_ip.s_addr);
82 struct in_addr serv_fde_local_ip;
4dd643d5 83 server->local.getInAddr(serv_fde_local_ip);
b5523edc 84 nfct_set_attr_u32(ct, ATTR_IPV4_SRC, serv_fde_local_ip.s_addr);
425de4c8
AJ
85 }
86
87 nfct_set_attr_u8(ct, ATTR_L4PROTO, IPPROTO_TCP);
4dd643d5
AJ
88 nfct_set_attr_u16(ct, ATTR_PORT_DST, htons(server->remote.port()));
89 nfct_set_attr_u16(ct, ATTR_PORT_SRC, htons(server->local.port()));
425de4c8
AJ
90
91 /* Open a handle to the conntrack */
92 if (struct nfct_handle *h = nfct_open(CONNTRACK, 0)) {
93 /* Register the callback. The callback function will record the mark value. */
b5523edc 94 nfct_callback_register(h, NFCT_T_ALL, getNfMarkCallback, (void *)clientFde);
425de4c8
AJ
95 /* Query the conntrack table using the data previously set */
96 int x = nfct_query(h, NFCT_Q_GET, ct);
97 if (x == -1) {
98 debugs(17, 2, "QOS: Failed to retrieve connection mark: (" << x << ") " << strerror(errno)
b5523edc 99 << " (Destination " << server->remote << ", source " << server->local << ")" );
425de4c8 100 }
425de4c8
AJ
101 nfct_close(h);
102 } else {
103 debugs(17, 2, "QOS: Failed to open conntrack handle for upstream netfilter mark retrieval.");
104 }
425de4c8
AJ
105 nfct_destroy(ct);
106
107 } else {
108 debugs(17, 2, "QOS: Failed to allocate new conntrack for upstream netfilter mark retrieval.");
109 }
110#endif
111}
112
113#if USE_LIBNETFILTERCONNTRACK
114int
115Ip::Qos::getNfMarkCallback(enum nf_conntrack_msg_type type,
ab745b44
A
116 struct nf_conntrack *ct,
117 void *data)
425de4c8 118{
ab745b44
A
119 fde *clientFde = (fde *)data;
120 clientFde->nfmarkFromServer = nfct_get_attr_u32(ct, ATTR_MARK);
121 debugs(17, 3, "QOS: Retrieved connection mark value: " << clientFde->nfmarkFromServer);
425de4c8 122
ab745b44 123 return NFCT_CB_CONTINUE;
425de4c8
AJ
124}
125#endif
126
127int
b5523edc 128Ip::Qos::doTosLocalMiss(const Comm::ConnectionPointer &conn, const hier_code hierCode)
425de4c8
AJ
129{
130 tos_t tos = 0;
131 if (Ip::Qos::TheConfig.tosSiblingHit && hierCode==SIBLING_HIT) {
132 tos = Ip::Qos::TheConfig.tosSiblingHit;
133 debugs(33, 2, "QOS: Sibling Peer hit with hier code=" << hierCode << ", TOS=" << int(tos));
134 } else if (Ip::Qos::TheConfig.tosParentHit && hierCode==PARENT_HIT) {
135 tos = Ip::Qos::TheConfig.tosParentHit;
136 debugs(33, 2, "QOS: Parent Peer hit with hier code=" << hierCode << ", TOS=" << int(tos));
a29d2a95 137 } else if (Ip::Qos::TheConfig.preserveMissTos) {
b5523edc 138 tos = fd_table[conn->fd].tosFromServer & Ip::Qos::TheConfig.preserveMissTosMask;
a29d2a95 139 tos = (tos & ~Ip::Qos::TheConfig.tosMissMask) | (Ip::Qos::TheConfig.tosMiss & Ip::Qos::TheConfig.tosMissMask);
425de4c8 140 debugs(33, 2, "QOS: Preserving TOS on miss, TOS=" << int(tos));
a29d2a95
AB
141 } else if (Ip::Qos::TheConfig.tosMiss) {
142 tos = Ip::Qos::TheConfig.tosMiss & Ip::Qos::TheConfig.tosMissMask;
143 debugs(33, 2, "QOS: Cache miss, setting TOS=" << int(tos));
425de4c8 144 }
b5523edc 145 return setSockTos(conn, tos);
425de4c8
AJ
146}
147
148int
b5523edc 149Ip::Qos::doNfmarkLocalMiss(const Comm::ConnectionPointer &conn, const hier_code hierCode)
425de4c8
AJ
150{
151 nfmark_t mark = 0;
152 if (Ip::Qos::TheConfig.markSiblingHit && hierCode==SIBLING_HIT) {
153 mark = Ip::Qos::TheConfig.markSiblingHit;
154 debugs(33, 2, "QOS: Sibling Peer hit with hier code=" << hierCode << ", Mark=" << mark);
155 } else if (Ip::Qos::TheConfig.markParentHit && hierCode==PARENT_HIT) {
156 mark = Ip::Qos::TheConfig.markParentHit;
157 debugs(33, 2, "QOS: Parent Peer hit with hier code=" << hierCode << ", Mark=" << mark);
425de4c8 158 } else if (Ip::Qos::TheConfig.preserveMissMark) {
b5523edc 159 mark = fd_table[conn->fd].nfmarkFromServer & Ip::Qos::TheConfig.preserveMissMarkMask;
a29d2a95 160 mark = (mark & ~Ip::Qos::TheConfig.markMissMask) | (Ip::Qos::TheConfig.markMiss & Ip::Qos::TheConfig.markMissMask);
425de4c8 161 debugs(33, 2, "QOS: Preserving mark on miss, Mark=" << mark);
a29d2a95
AB
162 } else if (Ip::Qos::TheConfig.markMiss) {
163 mark = Ip::Qos::TheConfig.markMiss & Ip::Qos::TheConfig.markMissMask;
164 debugs(33, 2, "QOS: Cache miss, setting Mark=" << mark);
425de4c8 165 }
b5523edc 166 return setSockNfmark(conn, mark);
425de4c8
AJ
167}
168
169int
b5523edc 170Ip::Qos::doTosLocalHit(const Comm::ConnectionPointer &conn)
425de4c8
AJ
171{
172 debugs(33, 2, "QOS: Setting TOS for local hit, TOS=" << int(Ip::Qos::TheConfig.tosLocalHit));
b5523edc 173 return setSockTos(conn, Ip::Qos::TheConfig.tosLocalHit);
425de4c8
AJ
174}
175
176int
b5523edc 177Ip::Qos::doNfmarkLocalHit(const Comm::ConnectionPointer &conn)
425de4c8
AJ
178{
179 debugs(33, 2, "QOS: Setting netfilter mark for local hit, mark=" << Ip::Qos::TheConfig.markLocalHit);
b5523edc 180 return setSockNfmark(conn, Ip::Qos::TheConfig.markLocalHit);
425de4c8
AJ
181}
182
183/* Qos::Config class */
184
185Ip::Qos::Config Ip::Qos::TheConfig;
186
fad2588a 187Ip::Qos::Config::Config() : tosLocalHit(0), tosSiblingHit(0), tosParentHit(0),
f53156c2
A
188 tosMiss(0), tosMissMask(0), preserveMissTos(false),
189 preserveMissTosMask(0xFF), markLocalHit(0), markSiblingHit(0),
190 markParentHit(0), markMiss(0), markMissMask(0),
191 preserveMissMark(false), preserveMissMarkMask(0xFFFFFFFF),
192 tosToServer(NULL), tosToClient(NULL), nfmarkToServer(NULL),
193 nfmarkToClient(NULL)
425de4c8 194{
575cb927
AJ
195}
196
197void
425de4c8 198Ip::Qos::Config::parseConfigLine()
575cb927 199{
575cb927
AJ
200 /* parse options ... */
201 char *token;
425de4c8
AJ
202 /* These are set as appropriate and then used to check whether the initial loop has been done */
203 bool mark = false;
204 bool tos = false;
205 /* Assume preserve is true. We don't set at initialisation as this affects isHitTosActive().
206 We have to do this now, as we may never match the 'tos' parameter below */
207#if !USE_QOS_TOS
208 debugs(3, DBG_CRITICAL, "ERROR: Invalid option 'qos_flows'. QOS features not enabled in this build");
209 self_destruct();
210#endif
211
2eceb328 212 while ( (token = ConfigParser::NextToken()) ) {
575cb927 213
425de4c8
AJ
214 // Work out TOS or mark. Default to TOS for backwards compatibility
215 if (!(mark || tos)) {
216 if (strncmp(token, "mark",4) == 0) {
11e8cfe3 217#if SO_MARK && USE_LIBCAP
425de4c8
AJ
218 mark = true;
219 // Assume preserve is true. We don't set at initialisation as this affects isHitNfmarkActive()
220#if USE_LIBNETFILTERCONNTRACK
221 preserveMissMark = true;
222# else // USE_LIBNETFILTERCONNTRACK
223 preserveMissMark = false;
224 debugs(3, DBG_IMPORTANT, "WARNING: Squid not compiled with Netfilter conntrack library. "
ab745b44 225 << "Netfilter mark preservation not available.");
425de4c8 226#endif // USE_LIBNETFILTERCONNTRACK
11e8cfe3 227#elif SO_MARK // SO_MARK && USE_LIBCAP
425de4c8 228 debugs(3, DBG_CRITICAL, "ERROR: Invalid parameter 'mark' in qos_flows option. "
11e8cfe3 229 << "Linux Netfilter marking not available without LIBCAP support.");
425de4c8 230 self_destruct();
11e8cfe3
AB
231#else // SO_MARK && USE_LIBCAP
232 debugs(3, DBG_CRITICAL, "ERROR: Invalid parameter 'mark' in qos_flows option. "
233 << "Linux Netfilter marking not available on this platform.");
234 self_destruct();
235#endif // SO_MARK && USE_LIBCAP
425de4c8
AJ
236 } else if (strncmp(token, "tos",3) == 0) {
237 preserveMissTos = true;
238 tos = true;
239 } else {
240 preserveMissTos = true;
241 tos = true;
242 }
243 }
244
af6a12ee 245 if (strncmp(token, "local-hit=",10) == 0) {
425de4c8
AJ
246
247 if (mark) {
248 if (!xstrtoui(&token[10], NULL, &markLocalHit, 0, std::numeric_limits<nfmark_t>::max())) {
ab745b44
A
249 debugs(3, DBG_CRITICAL, "ERROR: Bad mark local-hit value " << &token[10]);
250 self_destruct();
425de4c8
AJ
251 }
252 } else {
253 unsigned int v = 0;
254 if (!xstrtoui(&token[10], NULL, &v, 0, std::numeric_limits<tos_t>::max())) {
ab745b44
A
255 debugs(3, DBG_CRITICAL, "ERROR: Bad TOS local-hit value " << &token[10]);
256 self_destruct();
425de4c8
AJ
257 }
258 tosLocalHit = (tos_t)v;
259 }
260
af6a12ee 261 } else if (strncmp(token, "sibling-hit=",12) == 0) {
425de4c8
AJ
262
263 if (mark) {
264 if (!xstrtoui(&token[12], NULL, &markSiblingHit, 0, std::numeric_limits<nfmark_t>::max())) {
ab745b44
A
265 debugs(3, DBG_CRITICAL, "ERROR: Bad mark sibling-hit value " << &token[12]);
266 self_destruct();
425de4c8
AJ
267 }
268 } else {
269 unsigned int v = 0;
270 if (!xstrtoui(&token[12], NULL, &v, 0, std::numeric_limits<tos_t>::max())) {
ab745b44
A
271 debugs(3, DBG_CRITICAL, "ERROR: Bad TOS sibling-hit value " << &token[12]);
272 self_destruct();
425de4c8
AJ
273 }
274 tosSiblingHit = (tos_t)v;
275 }
276
af6a12ee 277 } else if (strncmp(token, "parent-hit=",11) == 0) {
425de4c8
AJ
278
279 if (mark) {
280 if (!xstrtoui(&token[11], NULL, &markParentHit, 0, std::numeric_limits<nfmark_t>::max())) {
ab745b44
A
281 debugs(3, DBG_CRITICAL, "ERROR: Bad mark parent-hit value " << &token[11]);
282 self_destruct();
425de4c8
AJ
283 }
284 } else {
285 unsigned int v = 0;
286 if (!xstrtoui(&token[11], NULL, &v, 0, std::numeric_limits<tos_t>::max())) {
ab745b44
A
287 debugs(3, DBG_CRITICAL, "ERROR: Bad TOS parent-hit value " << &token[11]);
288 self_destruct();
425de4c8
AJ
289 }
290 tosParentHit = (tos_t)v;
291 }
292
293 } else if (strncmp(token, "miss=",5) == 0) {
294
a29d2a95 295 char *end;
425de4c8 296 if (mark) {
a29d2a95 297 if (!xstrtoui(&token[5], &end, &markMiss, 0, std::numeric_limits<nfmark_t>::max())) {
ab745b44
A
298 debugs(3, DBG_CRITICAL, "ERROR: Bad mark miss value " << &token[5]);
299 self_destruct();
425de4c8 300 }
a29d2a95
AB
301 if (*end == '/') {
302 if (!xstrtoui(end + 1, NULL, &markMissMask, 0, std::numeric_limits<nfmark_t>::max())) {
303 debugs(3, DBG_CRITICAL, "ERROR: Bad mark miss mask value " << (end + 1) << ". Using 0xFFFFFFFF instead.");
304 markMissMask = 0xFFFFFFFF;
305 }
306 } else {
307 markMissMask = 0xFFFFFFFF;
308 }
425de4c8
AJ
309 } else {
310 unsigned int v = 0;
a29d2a95 311 if (!xstrtoui(&token[5], &end, &v, 0, std::numeric_limits<tos_t>::max())) {
ab745b44
A
312 debugs(3, DBG_CRITICAL, "ERROR: Bad TOS miss value " << &token[5]);
313 self_destruct();
425de4c8
AJ
314 }
315 tosMiss = (tos_t)v;
a29d2a95
AB
316 if (*end == '/') {
317 if (!xstrtoui(end + 1, NULL, &v, 0, std::numeric_limits<tos_t>::max())) {
318 debugs(3, DBG_CRITICAL, "ERROR: Bad TOS miss mask value " << (end + 1) << ". Using 0xFF instead.");
319 tosMissMask = 0xFF;
320 } else
321 tosMissMask = (tos_t)v;
322 } else {
323 tosMissMask = 0xFF;
324 }
425de4c8
AJ
325 }
326
af6a12ee 327 } else if (strcmp(token, "disable-preserve-miss") == 0) {
425de4c8
AJ
328
329 if (preserveMissTosMask!=0xFFU || preserveMissMarkMask!=0xFFFFFFFFU) {
330 debugs(3, DBG_CRITICAL, "ERROR: miss-mask feature cannot be set with disable-preserve-miss");
331 self_destruct();
332 }
333 if (mark) {
334 preserveMissMark = false;
335 preserveMissMarkMask = 0;
336 } else {
337 preserveMissTos = false;
338 preserveMissTosMask = 0;
339 }
340
341 } else if (strncmp(token, "miss-mask=",10) == 0) {
342
343 if (mark && preserveMissMark) {
344 if (!xstrtoui(&token[10], NULL, &preserveMissMarkMask, 0, std::numeric_limits<nfmark_t>::max())) {
ab745b44
A
345 debugs(3, DBG_CRITICAL, "ERROR: Bad mark miss-mark value " << &token[10]);
346 self_destruct();
425de4c8
AJ
347 }
348 } else if (preserveMissTos) {
349 unsigned int v = 0;
350 if (!xstrtoui(&token[10], NULL, &v, 0, std::numeric_limits<tos_t>::max())) {
ab745b44
A
351 debugs(3, DBG_CRITICAL, "ERROR: Bad TOS miss-mark value " << &token[10]);
352 self_destruct();
425de4c8
AJ
353 }
354 preserveMissTosMask = (tos_t)v;
355 } else {
356 debugs(3, DBG_CRITICAL, "ERROR: miss-mask feature cannot be set without miss-preservation enabled");
357 self_destruct();
358 }
359
575cb927
AJ
360 }
361 }
362}
363
dbe6f864
AJ
364/**
365 * NOTE: Due to the low-level nature of the library these
366 * objects are part of the dump function must be self-contained.
367 * which means no StoreEntry refrences. Just a basic char* buffer.
425de4c8 368*/
575cb927 369void
425de4c8 370Ip::Qos::Config::dumpConfigLine(char *entry, const char *name) const
575cb927 371{
dbe6f864 372 char *p = entry;
425de4c8 373 if (isHitTosActive()) {
575cb927 374
425de4c8
AJ
375 p += snprintf(p, 11, "%s", name); // strlen("qos_flows ");
376 p += snprintf(p, 4, "%s", "tos");
575cb927 377
425de4c8
AJ
378 if (tosLocalHit > 0) {
379 p += snprintf(p, 16, " local-hit=0x%02X", tosLocalHit);
380 }
381 if (tosSiblingHit > 0) {
382 p += snprintf(p, 18, " sibling-hit=0x%02X", tosSiblingHit);
383 }
384 if (tosParentHit > 0) {
385 p += snprintf(p, 17, " parent-hit=0x%02X", tosParentHit);
386 }
387 if (tosMiss > 0) {
388 p += snprintf(p, 11, " miss=0x%02X", tosMiss);
a29d2a95
AB
389 if (tosMissMask!=0xFFU) {
390 p += snprintf(p, 6, "/0x%02X", markMissMask);
391 }
425de4c8
AJ
392 }
393 if (preserveMissTos == 0) {
394 p += snprintf(p, 23, " disable-preserve-miss");
395 }
396 if (preserveMissTos && preserveMissTosMask != 0) {
397 p += snprintf(p, 16, " miss-mask=0x%02X", preserveMissTosMask);
398 }
399 p += snprintf(p, 2, "\n");
575cb927 400 }
b5523edc 401
425de4c8
AJ
402 if (isHitNfmarkActive()) {
403 p += snprintf(p, 11, "%s", name); // strlen("qos_flows ");
404 p += snprintf(p, 5, "%s", "mark");
405
406 if (markLocalHit > 0) {
407 p += snprintf(p, 22, " local-hit=0x%02X", markLocalHit);
408 }
409 if (markSiblingHit > 0) {
410 p += snprintf(p, 24, " sibling-hit=0x%02X", markSiblingHit);
411 }
412 if (markParentHit > 0) {
413 p += snprintf(p, 23, " parent-hit=0x%02X", markParentHit);
414 }
415 if (markMiss > 0) {
416 p += snprintf(p, 17, " miss=0x%02X", markMiss);
a29d2a95
AB
417 if (markMissMask!=0xFFFFFFFFU) {
418 p += snprintf(p, 12, "/0x%02X", markMissMask);
419 }
425de4c8
AJ
420 }
421 if (preserveMissMark == false) {
422 p += snprintf(p, 23, " disable-preserve-miss");
423 }
424 if (preserveMissMark && preserveMissMarkMask != 0) {
425 p += snprintf(p, 22, " miss-mask=0x%02X", preserveMissMarkMask);
426 }
427 p += snprintf(p, 2, "\n");
575cb927 428 }
575cb927
AJ
429}
430
425de4c8
AJ
431#if !_USE_INLINE_
432#include "Qos.cci"
433#endif