summaryrefslogtreecommitdiff
path: root/package/network/services/dnsmasq/patches/0004-Add-packet-dump-debugging-facility.patch
blob: e1c55f029d54c74fed06fe615130dff785215c3c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
From 6b17335209639a56f214d011eaed4ebcde8dd276 Mon Sep 17 00:00:00 2001
From: Simon Kelley <simon@thekelleys.org.uk>
Date: Tue, 8 May 2018 18:32:14 +0100
Subject: [PATCH 04/17] Add packet-dump debugging facility.

Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
---
 CHANGELOG      |   6 ++
 Makefile       |   2 +-
 bld/Android.mk |   3 +-
 man/dnsmasq.8  |   7 ++
 src/config.h   |  16 ++++-
 src/dnsmasq.c  |  16 ++++-
 src/dnsmasq.h  |  29 +++++++-
 src/dump.c     | 210 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/forward.c  |  37 ++++++++--
 src/option.c   |  14 ++++
 10 files changed, 329 insertions(+), 11 deletions(-)
 create mode 100644 src/dump.c

--- a/CHANGELOG
+++ b/CHANGELOG
@@ -17,6 +17,12 @@ version 2.80
         Fix DHCP broken-ness when --no-ping AND --dhcp-sequential-ip
 	are set. Thanks to Daniel Miess for help with this.
 
+	Add a facilty to store DNS packets sent/recieved in a
+	pcap-format file for later debugging. The file location
+	is given by the --dumpfile option, and a bitmap controlling
+	which packets should be dumped is given by the --dumpmask
+	option.
+
 
 version 2.79
 	Fix parsing of CNAME arguments, which are confused by extra spaces.
--- a/Makefile
+++ b/Makefile
@@ -76,7 +76,7 @@ objs = cache.o rfc1035.o util.o option.o
        helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \
        dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \
        domain.o dnssec.o blockdata.o tables.o loop.o inotify.o \
-       poll.o rrfilter.o edns0.o arp.o crypto.o
+       poll.o rrfilter.o edns0.o arp.o crypto.o dump.o
 
 hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
        dns-protocol.h radv-protocol.h ip6addr.h
--- a/bld/Android.mk
+++ b/bld/Android.mk
@@ -10,7 +10,8 @@ LOCAL_SRC_FILES :=  bpf.c cache.c dbus.c
 		    dhcp6.c rfc3315.c dhcp-common.c outpacket.c \
 		    radv.c slaac.c auth.c ipset.c domain.c \
 	            dnssec.c dnssec-openssl.c blockdata.c tables.c \
-		    loop.c inotify.c poll.c rrfilter.c edns0.c arp.c crypto.c
+		    loop.c inotify.c poll.c rrfilter.c edns0.c arp.c \
+		    crypto.c dump.c
 
 LOCAL_MODULE := dnsmasq
 
--- a/man/dnsmasq.8
+++ b/man/dnsmasq.8
@@ -647,6 +647,13 @@ V4 mapped IPv6 addresses, which have a r
 The address range can be of the form
 <ip address>,<ip address> or <ip address>/<netmask> in both forms of the option.
 .TP
+.B --dumpfile=<path/to/file>
+Specify the location of a pcap-format file which dnsmasq uses to dump copies of network packets for debugging purposes. If the file exists when dnsmasq starts, it is not deleted; new packets are added to the end.
+.TP
+.B --dumpmask=<mask>
+Specify which types of packets should be added to the dumpfile. The argument should be the OR of the bitmasks for each type of packet to be dumped: it can be specified in hex by preceding the number with 0x in  the normal way. Each time a packet is written to the dumpfile, dnsmasq logs the packet sequence and the mask
+representing its type. The current types are: 0x0001 - DNS queries from clients 0x0002 DNS replies to clients 0x0004 - DNS queries to upstream 0x0008 - DNS replies from upstream 0x0010 - queries send upstream for DNSSEC validation 0x0020 - replies to queries for DNSSEC validation 0x0040 - replies to client queries which fail DNSSEC validation 0x0080 replies to queries for DNSSEC validation which fail validation.
+.TP
 .B --add-mac[=base64|text]
 Add the MAC address of the requestor to DNS queries which are
 forwarded upstream. This may be used to DNS filtering by the upstream
