3 #include "acl/Gadgets.h"
5 #include "comm/Connection.h"
6 #include "ConfigParser.h"
10 #include "ip/QosConfig.h"
21 Ip::Qos::getTosFromServer(const Comm::ConnectionPointer
&server
, fde
*clientFde
)
23 #if USE_QOS_TOS && _SQUID_LINUX_
24 /* Bug 2537: This part of ZPH only applies to patched Linux kernels. */
26 int tos_len
= sizeof(tos
);
27 clientFde
->tosFromServer
= 0;
28 if (setsockopt(server
->fd
,SOL_IP
,IP_RECVTOS
,&tos
,tos_len
)==0) {
29 unsigned char buf
[512];
31 if (getsockopt(server
->fd
,SOL_IP
,IP_PKTOPTIONS
,buf
,(socklen_t
*)&len
) == 0) {
32 /* Parse the PKTOPTIONS structure to locate the TOS data message
33 * prepared in the kernel by the ZPH incoming TCP TOS preserving
36 unsigned char * pbuf
= buf
;
37 while (pbuf
-buf
< len
) {
38 struct cmsghdr
*o
= (struct cmsghdr
*)pbuf
;
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
;
47 pbuf
+= CMSG_LEN(o
->cmsg_len
);
50 debugs(33, DBG_IMPORTANT
, "QOS: error in getsockopt(IP_PKTOPTIONS) on " << server
<< " " << xstrerror());
53 debugs(33, DBG_IMPORTANT
, "QOS: error in setsockopt(IP_RECVTOS) on " << server
<< " " << xstrerror());
58 void Ip::Qos::getNfmarkFromServer(const Comm::ConnectionPointer
&server
, const fde
*clientFde
)
60 #if USE_LIBNETFILTERCONNTRACK
61 /* Allocate a new conntrack */
62 if (struct nf_conntrack
*ct
= nfct_new()) {
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
69 if (Ip::EnableIpv6
&& server
->local
.isIPv6()) {
70 nfct_set_attr_u8(ct
, ATTR_L3PROTO
, AF_INET6
);
71 struct in6_addr serv_fde_remote_ip6
;
72 server
->remote
.getInAddr(serv_fde_remote_ip6
);
73 nfct_set_attr(ct
, ATTR_IPV6_DST
, serv_fde_remote_ip6
.s6_addr
);
74 struct in6_addr serv_fde_local_ip6
;
75 server
->local
.getInAddr(serv_fde_local_ip6
);
76 nfct_set_attr(ct
, ATTR_IPV6_SRC
, serv_fde_local_ip6
.s6_addr
);
78 nfct_set_attr_u8(ct
, ATTR_L3PROTO
, AF_INET
);
79 struct in_addr serv_fde_remote_ip
;
80 server
->remote
.getInAddr(serv_fde_remote_ip
);
81 nfct_set_attr_u32(ct
, ATTR_IPV4_DST
, serv_fde_remote_ip
.s_addr
);
82 struct in_addr serv_fde_local_ip
;
83 server
->local
.getInAddr(serv_fde_local_ip
);
84 nfct_set_attr_u32(ct
, ATTR_IPV4_SRC
, serv_fde_local_ip
.s_addr
);
87 nfct_set_attr_u8(ct
, ATTR_L4PROTO
, IPPROTO_TCP
);
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()));
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. */
94 nfct_callback_register(h
, NFCT_T_ALL
, getNfMarkCallback
, (void *)clientFde
);
95 /* Query the conntrack table using the data previously set */
96 int x
= nfct_query(h
, NFCT_Q_GET
, ct
);
98 debugs(17, 2, "QOS: Failed to retrieve connection mark: (" << x
<< ") " << strerror(errno
)
99 << " (Destination " << server
->remote
<< ", source " << server
->local
<< ")" );
103 debugs(17, 2, "QOS: Failed to open conntrack handle for upstream netfilter mark retrieval.");
108 debugs(17, 2, "QOS: Failed to allocate new conntrack for upstream netfilter mark retrieval.");
113 #if USE_LIBNETFILTERCONNTRACK
115 Ip::Qos::getNfMarkCallback(enum nf_conntrack_msg_type type
,
116 struct nf_conntrack
*ct
,
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
);
123 return NFCT_CB_CONTINUE
;
128 Ip::Qos::doTosLocalMiss(const Comm::ConnectionPointer
&conn
, const hier_code hierCode
)
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
));
137 } else if (Ip::Qos::TheConfig
.preserveMissTos
) {
138 tos
= fd_table
[conn
->fd
].tosFromServer
& Ip::Qos::TheConfig
.preserveMissTosMask
;
139 tos
= (tos
& ~Ip::Qos::TheConfig
.tosMissMask
) | (Ip::Qos::TheConfig
.tosMiss
& Ip::Qos::TheConfig
.tosMissMask
);
140 debugs(33, 2, "QOS: Preserving TOS on miss, TOS=" << int(tos
));
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
));
145 return setSockTos(conn
, tos
);
149 Ip::Qos::doNfmarkLocalMiss(const Comm::ConnectionPointer
&conn
, const hier_code hierCode
)
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
);
158 } else if (Ip::Qos::TheConfig
.preserveMissMark
) {
159 mark
= fd_table
[conn
->fd
].nfmarkFromServer
& Ip::Qos::TheConfig
.preserveMissMarkMask
;
160 mark
= (mark
& ~Ip::Qos::TheConfig
.markMissMask
) | (Ip::Qos::TheConfig
.markMiss
& Ip::Qos::TheConfig
.markMissMask
);
161 debugs(33, 2, "QOS: Preserving mark on miss, Mark=" << mark
);
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
);
166 return setSockNfmark(conn
, mark
);
170 Ip::Qos::doTosLocalHit(const Comm::ConnectionPointer
&conn
)
172 debugs(33, 2, "QOS: Setting TOS for local hit, TOS=" << int(Ip::Qos::TheConfig
.tosLocalHit
));
173 return setSockTos(conn
, Ip::Qos::TheConfig
.tosLocalHit
);
177 Ip::Qos::doNfmarkLocalHit(const Comm::ConnectionPointer
&conn
)
179 debugs(33, 2, "QOS: Setting netfilter mark for local hit, mark=" << Ip::Qos::TheConfig
.markLocalHit
);
180 return setSockNfmark(conn
, Ip::Qos::TheConfig
.markLocalHit
);
183 /* Qos::Config class */
185 Ip::Qos::Config
Ip::Qos::TheConfig
;
187 Ip::Qos::Config::Config() : tosLocalHit(0), tosSiblingHit(0), tosParentHit(0),
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
),
198 Ip::Qos::Config::parseConfigLine()
200 /* parse options ... */
202 /* These are set as appropriate and then used to check whether the initial loop has been done */
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 */
208 debugs(3, DBG_CRITICAL
, "ERROR: Invalid option 'qos_flows'. QOS features not enabled in this build");
212 while ( (token
= ConfigParser::NextToken()) ) {
214 // Work out TOS or mark. Default to TOS for backwards compatibility
215 if (!(mark
|| tos
)) {
216 if (strncmp(token
, "mark",4) == 0) {
217 #if SO_MARK && USE_LIBCAP
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. "
225 << "Netfilter mark preservation not available.");
226 #endif // USE_LIBNETFILTERCONNTRACK
227 #elif SO_MARK // SO_MARK && USE_LIBCAP
228 debugs(3, DBG_CRITICAL
, "ERROR: Invalid parameter 'mark' in qos_flows option. "
229 << "Linux Netfilter marking not available without LIBCAP support.");
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.");
235 #endif // SO_MARK && USE_LIBCAP
236 } else if (strncmp(token
, "tos",3) == 0) {
237 preserveMissTos
= true;
240 preserveMissTos
= true;
245 if (strncmp(token
, "local-hit=",10) == 0) {
248 if (!xstrtoui(&token
[10], NULL
, &markLocalHit
, 0, std::numeric_limits
<nfmark_t
>::max())) {
249 debugs(3, DBG_CRITICAL
, "ERROR: Bad mark local-hit value " << &token
[10]);
254 if (!xstrtoui(&token
[10], NULL
, &v
, 0, std::numeric_limits
<tos_t
>::max())) {
255 debugs(3, DBG_CRITICAL
, "ERROR: Bad TOS local-hit value " << &token
[10]);
258 tosLocalHit
= (tos_t
)v
;
261 } else if (strncmp(token
, "sibling-hit=",12) == 0) {
264 if (!xstrtoui(&token
[12], NULL
, &markSiblingHit
, 0, std::numeric_limits
<nfmark_t
>::max())) {
265 debugs(3, DBG_CRITICAL
, "ERROR: Bad mark sibling-hit value " << &token
[12]);
270 if (!xstrtoui(&token
[12], NULL
, &v
, 0, std::numeric_limits
<tos_t
>::max())) {
271 debugs(3, DBG_CRITICAL
, "ERROR: Bad TOS sibling-hit value " << &token
[12]);
274 tosSiblingHit
= (tos_t
)v
;
277 } else if (strncmp(token
, "parent-hit=",11) == 0) {
280 if (!xstrtoui(&token
[11], NULL
, &markParentHit
, 0, std::numeric_limits
<nfmark_t
>::max())) {
281 debugs(3, DBG_CRITICAL
, "ERROR: Bad mark parent-hit value " << &token
[11]);
286 if (!xstrtoui(&token
[11], NULL
, &v
, 0, std::numeric_limits
<tos_t
>::max())) {
287 debugs(3, DBG_CRITICAL
, "ERROR: Bad TOS parent-hit value " << &token
[11]);
290 tosParentHit
= (tos_t
)v
;
293 } else if (strncmp(token
, "miss=",5) == 0) {
297 if (!xstrtoui(&token
[5], &end
, &markMiss
, 0, std::numeric_limits
<nfmark_t
>::max())) {
298 debugs(3, DBG_CRITICAL
, "ERROR: Bad mark miss value " << &token
[5]);
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;
307 markMissMask
= 0xFFFFFFFF;
311 if (!xstrtoui(&token
[5], &end
, &v
, 0, std::numeric_limits
<tos_t
>::max())) {
312 debugs(3, DBG_CRITICAL
, "ERROR: Bad TOS miss value " << &token
[5]);
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.");
321 tosMissMask
= (tos_t
)v
;
327 } else if (strcmp(token
, "disable-preserve-miss") == 0) {
329 if (preserveMissTosMask
!=0xFFU
|| preserveMissMarkMask
!=0xFFFFFFFFU
) {
330 debugs(3, DBG_CRITICAL
, "ERROR: miss-mask feature cannot be set with disable-preserve-miss");
334 preserveMissMark
= false;
335 preserveMissMarkMask
= 0;
337 preserveMissTos
= false;
338 preserveMissTosMask
= 0;
341 } else if (strncmp(token
, "miss-mask=",10) == 0) {
343 if (mark
&& preserveMissMark
) {
344 if (!xstrtoui(&token
[10], NULL
, &preserveMissMarkMask
, 0, std::numeric_limits
<nfmark_t
>::max())) {
345 debugs(3, DBG_CRITICAL
, "ERROR: Bad mark miss-mark value " << &token
[10]);
348 } else if (preserveMissTos
) {
350 if (!xstrtoui(&token
[10], NULL
, &v
, 0, std::numeric_limits
<tos_t
>::max())) {
351 debugs(3, DBG_CRITICAL
, "ERROR: Bad TOS miss-mark value " << &token
[10]);
354 preserveMissTosMask
= (tos_t
)v
;
356 debugs(3, DBG_CRITICAL
, "ERROR: miss-mask feature cannot be set without miss-preservation enabled");
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.
370 Ip::Qos::Config::dumpConfigLine(char *entry
, const char *name
) const
373 if (isHitTosActive()) {
375 p
+= snprintf(p
, 11, "%s", name
); // strlen("qos_flows ");
376 p
+= snprintf(p
, 4, "%s", "tos");
378 if (tosLocalHit
> 0) {
379 p
+= snprintf(p
, 16, " local-hit=0x%02X", tosLocalHit
);
381 if (tosSiblingHit
> 0) {
382 p
+= snprintf(p
, 18, " sibling-hit=0x%02X", tosSiblingHit
);
384 if (tosParentHit
> 0) {
385 p
+= snprintf(p
, 17, " parent-hit=0x%02X", tosParentHit
);
388 p
+= snprintf(p
, 11, " miss=0x%02X", tosMiss
);
389 if (tosMissMask
!=0xFFU
) {
390 p
+= snprintf(p
, 6, "/0x%02X", markMissMask
);
393 if (preserveMissTos
== 0) {
394 p
+= snprintf(p
, 23, " disable-preserve-miss");
396 if (preserveMissTos
&& preserveMissTosMask
!= 0) {
397 p
+= snprintf(p
, 16, " miss-mask=0x%02X", preserveMissTosMask
);
399 p
+= snprintf(p
, 2, "\n");
402 if (isHitNfmarkActive()) {
403 p
+= snprintf(p
, 11, "%s", name
); // strlen("qos_flows ");
404 p
+= snprintf(p
, 5, "%s", "mark");
406 if (markLocalHit
> 0) {
407 p
+= snprintf(p
, 22, " local-hit=0x%02X", markLocalHit
);
409 if (markSiblingHit
> 0) {
410 p
+= snprintf(p
, 24, " sibling-hit=0x%02X", markSiblingHit
);
412 if (markParentHit
> 0) {
413 p
+= snprintf(p
, 23, " parent-hit=0x%02X", markParentHit
);
416 p
+= snprintf(p
, 17, " miss=0x%02X", markMiss
);
417 if (markMissMask
!=0xFFFFFFFFU
) {
418 p
+= snprintf(p
, 12, "/0x%02X", markMissMask
);
421 if (preserveMissMark
== false) {
422 p
+= snprintf(p
, 23, " disable-preserve-miss");
424 if (preserveMissMark
&& preserveMissMarkMask
!= 0) {
425 p
+= snprintf(p
, 22, " miss-mask=0x%02X", preserveMissMarkMask
);
427 p
+= snprintf(p
, 2, "\n");