v1.5 branch refresh based upon upstream master @ c8677ca89e53e3be7988d54280fce166cc894a7e
[librecmc/librecmc.git] / package / network / services / dnsmasq / patches / 0004-Add-packet-dump-debugging-facility.patch
1 From 6b17335209639a56f214d011eaed4ebcde8dd276 Mon Sep 17 00:00:00 2001
2 From: Simon Kelley <simon@thekelleys.org.uk>
3 Date: Tue, 8 May 2018 18:32:14 +0100
4 Subject: [PATCH 04/10] Add packet-dump debugging facility.
5
6 Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
7 ---
8  CHANGELOG      |   6 ++
9  Makefile       |   2 +-
10  bld/Android.mk |   3 +-
11  man/dnsmasq.8  |   7 ++
12  src/config.h   |  16 ++++-
13  src/dnsmasq.c  |  16 ++++-
14  src/dnsmasq.h  |  29 +++++++-
15  src/dump.c     | 210 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
16  src/forward.c  |  37 ++++++++--
17  src/option.c   |  14 ++++
18  10 files changed, 329 insertions(+), 11 deletions(-)
19  create mode 100644 src/dump.c
20
21 --- a/CHANGELOG
22 +++ b/CHANGELOG
23 @@ -17,6 +17,12 @@ version 2.80
24          Fix DHCP broken-ness when --no-ping AND --dhcp-sequential-ip
25         are set. Thanks to Daniel Miess for help with this.
26  
27 +       Add a facilty to store DNS packets sent/recieved in a
28 +       pcap-format file for later debugging. The file location
29 +       is given by the --dumpfile option, and a bitmap controlling
30 +       which packets should be dumped is given by the --dumpmask
31 +       option.
32 +
33  
34  version 2.79
35         Fix parsing of CNAME arguments, which are confused by extra spaces.
36 --- a/Makefile
37 +++ b/Makefile
38 @@ -76,7 +76,7 @@ objs = cache.o rfc1035.o util.o option.o
39         helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \
40         dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \
41         domain.o dnssec.o blockdata.o tables.o loop.o inotify.o \
42 -       poll.o rrfilter.o edns0.o arp.o crypto.o
43 +       poll.o rrfilter.o edns0.o arp.o crypto.o dump.o
44  
45  hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
46         dns-protocol.h radv-protocol.h ip6addr.h
47 --- a/bld/Android.mk
48 +++ b/bld/Android.mk
49 @@ -10,7 +10,8 @@ LOCAL_SRC_FILES :=  bpf.c cache.c dbus.c
50                     dhcp6.c rfc3315.c dhcp-common.c outpacket.c \
51                     radv.c slaac.c auth.c ipset.c domain.c \
52                     dnssec.c dnssec-openssl.c blockdata.c tables.c \
53 -                   loop.c inotify.c poll.c rrfilter.c edns0.c arp.c crypto.c
54 +                   loop.c inotify.c poll.c rrfilter.c edns0.c arp.c \
55 +                   crypto.c dump.c
56  
57  LOCAL_MODULE := dnsmasq
58  
59 --- a/man/dnsmasq.8
60 +++ b/man/dnsmasq.8
61 @@ -647,6 +647,13 @@ V4 mapped IPv6 addresses, which have a r
62  The address range can be of the form
63  <ip address>,<ip address> or <ip address>/<netmask> in both forms of the option.
64  .TP
65 +.B --dumpfile=<path/to/file>
66 +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.
67 +.TP
68 +.B --dumpmask=<mask>
69 +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
70 +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.
71 +.TP
72  .B --add-mac[=base64|text]
73  Add the MAC address of the requestor to DNS queries which are
74  forwarded upstream. This may be used to DNS filtering by the upstream
75 --- a/src/config.h
76 +++ b/src/config.h
77 @@ -117,6 +117,9 @@ HAVE_AUTH
78  HAVE_DNSSEC
79     include DNSSEC validator.
80  
81 +HAVE_DUMPFILE
82 +   include code to dump packets to a libpcap-format file for debugging.
83 +
84  HAVE_LOOP
85     include functionality to probe for and remove DNS forwarding loops.
86  
87 @@ -132,6 +135,7 @@ NO_DHCP6
88  NO_SCRIPT
89  NO_LARGEFILE
90  NO_AUTH
91 +NO_DUMPFILE
92  NO_INOTIFY
93     these are available to explicitly disable compile time options which would 
94     otherwise be enabled automatically (HAVE_IPV6, >2Gb file sizes) or 
95 @@ -164,6 +168,7 @@ RESOLVFILE
96  #define HAVE_AUTH
97  #define HAVE_IPSET 
98  #define HAVE_LOOP
99 +#define HAVE_DUMPFILE
100  
101  /* Build options which require external libraries.
102     
103 @@ -363,6 +368,10 @@ HAVE_SOCKADDR_SA_LEN
104  #undef HAVE_LOOP
105  #endif
106  
107 +#ifdef NO_DUMPFILE
108 +#undef HAVE_DUMPFILE
109 +#endif
110 +
111  #if defined (HAVE_LINUX_NETWORK) && !defined(NO_INOTIFY)
112  #define HAVE_INOTIFY
113  #endif
114 @@ -451,8 +460,11 @@ static char *compile_opts =
115  #ifndef HAVE_INOTIFY
116  "no-"
117  #endif
118 -"inotify";
119 -
120 +"inotify "
121 +#ifndef HAVE_DUMPFILE
122 +"no-"
123 +#endif
124 +"dumpfile";
125  
126  #endif
127  
128 --- a/src/dnsmasq.c
129 +++ b/src/dnsmasq.c
130 @@ -366,7 +366,16 @@ int main (int argc, char **argv)
131    else
132      daemon->inotifyfd = -1;
133  #endif
134 -       
135 +
136 +  if (daemon->dump_file)
137 +#ifdef HAVE_DUMPFILE
138 +    dump_init();
139 +  else 
140 +    daemon->dumpfd = -1;
141 +#else
142 +  die(_("Packet dumps not available: set HAVE_DUMP in src/config.h"), NULL, EC_BADCONF);
143 +#endif
144 +  
145    if (option_bool(OPT_DBUS))
146  #ifdef HAVE_DBUS
147      {
148 @@ -1424,6 +1433,11 @@ static void async_event(int pipe, time_t
149  
150         if (daemon->runfile)
151           unlink(daemon->runfile);
152 +
153 +#ifdef HAVE_DUMPFILE
154 +       if (daemon->dumpfd != -1)
155 +         close(daemon->dumpfd);
156 +#endif
157         
158         my_syslog(LOG_INFO, _("exiting on receipt of SIGTERM"));
159         flush_log();
160 --- a/src/dnsmasq.h
161 +++ b/src/dnsmasq.h
162 @@ -119,6 +119,9 @@ typedef unsigned long long u64;
163  #include <net/if_arp.h>
164  #include <netinet/in_systm.h>
165  #include <netinet/ip.h>
166 +#ifdef HAVE_IPV6
167 +#include <netinet/ip6.h>
168 +#endif
169  #include <netinet/ip_icmp.h>
170  #include <sys/uio.h>
171  #include <syslog.h>
172 @@ -598,6 +601,16 @@ struct hostsfile {
173    unsigned int index; /* matches to cache entries for logging */
174  };
175  
176 +/* packet-dump flags */
177 +#define DUMP_QUERY     0x0001
178 +#define DUMP_REPLY     0x0002
179 +#define DUMP_UP_QUERY  0x0004
180 +#define DUMP_UP_REPLY  0x0008
181 +#define DUMP_SEC_QUERY 0x0010
182 +#define DUMP_SEC_REPLY 0x0020
183 +#define DUMP_BOGUS     0x0040
184 +#define DUMP_SEC_BOGUS 0x0080
185 +
186  
187  /* DNSSEC status values. */
188  #define STAT_SECURE             1
189 @@ -1020,14 +1033,14 @@ extern struct daemon {
190    unsigned int duid_enterprise, duid_config_len;
191    unsigned char *duid_config;
192    char *dbus_name;
193 +  char *dump_file;
194 +  int dump_mask;
195    unsigned long soa_sn, soa_refresh, soa_retry, soa_expiry;
196  #ifdef OPTION6_PREFIX_CLASS 
197    struct prefix_class *prefix_classes;
198  #endif
199  #ifdef HAVE_DNSSEC
200    struct ds_config *ds;
201 -  int dnssec_no_time_check;
202 -  int back_to_the_future;
203    char *timestamp_file;
204  #endif
205  
206 @@ -1040,6 +1053,8 @@ extern struct daemon {
207    char *workspacename; /* ditto */
208    char *rr_status; /* flags for individual RRs */
209    int rr_status_sz;
210 +  int dnssec_no_time_check;
211 +  int back_to_the_future;
212  #endif
213    unsigned int local_answer, queries_forwarded, auth_answer;
214    struct frec *frec_list;
215 @@ -1094,6 +1109,10 @@ extern struct daemon {
216    char *addrbuff;
217    char *addrbuff2; /* only allocated when OPT_EXTRALOG */
218  
219 +#ifdef HAVE_DUMPFILE
220 +  /* file for packet dumps. */
221 +  int dumpfd;
222 +#endif
223  } *daemon;
224  
225  /* cache.c */
226 @@ -1588,3 +1607,9 @@ int check_source(struct dns_header *head
227  /* arp.c */
228  int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy, time_t now);
229  int do_arp_script_run(void);
230 +
231 +/* dump.c */
232 +#ifdef HAVE_DUMPFILE
233 +void dump_init(void);
234 +void dump_packet(int mask, void *packet, size_t len, union mysockaddr *src, union mysockaddr *dst);
235 +#endif
236 --- /dev/null
237 +++ b/src/dump.c
238 @@ -0,0 +1,210 @@
239 +/* dnsmasq is Copyright (c) 2000-2018 Simon Kelley
240 +
241 +   This program is free software; you can redistribute it and/or modify
242 +   it under the terms of the GNU General Public License as published by
243 +   the Free Software Foundation; version 2 dated June, 1991, or
244 +   (at your option) version 3 dated 29 June, 2007.
245
246 +   This program is distributed in the hope that it will be useful,
247 +   but WITHOUT ANY WARRANTY; without even the implied warranty of
248 +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
249 +   GNU General Public License for more details.
250 +     
251 +   You should have received a copy of the GNU General Public License
252 +   along with this program.  If not, see <http://www.gnu.org/licenses/>.
253 +*/
254 +
255 +#include "dnsmasq.h"
256 +
257 +#ifdef HAVE_DUMPFILE
258 +
259 +static u32 packet_count;
260 +
261 +/* https://wiki.wireshark.org/Development/LibpcapFileFormat */
262 +struct pcap_hdr_s {
263 +        u32 magic_number;   /* magic number */
264 +        u16 version_major;  /* major version number */
265 +        u16 version_minor;  /* minor version number */
266 +        u32 thiszone;       /* GMT to local correction */
267 +        u32 sigfigs;        /* accuracy of timestamps */
268 +        u32 snaplen;        /* max length of captured packets, in octets */
269 +        u32 network;        /* data link type */
270 +};
271 +
272 +struct pcaprec_hdr_s {
273 +        u32 ts_sec;         /* timestamp seconds */
274 +        u32 ts_usec;        /* timestamp microseconds */
275 +        u32 incl_len;       /* number of octets of packet saved in file */
276 +        u32 orig_len;       /* actual length of packet */
277 +};
278 +
279 +
280 +void dump_init(void)
281 +{
282 +  struct stat buf;
283 +  struct pcap_hdr_s header;
284 +  struct pcaprec_hdr_s pcap_header;
285 +
286 +  packet_count = 0;
287 +  
288 +  if (stat(daemon->dump_file, &buf) == -1)
289 +    {
290 +      /* doesn't exist, create and add header */
291 +      header.magic_number = 0xa1b2c3d4;
292 +      header.version_major = 2;
293 +      header.version_minor = 4;
294 +      header.thiszone = 0;
295 +      header.sigfigs = 0;
296 +      header.snaplen = daemon->edns_pktsz + 200; /* slop for IP/UDP headers */
297 +      header.network = 101; /* DLT_RAW http://www.tcpdump.org/linktypes.html */
298 +
299 +      if (errno != ENOENT ||
300 +         (daemon->dumpfd = creat(daemon->dump_file, S_IRUSR | S_IWUSR)) == -1 ||
301 +         !read_write(daemon->dumpfd, (void *)&header, sizeof(header), 0))
302 +       die(_("cannot create %s: %s"), daemon->dump_file, EC_FILE);
303 +    }
304 +  else if ((daemon->dumpfd = open(daemon->dump_file, O_APPEND | O_RDWR)) == -1 ||
305 +          !read_write(daemon->dumpfd, (void *)&header, sizeof(header), 1) ||
306 +          header.magic_number != 0xa1b2c3d4)
307 +    die(_("cannot access %s: %s"), daemon->dump_file, EC_FILE);
308 +  else
309 +    {
310 +      /* count existing records */
311 +      while (read_write(daemon->dumpfd, (void *)&pcap_header, sizeof(pcap_header), 1))
312 +       {
313 +         lseek(daemon->dumpfd, pcap_header.incl_len, SEEK_CUR);
314 +         packet_count++;
315 +       }
316 +    }
317 +}
318 +
319 +void dump_packet(int mask, void *packet, size_t len, union mysockaddr *src, union mysockaddr *dst)
320 +{
321 +  struct ip ip;
322 +#ifdef HAVE_IPV6
323 +  struct ip6_hdr ip6;
324 +  int family;
325 +#endif
326 +  struct udphdr {
327 +    u16 uh_sport;               /* source port */
328 +    u16 uh_dport;               /* destination port */
329 +    u16 uh_ulen;                /* udp length */
330 +    u16 uh_sum;                 /* udp checksum */
331 +  } udp;
332 +  struct pcaprec_hdr_s pcap_header;
333 +  struct timeval time;
334 +  u32 i, sum;
335 +  void *iphdr;
336 +  size_t ipsz;
337 +  int rc;
338 +  
339 +  if (daemon->dumpfd == -1 || !(mask & daemon->dump_mask))
340 +    return;
341 +  
342 +  /* So wireshark can Id the packet. */
343 +  udp.uh_sport = udp.uh_dport = htons(NAMESERVER_PORT);
344 +
345 +#ifdef HAVE_IPV6
346 +  if (src)
347 +    family = src->sa.sa_family;
348 +  else
349 +    family = dst->sa.sa_family;
350 +
351 +  if (family == AF_INET6)
352 +    {
353 +      iphdr = &ip6;
354 +      ipsz = sizeof(ip6);
355 +      memset(&ip6, 0, sizeof(ip6));
356 +      
357 +      ip6.ip6_vfc = 6 << 4;
358 +      ip6.ip6_plen = htons(sizeof(struct udphdr) + len);
359 +      ip6.ip6_nxt = IPPROTO_UDP;
360 +      ip6.ip6_hops = 64;
361 +
362 +      if (src)
363 +       {
364 +         memcpy(&ip6.ip6_src, &src->in6.sin6_addr, IN6ADDRSZ);
365 +         udp.uh_sport = src->in6.sin6_port;
366 +       }
367 +      
368 +      if (dst)
369 +       {
370 +         memcpy(&ip6.ip6_dst, &dst->in6.sin6_addr, IN6ADDRSZ);
371 +         udp.uh_dport = dst->in6.sin6_port;
372 +       }
373 +            
374 +      /* start UDP checksum */
375 +      for (sum = 0, i = 0; i < IN6ADDRSZ; i++)
376 +       sum += ((u16 *)&ip6.ip6_src)[i];
377 +    }
378 +  else
379 +#endif
380 +    {
381 +      iphdr = &ip;
382 +      ipsz = sizeof(ip);
383 +      memset(&ip, 0, sizeof(ip));
384 +      
385 +      ip.ip_v = IPVERSION;
386 +      ip.ip_hl = sizeof(struct ip) / 4;
387 +      ip.ip_len = htons(sizeof(struct ip) + sizeof(struct udphdr) + len); 
388 +      ip.ip_ttl = IPDEFTTL;
389 +      ip.ip_p = IPPROTO_UDP;
390 +      
391 +      if (src)
392 +       {
393 +         ip.ip_src = src->in.sin_addr;
394 +         udp.uh_sport = src->in.sin_port;
395 +       }
396 +
397 +      if (dst)
398 +       {
399 +         ip.ip_dst = dst->in.sin_addr;
400 +         udp.uh_dport = dst->in.sin_port;
401 +       }
402 +      
403 +      ip.ip_sum = 0;
404 +      for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
405 +       sum += ((u16 *)&ip)[i];
406 +      while (sum >> 16)
407 +       sum = (sum & 0xffff) + (sum >> 16);  
408 +      ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
409 +      
410 +      /* start UDP checksum */
411 +      sum = ip.ip_src.s_addr & 0xffff;
412 +      sum += (ip.ip_src.s_addr >> 16) & 0xffff;
413 +      sum += ip.ip_dst.s_addr & 0xffff;
414 +      sum += (ip.ip_dst.s_addr >> 16) & 0xffff;
415 +    }
416 +  
417 +  if (len & 1)
418 +    ((unsigned char *)packet)[len] = 0; /* for checksum, in case length is odd. */
419 +
420 +  udp.uh_sum = 0;
421 +  udp.uh_ulen = htons(sizeof(struct udphdr) + len);
422 +  sum += htons(IPPROTO_UDP);
423 +  sum += htons(sizeof(struct udphdr) + len);
424 +  for (i = 0; i < sizeof(struct udphdr)/2; i++)
425 +    sum += ((u16 *)&udp)[i];
426 +  for (i = 0; i < (len + 1) / 2; i++)
427 +    sum += ((u16 *)packet)[i];
428 +  while (sum >> 16)
429 +    sum = (sum & 0xffff) + (sum >> 16);
430 +  udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
431 +
432 +  rc = gettimeofday(&time, NULL);
433 +  pcap_header.ts_sec = time.tv_sec;
434 +  pcap_header.ts_usec = time.tv_usec;
435 +  pcap_header.incl_len = pcap_header.orig_len = ipsz + sizeof(udp) + len;
436 +  
437 +  if (rc == -1 ||
438 +      !read_write(daemon->dumpfd, (void *)&pcap_header, sizeof(pcap_header), 0) ||
439 +      !read_write(daemon->dumpfd, iphdr, ipsz, 0) ||
440 +      !read_write(daemon->dumpfd, (void *)&udp, sizeof(udp), 0) ||
441 +      !read_write(daemon->dumpfd, (void *)packet, len, 0))
442 +    my_syslog(LOG_ERR, _("failed to write packet dump"));
443 +  else
444 +    my_syslog(LOG_INFO, _("dumping UDP packet %u mask 0x%04x"), ++packet_count, mask);
445 +
446 +}
447 +
448 +#endif
449 --- a/src/forward.c
450 +++ b/src/forward.c
451 @@ -508,6 +508,10 @@ static int forward_query(int udpfd, unio
452             
453               if (errno == 0)
454                 {
455 +#ifdef HAVE_DUMPFILE
456 +                 dump_packet(DUMP_UP_QUERY, (void *)header, plen, NULL, &start->addr);
457 +#endif
458 +                 
459                   /* Keep info in case we want to re-send this packet */
460                   daemon->srv_save = start;
461                   daemon->packet_len = plen;
462 @@ -769,7 +773,7 @@ void reply_query(int fd, int family, tim
463  #endif
464    
465    header = (struct dns_header *)daemon->packet;
466 -  
467 +
468    if (n < (int)sizeof(struct dns_header) || !(header->hb3 & HB3_QR))
469      return;
470    
471 @@ -796,6 +800,12 @@ void reply_query(int fd, int family, tim
472    if (!(forward = lookup_frec(ntohs(header->id), hash)))
473      return;
474    
475 +#ifdef HAVE_DUMPFILE
476 +  dump_packet((forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) ? DUMP_SEC_REPLY : DUMP_UP_REPLY,
477 +             (void *)header, n, &serveraddr, NULL);
478 +#endif
479 +  
480 +  
481    /* log_query gets called indirectly all over the place, so 
482       pass these in global variables - sorry. */
483    daemon->log_display_id = forward->log_id;
484 @@ -934,6 +944,11 @@ void reply_query(int fd, int family, tim
485                     status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class, 
486                                                    !option_bool(OPT_DNSSEC_IGN_NS) && (server->flags & SERV_DO_DNSSEC),
487                                                    NULL, NULL);
488 +#ifdef HAVE_DUMPFILE
489 +                 if (status == STAT_BOGUS)
490 +                   dump_packet((forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) ? DUMP_SEC_BOGUS : DUMP_BOGUS,
491 +                               header, (size_t)n, &serveraddr, NULL);
492 +#endif
493                 }
494               
495               /* Can't validate, as we're missing key data. Put this
496 @@ -1060,6 +1075,11 @@ void reply_query(int fd, int family, tim
497                                 setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
498                             }
499  #endif
500 +                         
501 +#ifdef HAVE_DUMPFILE
502 +                         dump_packet(DUMP_SEC_QUERY, (void *)header, (size_t)nn, NULL, &server->addr);
503 +#endif
504 +                         
505                           while (retry_send(sendto(fd, (char *)header, nn, 0, 
506                                                    &server->addr.sa, 
507                                                    sa_len(&server->addr)))); 
508 @@ -1114,8 +1134,8 @@ void reply_query(int fd, int family, tim
509               bogusanswer = 1;
510             }
511         }
512 -#endif     
513 -      
514 +#endif
515 +
516        /* restore CD bit to the value in the query */
517        if (forward->flags & FREC_CHECKING_DISABLED)
518         header->hb4 |= HB4_CD;
519 @@ -1141,6 +1161,11 @@ void reply_query(int fd, int family, tim
520               nn = resize_packet(header, nn, NULL, 0);
521             }
522  #endif
523 +
524 +#ifdef HAVE_DUMPFILE
525 +         dump_packet(DUMP_REPLY, daemon->packet, (size_t)nn, NULL, &forward->source);
526 +#endif
527 +         
528           send_from(forward->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, nn, 
529                     &forward->source, &forward->dest, forward->iface);
530         }
531 @@ -1394,7 +1419,11 @@ void receive_query(struct listener *list
532       pass these in global variables - sorry. */
533    daemon->log_display_id = ++daemon->log_id;
534    daemon->log_source_addr = &source_addr;
535 -  
536 +
537 +#ifdef HAVE_DUMPFILE
538 +  dump_packet(DUMP_QUERY, daemon->packet, (size_t)n, &source_addr, NULL);
539 +#endif
540 +         
541    if (extract_request(header, (size_t)n, daemon->namebuff, &type))
542      {
543  #ifdef HAVE_AUTH
544 --- a/src/option.c
545 +++ b/src/option.c
546 @@ -161,6 +161,8 @@ struct myoption {
547  #define LOPT_TFTP_MTU      349
548  #define LOPT_REPLY_DELAY   350
549  #define LOPT_RAPID_COMMIT  351
550 +#define LOPT_DUMPFILE      352
551 +#define LOPT_DUMPMASK      353
552   
553  #ifdef HAVE_GETOPT_LONG
554  static const struct option opts[] =  
555 @@ -327,6 +329,8 @@ static const struct myoption opts[] =
556      { "dhcp-ttl", 1, 0 , LOPT_DHCPTTL },
557      { "dhcp-reply-delay", 1, 0, LOPT_REPLY_DELAY },
558      { "dhcp-rapid-commit", 0, 0, LOPT_RAPID_COMMIT },
559 +    { "dumpfile", 1, 0, LOPT_DUMPFILE },
560 +    { "dumpmask", 1, 0, LOPT_DUMPMASK },
561      { NULL, 0, 0, 0 }
562    };
563  
564 @@ -500,6 +504,8 @@ static struct {
565    { LOPT_DHCPTTL, ARG_ONE, "<ttl>", gettext_noop("Set TTL in DNS responses with DHCP-derived addresses."), NULL }, 
566    { LOPT_REPLY_DELAY, ARG_ONE, "<integer>", gettext_noop("Delay DHCP replies for at least number of seconds."), NULL },
567    { LOPT_RAPID_COMMIT, OPT_RAPID_COMMIT, NULL, gettext_noop("Enables DHCPv4 Rapid Commit option."), NULL },
568 +  { LOPT_DUMPFILE, ARG_ONE, "<path>", gettext_noop("Path to debug packet dump file"), NULL },
569 +  { LOPT_DUMPMASK, ARG_ONE, "<hex>", gettext_noop("Mask which packets to dump"), NULL },
570    { 0, 0, NULL, NULL, NULL }
571  }; 
572  
573 @@ -1811,6 +1817,14 @@ static int one_opt(int option, char *arg
574         ret_err(_("bad MX target"));
575        break;
576  
577 +    case LOPT_DUMPFILE:  /* --dumpfile */
578 +      daemon->dump_file = opt_string_alloc(arg);
579 +      break;
580 +
581 +    case LOPT_DUMPMASK:  /* --dumpmask */
582 +      daemon->dump_mask = strtol(arg, NULL, 0);
583 +      break;
584 +      
585  #ifdef HAVE_DHCP      
586      case 'l':  /* --dhcp-leasefile */
587        daemon->lease_file = opt_string_alloc(arg);