--- a/src/config.h
+++ b/src/config.h
@@ -117,6 +117,9 @@ HAVE_AUTH
 HAVE_DNSSEC
    include DNSSEC validator.
 
+HAVE_DUMPFILE
+   include code to dump packets to a libpcap-format file for debugging.
+
 HAVE_LOOP
    include functionality to probe for and remove DNS forwarding loops.
 
@@ -132,6 +135,7 @@ NO_DHCP6
 NO_SCRIPT
 NO_LARGEFILE
 NO_AUTH
+NO_DUMPFILE
 NO_INOTIFY
    these are available to explicitly disable compile time options which would 
    otherwise be enabled automatically (HAVE_IPV6, >2Gb file sizes) or 
@@ -164,6 +168,7 @@ RESOLVFILE
 #define HAVE_AUTH
 #define HAVE_IPSET 
 #define HAVE_LOOP
+#define HAVE_DUMPFILE
 
 /* Build options which require external libraries.
    
@@ -363,6 +368,10 @@ HAVE_SOCKADDR_SA_LEN
 #undef HAVE_LOOP
 #endif
 
+#ifdef NO_DUMPFILE
+#undef HAVE_DUMPFILE
+#endif
+
 #if defined (HAVE_LINUX_NETWORK) && !defined(NO_INOTIFY)
 #define HAVE_INOTIFY
 #endif
@@ -451,8 +460,11 @@ static char *compile_opts =
 #ifndef HAVE_INOTIFY
 "no-"
 #endif
-"inotify";
-
+"inotify "
+#ifndef HAVE_DUMPFILE
+"no-"
+#endif
+"dumpfile";
 
 #endif
 
--- a/src/dnsmasq.c
+++ b/src/dnsmasq.c
@@ -366,7 +366,16 @@ int main (int argc, char **argv)
   else
     daemon->inotifyfd = -1;
 #endif
-       
+
+  if (daemon->dump_file)
+#ifdef HAVE_DUMPFILE
+    dump_init();
+  else 
+    daemon->dumpfd = -1;
+#else
+  die(_("Packet dumps not available: set HAVE_DUMP in src/config.h"), NULL, EC_BADCONF);
+#endif
+  
   if (option_bool(OPT_DBUS))
 #ifdef HAVE_DBUS
     {
@@ -1424,6 +1433,11 @@ static void async_event(int pipe, time_t
 
 	if (daemon->runfile)
 	  unlink(daemon->runfile);
+
+#ifdef HAVE_DUMPFILE
+	if (daemon->dumpfd != -1)
+	  close(daemon->dumpfd);
+#endif
 	
 	my_syslog(LOG_INFO, _("exiting on receipt of SIGTERM"));
 	flush_log();
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -119,6 +119,9 @@ typedef unsigned long long u64;
 #include <net/if_arp.h>
 #include <netinet/in_systm.h>
 #include <netinet/ip.h>
+#ifdef HAVE_IPV6
+#include <netinet/ip6.h>
+#endif
 #include <netinet/ip_icmp.h>
 #include <sys/uio.h>
 #include <syslog.h>
@@ -598,6 +601,16 @@ struct hostsfile {
   unsigned int index; /* matches to cache entries for logging */
 };
 
+/* packet-dump flags */
+#define DUMP_QUERY     0x0001
+#define DUMP_REPLY     0x0002
+#define DUMP_UP_QUERY  0x0004
+#define DUMP_UP_REPLY  0x0008
+#define DUMP_SEC_QUERY 0x0010
+#define DUMP_SEC_REPLY 0x0020
+#define DUMP_BOGUS     0x0040
+#define DUMP_SEC_BOGUS 0x0080
+
 
 /* DNSSEC status values. */
 #define STAT_SECURE             1
