]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ip/QosConfig.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / ip / QosConfig.cc
1 /*
2 * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
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
9 #include "squid.h"
10
11 #include "acl/Gadgets.h"
12 #include "cache_cf.h"
13 #include "comm/Connection.h"
14 #include "compat/cmsg.h"
15 #include "ConfigParser.h"
16 #include "fde.h"
17 #include "globals.h"
18 #include "hier_code.h"
19 #include "ip/QosConfig.h"
20 #include "ip/tools.h"
21 #include "Parsing.h"
22
23 #include <cerrno>
24
25 CBDATA_CLASS_INIT(acl_tos);
26
27 acl_tos::~acl_tos()
28 {
29 aclDestroyAclList(&aclList);
30 delete next;
31 }
32
33 CBDATA_CLASS_INIT(acl_nfmark);
34
35 acl_nfmark::~acl_nfmark()
36 {
37 aclDestroyAclList(&aclList);
38 delete next;
39 }
40
41 void
42 Ip::Qos::getTosFromServer(const Comm::ConnectionPointer &server, fde *clientFde)
43 {
44 #if USE_QOS_TOS && _SQUID_LINUX_
45 /* Bug 2537: This part of ZPH only applies to patched Linux kernels. */
46 tos_t tos = 1;
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];
51 int len = 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
55 * patch.
56 */
57 unsigned char * pbuf = buf;
58 while (pbuf-buf < len) {
59 struct cmsghdr *o = (struct cmsghdr*)pbuf;
60 if (o->cmsg_len<=0)
61 break;
62
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;
66 break;
67 }
68 pbuf += CMSG_LEN(o->cmsg_len);
69 }
70 } else {
71 int xerrno = errno;
72 debugs(33, DBG_IMPORTANT, "QOS: error in getsockopt(IP_PKTOPTIONS) on " << server << " " << xstrerr(xerrno));
73 }
74 } else {
75 int xerrno = errno;
76 debugs(33, DBG_IMPORTANT, "QOS: error in setsockopt(IP_RECVTOS) on " << server << " " << xstrerr(xerrno));
77 }
78 #endif
79 }
80
81 void Ip::Qos::getNfmarkFromServer(const Comm::ConnectionPointer &server, const fde *clientFde)
82 {
83 #if USE_LIBNETFILTERCONNTRACK
84 /* Allocate a new conntrack */
85 if (struct nf_conntrack *ct = nfct_new()) {
86
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
89 * port numbers.
90 */
91
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);
100 } else {
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);
108 }
109
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()));
113
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);
120 if (x == -1) {
121 debugs(17, 2, "QOS: Failed to retrieve connection mark: (" << x << ") " << strerror(errno)
122 << " (Destination " << server->remote << ", source " << server->local << ")" );
123 }
124 nfct_close(h);
125 } else {
126 debugs(17, 2, "QOS: Failed to open conntrack handle for upstream netfilter mark retrieval.");
127 }
128 nfct_destroy(ct);
129
130 } else {
131 debugs(17, 2, "QOS: Failed to allocate new conntrack for upstream netfilter mark retrieval.");
132 }
133 #endif
134 }
135
136 #if USE_LIBNETFILTERCONNTRACK
137 int
138 Ip::Qos::getNfMarkCallback(enum nf_conntrack_msg_type,
139 struct nf_conntrack *ct,
140 void *data)
141 {
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);
145
146 return NFCT_CB_CONTINUE;
147 }
148 #endif
149
150 int
151 Ip::Qos::doTosLocalMiss(const Comm::ConnectionPointer &conn, const hier_code hierCode)
152 {
153 tos_t tos = 0;
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));
167 }
168 return setSockTos(conn, tos);
169 }
170
171 int
172 Ip::Qos::doNfmarkLocalMiss(const Comm::ConnectionPointer &conn, const hier_code hierCode)
173 {
174 nfmark_t mark = 0;
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);
188 }
189 return setSockNfmark(conn, mark);
190 }
191
192 int
193 Ip::Qos::doTosLocalHit(const Comm::ConnectionPointer &conn)
194 {
195 debugs(33, 2, "QOS: Setting TOS for local hit, TOS=" << int(Ip::Qos::TheConfig.tosLocalHit));
196 return setSockTos(conn, Ip::Qos::TheConfig.tosLocalHit);
197 }
198
199 int
200 Ip::Qos::doNfmarkLocalHit(const Comm::ConnectionPointer &conn)
201 {
202 debugs(33, 2, "QOS: Setting netfilter mark for local hit, mark=" << Ip::Qos::TheConfig.markLocalHit);
203 return setSockNfmark(conn, Ip::Qos::TheConfig.markLocalHit);
204 }
205
206 /* Qos::Config class */
207
208 Ip::Qos::Config Ip::Qos::TheConfig;
209
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),
216 nfmarkToClient(NULL)
217 {
218 }
219
220 void
221 Ip::Qos::Config::parseConfigLine()
222 {
223 /* parse options ... */
224 char *token;
225 /* These are set as appropriate and then used to check whether the initial loop has been done */
226 bool mark = false;
227 bool tos = false;
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 */
230 #if !USE_QOS_TOS
231 debugs(3, DBG_CRITICAL, "ERROR: Invalid option 'qos_flows'. QOS features not enabled in this build");
232 self_destruct();
233 #endif
234
235 while ( (token = ConfigParser::NextToken()) ) {
236
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
241 mark = true;
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.");
253 self_destruct();
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.");
257 self_destruct();
258 #endif // SO_MARK && USE_LIBCAP
259 } else if (strncmp(token, "tos",3) == 0) {
260 preserveMissTos = true;
261 tos = true;
262 } else {
263 preserveMissTos = true;
264 tos = true;
265 }
266 }
267
268 if (strncmp(token, "local-hit=",10) == 0) {
269
270 if (mark) {
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]);
273 self_destruct();
274 }
275 } else {
276 unsigned int v = 0;
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]);
279 self_destruct();
280 }
281 tosLocalHit = (tos_t)v;
282 }
283
284 } else if (strncmp(token, "sibling-hit=",12) == 0) {
285
286 if (mark) {
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]);
289 self_destruct();
290 }
291 } else {
292 unsigned int v = 0;
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]);
295 self_destruct();
296 }
297 tosSiblingHit = (tos_t)v;
298 }
299
300 } else if (strncmp(token, "parent-hit=",11) == 0) {
301
302 if (mark) {
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]);
305 self_destruct();
306 }
307 } else {
308 unsigned int v = 0;
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]);
311 self_destruct();
312 }
313 tosParentHit = (tos_t)v;
314 }
315
316 } else if (strncmp(token, "miss=",5) == 0) {
317
318 char *end;
319 if (mark) {
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]);
322 self_destruct();
323 }
324 if (*end == '/') {
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;
328 }
329 } else {
330 markMissMask = 0xFFFFFFFF;
331 }
332 } else {
333 unsigned int v = 0;
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]);
336 self_destruct();
337 }
338 tosMiss = (tos_t)v;
339 if (*end == '/') {
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.");
342 tosMissMask = 0xFF;
343 } else
344 tosMissMask = (tos_t)v;
345 } else {
346 tosMissMask = 0xFF;
347 }
348 }
349
350 } else if (strcmp(token, "disable-preserve-miss") == 0) {
351
352 if (preserveMissTosMask!=0xFFU || preserveMissMarkMask!=0xFFFFFFFFU) {
353 debugs(3, DBG_CRITICAL, "ERROR: miss-mask feature cannot be set with disable-preserve-miss");
354 self_destruct();
355 }
356 if (mark) {
357 preserveMissMark = false;
358 preserveMissMarkMask = 0;
359 } else {
360 preserveMissTos = false;
361 preserveMissTosMask = 0;
362 }
363
364 } else if (strncmp(token, "miss-mask=",10) == 0) {
365
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]);
369 self_destruct();
370 }
371 } else if (preserveMissTos) {
372 unsigned int v = 0;
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]);
375 self_destruct();
376 }
377 preserveMissTosMask = (tos_t)v;
378 } else {
379 debugs(3, DBG_CRITICAL, "ERROR: miss-mask feature cannot be set without miss-preservation enabled");
380 self_destruct();
381 }
382
383 }
384 }
385 }
386
387 /**
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.
391 */
392 void
393 Ip::Qos::Config::dumpConfigLine(char *entry, const char *name) const
394 {
395 char *p = entry;
396 if (isHitTosActive()) {
397
398 p += snprintf(p, 11, "%s", name); // strlen("qos_flows ");
399 p += snprintf(p, 4, "%s", "tos");
400
401 if (tosLocalHit > 0) {
402 p += snprintf(p, 16, " local-hit=0x%02X", tosLocalHit);
403 }
404 if (tosSiblingHit > 0) {
405 p += snprintf(p, 18, " sibling-hit=0x%02X", tosSiblingHit);
406 }
407 if (tosParentHit > 0) {
408 p += snprintf(p, 17, " parent-hit=0x%02X", tosParentHit);
409 }
410 if (tosMiss > 0) {
411 p += snprintf(p, 11, " miss=0x%02X", tosMiss);
412 if (tosMissMask!=0xFFU) {
413 p += snprintf(p, 6, "/0x%02X", tosMissMask);
414 }
415 }
416 if (preserveMissTos == 0) {
417 p += snprintf(p, 23, " disable-preserve-miss");
418 }
419 if (preserveMissTos && preserveMissTosMask != 0) {
420 p += snprintf(p, 16, " miss-mask=0x%02X", preserveMissTosMask);
421 }
422 p += snprintf(p, 2, "\n");
423 }
424
425 if (isHitNfmarkActive()) {
426 p += snprintf(p, 11, "%s", name); // strlen("qos_flows ");
427 p += snprintf(p, 5, "%s", "mark");
428
429 if (markLocalHit > 0) {
430 p += snprintf(p, 22, " local-hit=0x%02X", markLocalHit);
431 }
432 if (markSiblingHit > 0) {
433 p += snprintf(p, 24, " sibling-hit=0x%02X", markSiblingHit);
434 }
435 if (markParentHit > 0) {
436 p += snprintf(p, 23, " parent-hit=0x%02X", markParentHit);
437 }
438 if (markMiss > 0) {
439 p += snprintf(p, 17, " miss=0x%02X", markMiss);
440 if (markMissMask!=0xFFFFFFFFU) {
441 p += snprintf(p, 12, "/0x%02X", markMissMask);
442 }
443 }
444 if (preserveMissMark == false) {
445 p += snprintf(p, 23, " disable-preserve-miss");
446 }
447 if (preserveMissMark && preserveMissMarkMask != 0) {
448 p += snprintf(p, 22, " miss-mask=0x%02X", preserveMissMarkMask);
449 }
450 p += snprintf(p, 2, "\n");
451 }
452 }
453
454 #if !_USE_INLINE_
455 #include "Qos.cci"
456 #endif
457