[37aaf89] | 1 | Description: dnscache: merge similar outgoing udp packets |
---|
| 2 | This patch takes a slightly different approach to merging |
---|
| 3 | than the previous qmerge patch: rather than merging between |
---|
| 4 | the "query" and "dns_transmit" layers, it actually |
---|
| 5 | incorporates the merging into the dns_transmit layer. |
---|
| 6 | . |
---|
| 7 | This makes it a much more invasive and messy patch, but |
---|
| 8 | means that it should correctly handle all situations |
---|
| 9 | (in particular, ones where sending to two different IPs will |
---|
| 10 | gives different results, generally because one server is |
---|
| 11 | down or lame). |
---|
| 12 | . |
---|
| 13 | The general strategy is this: |
---|
| 14 | . |
---|
| 15 | - when a dns_transmit is about to send() a UDP query, it |
---|
| 16 | checks for an equivalent inprogress query. |
---|
| 17 | . |
---|
| 18 | If it finds one, it marks the inprogress dns_transmit as |
---|
| 19 | its "master", and itself as a "slave" of the master. |
---|
| 20 | . |
---|
| 21 | If it doesn't, it sends the packet and registers itself |
---|
| 22 | as inprogress. |
---|
| 23 | . |
---|
| 24 | - when a dns_transmit recv()s a UDP packet, it creates a |
---|
| 25 | copy of the packet for each of its slaves |
---|
| 26 | . |
---|
| 27 | Two outgoing packets are considered equivalent if: |
---|
| 28 | . |
---|
| 29 | 1. they are going to the same IP |
---|
| 30 | 2. they have the same qtype |
---|
| 31 | 3. they have the same qname |
---|
| 32 | . |
---|
| 33 | Because this change affects the dns library itself, this |
---|
| 34 | change can potentially affect not just dnscache, but all of |
---|
| 35 | the diagnostic tools. To address this, merging must be |
---|
| 36 | enabled explicitly by the caller; dnscache will enable |
---|
| 37 | merging if the MERGEQUERIES environment variable is set. |
---|
| 38 | . |
---|
| 39 | I tried to keep the patch as small and simple as possible so |
---|
| 40 | that its correctness could be verified by readers. There |
---|
| 41 | are a few places where performance might benefit from making |
---|
| 42 | it more complex: |
---|
| 43 | . |
---|
| 44 | - dns_transmit now knows the value of MAXUDP, since it is |
---|
| 45 | an upper bound on the number of slaves and inprogress |
---|
| 46 | queries. As a result: |
---|
| 47 | . |
---|
| 48 | - each non-merging program which uses the dns library |
---|
| 49 | wastes MAXUDP*sizeof(pointer) bytes of static memory |
---|
| 50 | for the inprogress list |
---|
| 51 | . |
---|
| 52 | - each dns_transmit uses an extra MAXUDP*sizeof(pointer) |
---|
| 53 | bytes for its slaves list. For dnscache, this |
---|
| 54 | translates to 160K total on a 32-bit platform with |
---|
| 55 | default MAXUDP. |
---|
| 56 | . |
---|
| 57 | Both could be avoided by using dynamic allocation. |
---|
| 58 | . |
---|
| 59 | - We have to do an O(MAXUDP) linear search to find similar |
---|
| 60 | inprogress queries (non-merge-enabled users of the |
---|
| 61 | library avoid paying this price, though). |
---|
| 62 | . |
---|
| 63 | This could be avoided by using a data structure with a |
---|
| 64 | fast key lookup for inprogress. |
---|
| 65 | . |
---|
| 66 | This patch is only lightly tested. Use on production servers at your own |
---|
| 67 | risk (and please report to the list if you have success using it). |
---|
| 68 | Author: Jeff King <peff () peff ! net> |
---|
| 69 | Date: Wed, 1 Apr 2009 14:10:54 +0000 |
---|
| 70 | Debian-Bug: https://bugs.debian.org/516394 |
---|
| 71 | Last-Update: 2020-07-26 |
---|
| 72 | |
---|
| 73 | diff --git a/clients.h b/clients.h |
---|
| 74 | new file mode 100644 |
---|
| 75 | index 0000000..983a4ad |
---|
| 76 | --- /dev/null |
---|
| 77 | +++ b/clients.h |
---|
| 78 | @@ -0,0 +1,7 @@ |
---|
| 79 | +#ifndef CLIENTS_H |
---|
| 80 | +#define CLIENTS_H |
---|
| 81 | + |
---|
| 82 | +#define MAXUDP 200 |
---|
| 83 | +#define MAXTCP 20 |
---|
| 84 | + |
---|
| 85 | +#endif /* CLIENTS_H */ |
---|
| 86 | diff --git a/dns.h b/dns.h |
---|
| 87 | index 3849f4c..d1e2ffc 100644 |
---|
| 88 | --- a/dns.h |
---|
| 89 | +++ b/dns.h |
---|
| 90 | @@ -4,6 +4,7 @@ |
---|
| 91 | #include "stralloc.h" |
---|
| 92 | #include "iopause.h" |
---|
| 93 | #include "taia.h" |
---|
| 94 | +#include "clients.h" |
---|
| 95 | |
---|
| 96 | #define DNS_C_IN "\0\1" |
---|
| 97 | #define DNS_C_ANY "\0\377" |
---|
| 98 | @@ -38,8 +39,14 @@ struct dns_transmit { |
---|
| 99 | const char *servers; |
---|
| 100 | char localip[4]; |
---|
| 101 | char qtype[2]; |
---|
| 102 | + struct dns_transmit *master; |
---|
| 103 | + struct dns_transmit *slaves[MAXUDP]; |
---|
| 104 | + int nslaves; |
---|
| 105 | } ; |
---|
| 106 | |
---|
| 107 | +extern void dns_enable_merge(void (*logger)(const char *, const char *, |
---|
| 108 | + const char *)); |
---|
| 109 | + |
---|
| 110 | extern void dns_random_init(const char *); |
---|
| 111 | extern unsigned int dns_random(unsigned int); |
---|
| 112 | |
---|
| 113 | diff --git a/dns_transmit.c b/dns_transmit.c |
---|
| 114 | index 4d6e39f..3984776 100644 |
---|
| 115 | --- a/dns_transmit.c |
---|
| 116 | +++ b/dns_transmit.c |
---|
| 117 | @@ -7,6 +7,61 @@ |
---|
| 118 | #include "byte.h" |
---|
| 119 | #include "uint16.h" |
---|
| 120 | #include "dns.h" |
---|
| 121 | +#include "strerr.h" |
---|
| 122 | + |
---|
| 123 | +static int merge_enable; |
---|
| 124 | +static void (*merge_logger)(const char *, const char *, const char *); |
---|
| 125 | +void dns_enable_merge(void (*f)(const char *, const char *, const char *)) |
---|
| 126 | +{ |
---|
| 127 | + merge_enable = 1; |
---|
| 128 | + merge_logger = f; |
---|
| 129 | +} |
---|
| 130 | + |
---|
| 131 | +static int merge_equal(struct dns_transmit *a, struct dns_transmit *b) |
---|
| 132 | +{ |
---|
| 133 | + const char *ip1 = a->servers + 4 * a->curserver; |
---|
| 134 | + const char *ip2 = b->servers + 4 * b->curserver; |
---|
| 135 | + return |
---|
| 136 | + byte_equal(ip1, 4, ip2) && |
---|
| 137 | + byte_equal(a->qtype, 2, b->qtype) && |
---|
| 138 | + dns_domain_equal(a->query + 14, b->query + 14); |
---|
| 139 | +} |
---|
| 140 | + |
---|
| 141 | +struct dns_transmit *inprogress[MAXUDP]; |
---|
| 142 | + |
---|
| 143 | +static int try_merge(struct dns_transmit *d) |
---|
| 144 | +{ |
---|
| 145 | + int i; |
---|
| 146 | + for (i = 0; i < MAXUDP; i++) { |
---|
| 147 | + if (!inprogress[i]) continue; |
---|
| 148 | + if (!merge_equal(d, inprogress[i])) continue; |
---|
| 149 | + d->master = inprogress[i]; |
---|
| 150 | + inprogress[i]->slaves[inprogress[i]->nslaves++] = d; |
---|
| 151 | + return 1; |
---|
| 152 | + } |
---|
| 153 | + return 0; |
---|
| 154 | +} |
---|
| 155 | + |
---|
| 156 | +static void register_inprogress(struct dns_transmit *d) |
---|
| 157 | +{ |
---|
| 158 | + int i; |
---|
| 159 | + for (i = 0; i < MAXUDP; i++) { |
---|
| 160 | + if (!inprogress[i]) { |
---|
| 161 | + inprogress[i] = d; |
---|
| 162 | + return; |
---|
| 163 | + } |
---|
| 164 | + } |
---|
| 165 | + strerr_die1x(100, "BUG: out of inprogress slots"); |
---|
| 166 | +} |
---|
| 167 | + |
---|
| 168 | +static void unregister_inprogress(struct dns_transmit *d) |
---|
| 169 | +{ |
---|
| 170 | + int i; |
---|
| 171 | + for (i = 0; i < MAXUDP; i++) { |
---|
| 172 | + if (inprogress[i] == d) |
---|
| 173 | + inprogress[i] = 0; |
---|
| 174 | + } |
---|
| 175 | +} |
---|
| 176 | |
---|
| 177 | static int serverwantstcp(const char *buf,unsigned int len) |
---|
| 178 | { |
---|
| 179 | @@ -59,8 +114,28 @@ static void packetfree(struct dns_transmit *d) |
---|
| 180 | d->packet = 0; |
---|
| 181 | } |
---|
| 182 | |
---|
| 183 | +static void mergefree(struct dns_transmit *d) |
---|
| 184 | +{ |
---|
| 185 | + int i; |
---|
| 186 | + if (merge_enable) |
---|
| 187 | + unregister_inprogress(d); |
---|
| 188 | + /* unregister us from our master */ |
---|
| 189 | + if (d->master) { |
---|
| 190 | + for (i = 0; i < d->master->nslaves; i++) |
---|
| 191 | + if (d->master->slaves[i] == d) |
---|
| 192 | + d->master->slaves[i] = 0; |
---|
| 193 | + } |
---|
| 194 | + /* and unregister all of our slaves from us */ |
---|
| 195 | + for (i = 0; i < d->nslaves; i++) { |
---|
| 196 | + if (d->slaves[i]) |
---|
| 197 | + d->slaves[i]->master = NULL; |
---|
| 198 | + } |
---|
| 199 | + d->nslaves = 0; |
---|
| 200 | +} |
---|
| 201 | + |
---|
| 202 | static void queryfree(struct dns_transmit *d) |
---|
| 203 | { |
---|
| 204 | + mergefree(d); |
---|
| 205 | if (!d->query) return; |
---|
| 206 | alloc_free(d->query); |
---|
| 207 | d->query = 0; |
---|
| 208 | @@ -99,11 +174,18 @@ static int thisudp(struct dns_transmit *d) |
---|
| 209 | const char *ip; |
---|
| 210 | |
---|
| 211 | socketfree(d); |
---|
| 212 | + mergefree(d); |
---|
| 213 | |
---|
| 214 | while (d->udploop < 4) { |
---|
| 215 | for (;d->curserver < 16;++d->curserver) { |
---|
| 216 | ip = d->servers + 4 * d->curserver; |
---|
| 217 | if (byte_diff(ip,4,"\0\0\0\0")) { |
---|
| 218 | + if (merge_enable && try_merge(d)) { |
---|
| 219 | + if (merge_logger) |
---|
| 220 | + merge_logger(ip, d->qtype, d->query + 14); |
---|
| 221 | + return 0; |
---|
| 222 | + } |
---|
| 223 | + |
---|
| 224 | d->query[2] = dns_random(256); |
---|
| 225 | d->query[3] = dns_random(256); |
---|
| 226 | |
---|
| 227 | @@ -118,6 +200,8 @@ static int thisudp(struct dns_transmit *d) |
---|
| 228 | taia_uint(&d->deadline,timeouts[d->udploop]); |
---|
| 229 | taia_add(&d->deadline,&d->deadline,&now); |
---|
| 230 | d->tcpstate = 0; |
---|
| 231 | + if (merge_enable) |
---|
| 232 | + register_inprogress(d); |
---|
| 233 | return 0; |
---|
| 234 | } |
---|
| 235 | |
---|
| 236 | @@ -226,8 +310,12 @@ void dns_transmit_io(struct dns_transmit *d,iopause_fd *x,struct taia *deadline) |
---|
| 237 | x->fd = d->s1 - 1; |
---|
| 238 | |
---|
| 239 | switch(d->tcpstate) { |
---|
| 240 | - case 0: case 3: case 4: case 5: |
---|
| 241 | - x->events = IOPAUSE_READ; |
---|
| 242 | + case 0: |
---|
| 243 | + if (d->master) return; |
---|
| 244 | + if (d->packet) { taia_now(deadline); return; } |
---|
| 245 | + /* otherwise, fall through */ |
---|
| 246 | + case 3: case 4: case 5: |
---|
| 247 | + x->events = IOPAUSE_READ; |
---|
| 248 | break; |
---|
| 249 | case 1: case 2: |
---|
| 250 | x->events = IOPAUSE_WRITE; |
---|
| 251 | @@ -244,10 +332,14 @@ int dns_transmit_get(struct dns_transmit *d,const iopause_fd *x,const struct tai |
---|
| 252 | unsigned char ch; |
---|
| 253 | int r; |
---|
| 254 | int fd; |
---|
| 255 | + int i; |
---|
| 256 | |
---|
| 257 | errno = error_io; |
---|
| 258 | fd = d->s1 - 1; |
---|
| 259 | |
---|
| 260 | + if (d->tcpstate == 0 && d->master) return 0; |
---|
| 261 | + if (d->tcpstate == 0 && d->packet) return 1; |
---|
| 262 | + |
---|
| 263 | if (!x->revents) { |
---|
| 264 | if (taia_less(when,&d->deadline)) return 0; |
---|
| 265 | errno = error_timeout; |
---|
| 266 | @@ -279,6 +371,15 @@ have sent query to curserver on UDP socket s |
---|
| 267 | d->packet = alloc(d->packetlen); |
---|
| 268 | if (!d->packet) { dns_transmit_free(d); return -1; } |
---|
| 269 | byte_copy(d->packet,d->packetlen,udpbuf); |
---|
| 270 | + |
---|
| 271 | + for (i = 0; i < d->nslaves; i++) { |
---|
| 272 | + if (!d->slaves[i]) continue; |
---|
| 273 | + d->slaves[i]->packetlen = d->packetlen; |
---|
| 274 | + d->slaves[i]->packet = alloc(d->packetlen); |
---|
| 275 | + if (!d->slaves[i]->packet) { dns_transmit_free(d->slaves[i]); continue; } |
---|
| 276 | + byte_copy(d->slaves[i]->packet,d->packetlen,udpbuf); |
---|
| 277 | + } |
---|
| 278 | + |
---|
| 279 | queryfree(d); |
---|
| 280 | return 1; |
---|
| 281 | } |
---|
| 282 | diff --git a/dnscache.c b/dnscache.c |
---|
| 283 | index 8c899a3..c8db179 100644 |
---|
| 284 | --- a/dnscache.c |
---|
| 285 | +++ b/dnscache.c |
---|
| 286 | @@ -54,7 +54,6 @@ uint64 numqueries = 0; |
---|
| 287 | |
---|
| 288 | static int udp53; |
---|
| 289 | |
---|
| 290 | -#define MAXUDP 200 |
---|
| 291 | static struct udpclient { |
---|
| 292 | struct query q; |
---|
| 293 | struct taia start; |
---|
| 294 | @@ -131,7 +130,6 @@ void u_new(void) |
---|
| 295 | |
---|
| 296 | static int tcp53; |
---|
| 297 | |
---|
| 298 | -#define MAXTCP 20 |
---|
| 299 | struct tcpclient { |
---|
| 300 | struct query q; |
---|
| 301 | struct taia start; |
---|
| 302 | @@ -435,6 +433,8 @@ int main() |
---|
| 303 | response_hidettl(); |
---|
| 304 | if (env_get("FORWARDONLY")) |
---|
| 305 | query_forwardonly(); |
---|
| 306 | + if (env_get("MERGEQUERIES")) |
---|
| 307 | + dns_enable_merge(log_merge); |
---|
| 308 | |
---|
| 309 | if (!roots_init()) |
---|
| 310 | strerr_die2sys(111,FATAL,"unable to read servers: "); |
---|
| 311 | diff --git a/log.c b/log.c |
---|
| 312 | index c43e8b0..3e1c674 100644 |
---|
| 313 | --- a/log.c |
---|
| 314 | +++ b/log.c |
---|
| 315 | @@ -150,6 +150,12 @@ void log_tx(const char *q,const char qtype[2],const char *control,const char ser |
---|
| 316 | line(); |
---|
| 317 | } |
---|
| 318 | |
---|
| 319 | +void log_merge(const char *addr, const char qtype[2], const char *q) |
---|
| 320 | +{ |
---|
| 321 | + string("merge "); ip(addr); space(); logtype(qtype); space(); name(q); |
---|
| 322 | + line(); |
---|
| 323 | +} |
---|
| 324 | + |
---|
| 325 | void log_cachedanswer(const char *q,const char type[2]) |
---|
| 326 | { |
---|
| 327 | string("cached "); logtype(type); space(); |
---|
| 328 | diff --git a/log.h b/log.h |
---|
| 329 | index fe62fa3..dd6408a 100644 |
---|
| 330 | --- a/log.h |
---|
| 331 | +++ b/log.h |
---|
| 332 | @@ -18,6 +18,7 @@ extern void log_cachednxdomain(const char *); |
---|
| 333 | extern void log_cachedns(const char *,const char *); |
---|
| 334 | |
---|
| 335 | extern void log_tx(const char *,const char *,const char *,const char *,unsigned int); |
---|
| 336 | +extern void log_merge(const char *, const char *, const char *); |
---|
| 337 | |
---|
| 338 | extern void log_nxdomain(const char *,const char *,unsigned int); |
---|
| 339 | extern void log_nodata(const char *,const char *,const char *,unsigned int); |
---|