]>
Commit | Line | Data |
---|---|---|
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 |
24 | CBDATA_CLASS_INIT(acl_tos); |
25 | ||
26 | acl_tos::~acl_tos() | |
27 | { | |
28 | aclDestroyAclList(&aclList); | |
29 | delete next; | |
30 | } | |
31 | ||
32 | CBDATA_CLASS_INIT(acl_nfmark); | |
33 | ||
34 | acl_nfmark::~acl_nfmark() | |
35 | { | |
36 | aclDestroyAclList(&aclList); | |
37 | delete next; | |
38 | } | |
39 | ||
425de4c8 | 40 | void |
b5523edc | 41 | Ip::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 | */ | |
90 | static int | |
91 | getNfmarkCallback(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 | */ | |
103 | static nf_conntrack * | |
104 | prepareConntrackQuery(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 | ||
140 | nfmark_t | |
141 | Ip::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 |
172 | bool |
173 | Ip::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 | |
213 | int | |
b5523edc | 214 | Ip::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 | ||
234 | int | |
b5523edc | 235 | Ip::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 | ||
255 | int | |
b5523edc | 256 | Ip::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 | ||
262 | int | |
b5523edc | 263 | Ip::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 | ||
271 | Ip::Qos::Config Ip::Qos::TheConfig; | |
272 | ||
fad2588a | 273 | Ip::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 | ||
283 | void | |
425de4c8 | 284 | Ip::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 | 455 | void |
425de4c8 | 456 | Ip::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 |
517 | int |
518 | Ip::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 | ||
557 | int | |
558 | Ip::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 | ||
565 | int | |
566 | Ip::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 | ||
585 | int | |
586 | Ip::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 | ||
593 | bool | |
594 | Ip::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 | ||
610 | bool | |
611 | Ip::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 |