@@ -1020,14 +1033,14 @@ extern struct daemon {
   unsigned int duid_enterprise, duid_config_len;
   unsigned char *duid_config;
   char *dbus_name;
+  char *dump_file;
+  int dump_mask;
   unsigned long soa_sn, soa_refresh, soa_retry, soa_expiry;
 #ifdef OPTION6_PREFIX_CLASS 
   struct prefix_class *prefix_classes;
 #endif
 #ifdef HAVE_DNSSEC
   struct ds_config *ds;
-  int dnssec_no_time_check;
-  int back_to_the_future;
   char *timestamp_file;
 #endif
 
@@ -1040,6 +1053,8 @@ extern struct daemon {
   char *workspacename; /* ditto */
   char *rr_status; /* flags for individual RRs */
   int rr_status_sz;
+  int dnssec_no_time_check;
+  int back_to_the_future;
 #endif
   unsigned int local_answer, queries_forwarded, auth_answer;
   struct frec *frec_list;
@@ -1094,6 +1109,10 @@ extern struct daemon {
   char *addrbuff;
   char *addrbuff2; /* only allocated when OPT_EXTRALOG */
 
+#ifdef HAVE_DUMPFILE
+  /* file for packet dumps. */
+  int dumpfd;
+#endif
 } *daemon;
 
 /* cache.c */
@@ -1588,3 +1607,9 @@ int check_source(struct dns_header *head
 /* arp.c */
 int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy, time_t now);
 int do_arp_script_run(void);
+
+/* dump.c */
+#ifdef HAVE_DUMPFILE
+void dump_init(void);
+void dump_packet(int mask, void *packet, size_t len, union mysockaddr *src, union mysockaddr *dst);
+#endif
--- /dev/null
+++ b/src/dump.c
@@ -0,0 +1,210 @@
+/* dnsmasq is Copyright (c) 2000-2018 Simon Kelley
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 dated June, 1991, or
+   (at your option) version 3 dated 29 June, 2007.
+ 
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+     
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "dnsmasq.h"
+
+#ifdef HAVE_DUMPFILE
+
+static u32 packet_count;
+
+/* https://wiki.wireshark.org/Development/LibpcapFileFormat */
+struct pcap_hdr_s {
+        u32 magic_number;   /* magic number */
+        u16 version_major;  /* major version number */
+        u16 version_minor;  /* minor version number */
+        u32 thiszone;       /* GMT to local correction */
+        u32 sigfigs;        /* accuracy of timestamps */
+        u32 snaplen;        /* max length of captured packets, in octets */
+        u32 network;        /* data link type */
+};
+
+struct pcaprec_hdr_s {
+        u32 ts_sec;         /* timestamp seconds */
+        u32 ts_usec;        /* timestamp microseconds */
+        u32 incl_len;       /* number of octets of packet saved in file */
+        u32 orig_len;       /* actual length of packet */
+};
+
+
+void dump_init(void)
+{
+  struct stat buf;
+  struct pcap_hdr_s header;
+  struct pcaprec_hdr_s pcap_header;
+
+  packet_count = 0;
+  
+  if (stat(daemon->dump_file, &buf) == -1)
+    {
+      /* doesn't exist, create and add header */
+      header.magic_number = 0xa1b2c3d4;
+      header.version_major = 2;
+      header.version_minor = 4;
+      header.thiszone = 0;
+      header.sigfigs = 0;
+      header.snaplen = daemon->edns_pktsz + 200; /* slop for IP/UDP headers */
+      header.network = 101; /* DLT_RAW http://www.tcpdump.org/linktypes.html */
+
+      if (errno != ENOENT ||
+	  (daemon->dumpfd = creat(daemon->dump_file, S_IRUSR | S_IWUSR)) == -1 ||
+	  !read_write(daemon->dumpfd, (void *)&header, sizeof(header), 0))
+	die(_("cannot create %s: %s"), daemon->dump_file, EC_FILE);
+    }
+  else if ((daemon->dumpfd = open(daemon->dump_file, O_APPEND | O_RDWR)) == -1 ||
+	   !read_write(daemon->dumpfd, (void *)&header, sizeof(header), 1) ||
+	   header.magic_number != 0xa1b2c3d4)
+    die(_("cannot access %s: %s"), daemon->dump_file, EC_FILE);
+  else
+    {
+      /* count existing records */
+      while (read_write(daemon->dumpfd, (void *)&pcap_header, sizeof(pcap_header), 1))
+	{
+	  lseek(daemon->dumpfd, pcap_header.incl_len, SEEK_CUR);
+	  packet_count++;
+	}
+    }
+}
+
+void dump_packet(int mask, void *packet, size_t len, union mysockaddr *src, union mysockaddr *dst)
+{
+  struct ip ip;
+#ifdef HAVE_IPV6
+  struct ip6_hdr ip6;
+  int family;
+#endif
+  struct udphdr {
+    u16 uh_sport;               /* source port */
+    u16 uh_dport;               /* destination port */
+    u16 uh_ulen;                /* udp length */
+    u16 uh_sum;                 /* udp checksum */
+  } udp;
+  struct pcaprec_hdr_s pcap_header;
+  struct timeval time;
+  u32 i, sum;
+  void *iphdr;
+  size_t ipsz;
+  int rc;
+  
+  if (daemon->dumpfd == -1 || !(mask & daemon->dump_mask))
+    return;
+  
+  /* So wireshark can Id the packet. */
+  udp.uh_sport = udp.uh_dport = htons(NAMESERVER_PORT);
+
+#ifdef HAVE_IPV6
+  if (src)
+    family = src->sa.sa_family;
+  else
+    family = dst->sa.sa_family;
+
+  if (family == AF_INET6)
+    {
+      iphdr = &ip6;
+      ipsz = sizeof(ip6);
+      memset(&ip6, 0, sizeof(ip6));
+      
+      ip6.ip6_vfc = 6 << 4;
+      ip6.ip6_plen = htons(sizeof(struct udphdr) + len);
+      ip6.ip6_nxt = IPPROTO_UDP;
+      ip6.ip6_hops = 64;
+
+      if (src)
+	{
+	  memcpy(&ip6.ip6_src, &src->in6.sin6_addr, IN6ADDRSZ);
+	  udp.uh_sport = src->in6.sin6_port;
+	}
+      
+      if (dst)
+	{
+	  memcpy(&ip6.ip6_dst, &dst->in6.sin6_addr, IN6ADDRSZ);
+	  udp.uh_dport = dst->in6.sin6_port;
+	}
+            
+      /* start UDP checksum */
+      for (sum = 0, i = 0; i < IN6ADDRSZ; i++)
+	sum += ((u16 *)&ip6.ip6_src)[i];
+    }
+  else
+#endif
+    {
+      iphdr = &ip;
+      ipsz = sizeof(ip);
+      memset(&ip, 0, sizeof(ip));
+      
+      ip.ip_v = IPVERSION;
+      ip.ip_hl = sizeof(struct ip) / 4;
+      ip.ip_len = htons(sizeof(struct ip) + sizeof(struct udphdr) + len); 
+      ip.ip_ttl = IPDEFTTL;
+      ip.ip_p = IPPROTO_UDP;
+      
+      if (src)
+	{
+	  ip.ip_src = src->in.sin_addr;
+	  udp.uh_sport = src->in.sin_port;
+	}
+
+      if (dst)
+	{
+	  ip.ip_dst = dst->in.sin_addr;
+	  udp.uh_dport = dst->in.sin_port;
+	}
+      
+      ip.ip_sum = 0;
+      for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
+	sum += ((u16 *)&ip)[i];
+      while (sum >> 16)
+	sum = (sum & 0xffff) + (sum >> 16);  
+      ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
+      
+      /* start UDP checksum */
+      sum = ip.ip_src.s_addr & 0xffff;
+      sum += (ip.ip_src.s_addr >> 16) & 0xffff;
+      sum += ip.ip_dst.s_addr & 0xffff;
+      sum += (ip.ip_dst.s_addr >> 16) & 0xffff;
+    }
+  
+  if (len & 1)
+    ((unsigned char *)packet)[len] = 0; /* for checksum, in case length is odd. */
+
+  udp.uh_sum = 0;
+  udp.uh_ulen = htons(sizeof(struct udphdr) + len);
+  sum += htons(IPPROTO_UDP);
+  sum += htons(sizeof(struct udphdr) + len);
+  for (i = 0; i < sizeof(struct udphdr)/2; i++)
+    sum += ((u16 *)&udp)[i];
+  for (i = 0; i < (len + 1) / 2; i++)
+    sum += ((u16 *)packet)[i];
+  while (sum >> 16)
+    sum = (sum & 0xffff) + (sum >> 16);
+  udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
+
+  rc = gettimeofday(&time, NULL);
+  pcap_header.ts_sec = time.tv_sec;
+  pcap_header.ts_usec = time.tv_usec;
+  pcap_header.incl_len = pcap_header.orig_len = ipsz + sizeof(udp) + len;
+  
+  if (rc == -1 ||
+      !read_write(daemon->dumpfd, (void *)&pcap_header, sizeof(pcap_header), 0) ||
+      !read_write(daemon->dumpfd, iphdr, ipsz, 0) ||
+      !read_write(daemon->dumpfd, (void *)&udp, sizeof(udp), 0) ||
+      !read_write(daemon->dumpfd, (void *)packet, len, 0))
+    my_syslog(LOG_ERR, _("failed to write packet dump"));
+  else
+    my_syslog(LOG_INFO, _("dumping UDP packet %u mask 0x%04x"), ++packet_count, mask);
+
+}
+
+#endif
--- a/src/forward.c
+++ b/src/forward.c
@@ -508,6 +508,10 @@ static int forward_query(int udpfd, unio
 	    
 	      if (errno == 0)
 		{
+#ifdef HAVE_DUMPFILE
+		  dump_packet(DUMP_UP_QUERY, (void *)header, plen, NULL, &start->addr);
+#endif
+		  
 		  /* Keep info in case we want to re-send this packet */
 		  daemon->srv_save = start;
 		  daemon->packet_len = plen;
@@ -769,7 +773,7 @@ void reply_query(int fd, int family, tim
 #endif
   
   header = (struct dns_header *)daemon->packet;
-  
+
   if (n < (int)sizeof(struct dns_header) || !(header->hb3 & HB3_QR))
     return;
   
@@ -796,6 +800,12 @@ void reply_query(int fd, int family, tim
   if (!(forward = lookup_frec(ntohs(header->id), hash)))
     return;
   
+#ifdef HAVE_DUMPFILE
+  dump_packet((forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) ? DUMP_SEC_REPLY : DUMP_UP_REPLY,
+	      (void *)header, n, &serveraddr, NULL);
+#endif
+  
+  
   /* log_query gets called indirectly all over the place, so 
      pass these in global variables - sorry. */
   daemon->log_display_id = forward->log_id;
@@ -934,6 +944,11 @@ void reply_query(int fd, int family, tim
 		    status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class, 
 						   !option_bool(OPT_DNSSEC_IGN_NS) && (server->flags & SERV_DO_DNSSEC),
 						   NULL, NULL);
+#ifdef HAVE_DUMPFILE
+		  if (status == STAT_BOGUS)
+		    dump_packet((forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) ? DUMP_SEC_BOGUS : DUMP_BOGUS,
+				header, (size_t)n, &serveraddr, NULL);
+#endif
 		}
 	      
 	      /* Can't validate, as we're missing key data. Put this
@@ -1060,6 +1075,11 @@ void reply_query(int fd, int family, tim
 				setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
 			    }
 #endif
+			  
+#ifdef HAVE_DUMPFILE
+			  dump_packet(DUMP_SEC_QUERY, (void *)header, (size_t)nn, NULL, &server->addr);
+#endif
+			  
 			  while (retry_send(sendto(fd, (char *)header, nn, 0, 
 						   &server->addr.sa, 
 						   sa_len(&server->addr)))); 
@@ -1114,8 +1134,8 @@ void reply_query(int fd, int family, tim
 	      bogusanswer = 1;
 	    }
 	}
-#endif     
-      
+#endif
+
       /* restore CD bit to the value in the query */
       if (forward->flags & FREC_CHECKING_DISABLED)
 	header->hb4 |= HB4_CD;
@@ -1141,6 +1161,11 @@ void reply_query(int fd, int family, tim
 	      nn = resize_packet(header, nn, NULL, 0);
 	    }
 #endif
+
+#ifdef HAVE_DUMPFILE
+	  dump_packet(DUMP_REPLY, daemon->packet, (size_t)nn, NULL, &forward->source);
+#endif
+	  
 	  send_from(forward->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, nn, 
 		    &forward->source, &forward->dest, forward->iface);
 	}
@@ -1394,7 +1419,11 @@ void receive_query(struct listener *list
      pass these in global variables - sorry. */
   daemon->log_display_id = ++daemon->log_id;
   daemon->log_source_addr = &source_addr;
-  
+
+#ifdef HAVE_DUMPFILE
+  dump_packet(DUMP_QUERY, daemon->packet, (size_t)n, &source_addr, NULL);
+#endif
+	  
   if (extract_request(header, (size_t)n, daemon->namebuff, &type))
     {
 #ifdef HAVE_AUTH
--- a/src/option.c
+++ b/src/option.c
@@ -161,6 +161,8 @@ struct myoption {
 #define LOPT_TFTP_MTU      349
 #define LOPT_REPLY_DELAY   350
 #define LOPT_RAPID_COMMIT  351
+#define LOPT_DUMPFILE      352
+#define LOPT_DUMPMASK      353
  
 #ifdef HAVE_GETOPT_LONG
 static const struct option opts[] =  
@@ -327,6 +329,8 @@ static const struct myoption opts[] =
     { "dhcp-ttl", 1, 0 , LOPT_DHCPTTL },
     { "dhcp-reply-delay", 1, 0, LOPT_REPLY_DELAY },
     { "dhcp-rapid-commit", 0, 0, LOPT_RAPID_COMMIT },
+    { "dumpfile", 1, 0, LOPT_DUMPFILE },
+    { "dumpmask", 1, 0, LOPT_DUMPMASK },
     { NULL, 0, 0, 0 }
   };
 
@@ -500,6 +504,8 @@ static struct {
   { LOPT_DHCPTTL, ARG_ONE, "<ttl>", gettext_noop("Set TTL in DNS responses with DHCP-derived addresses."), NULL }, 
   { LOPT_REPLY_DELAY, ARG_ONE, "<integer>", gettext_noop("Delay DHCP replies for at least number of seconds."), NULL },
   { LOPT_RAPID_COMMIT, OPT_RAPID_COMMIT, NULL, gettext_noop("Enables DHCPv4 Rapid Commit option."), NULL },
+  { LOPT_DUMPFILE, ARG_ONE, "<path>", gettext_noop("Path to debug packet dump file"), NULL },
+  { LOPT_DUMPMASK, ARG_ONE, "<hex>", gettext_noop("Mask which packets to dump"), NULL },
   { 0, 0, NULL, NULL, NULL }
 }; 
 
@@ -1811,6 +1817,14 @@ static int one_opt(int option, char *arg
 	ret_err(_("bad MX target"));
       break;
 
+    case LOPT_DUMPFILE:  /* --dumpfile */
+      daemon->dump_file = opt_string_alloc(arg);
+      break;
+
+    case LOPT_DUMPMASK:  /* --dumpmask */
+      daemon->dump_mask = strtol(arg, NULL, 0);
+      break;
+      
 #ifdef HAVE_DHCP      
     case 'l':  /* --dhcp-leasefile */
       daemon->lease_file = opt_string_alloc(arg);