Imported Upstream version 2.88
[platform/upstream/dnsmasq.git] / src / dump.c
1 /* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
2
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; version 2 dated June, 1991, or
6    (at your option) version 3 dated 29 June, 2007.
7  
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12      
13    You should have received a copy of the GNU General Public License
14    along with this program.  If not, see <http://www.gnu.org/licenses/>.
15 */
16
17 #include "dnsmasq.h"
18
19 #ifdef HAVE_DUMPFILE
20
21 #include <netinet/icmp6.h>
22
23 static u32 packet_count;
24 static void do_dump_packet(int mask, void *packet, size_t len,
25                            union mysockaddr *src, union mysockaddr *dst, int port, int proto);
26
27 /* https://wiki.wireshark.org/Development/LibpcapFileFormat */
28 struct pcap_hdr_s {
29         u32 magic_number;   /* magic number */
30         u16 version_major;  /* major version number */
31         u16 version_minor;  /* minor version number */
32         u32 thiszone;       /* GMT to local correction */
33         u32 sigfigs;        /* accuracy of timestamps */
34         u32 snaplen;        /* max length of captured packets, in octets */
35         u32 network;        /* data link type */
36 };
37
38 struct pcaprec_hdr_s {
39         u32 ts_sec;         /* timestamp seconds */
40         u32 ts_usec;        /* timestamp microseconds */
41         u32 incl_len;       /* number of octets of packet saved in file */
42         u32 orig_len;       /* actual length of packet */
43 };
44
45
46 void dump_init(void)
47 {
48   struct stat buf;
49   struct pcap_hdr_s header;
50   struct pcaprec_hdr_s pcap_header;
51
52   packet_count = 0;
53   
54   if (stat(daemon->dump_file, &buf) == -1)
55     {
56       /* doesn't exist, create and add header */
57       header.magic_number = 0xa1b2c3d4;
58       header.version_major = 2;
59       header.version_minor = 4;
60       header.thiszone = 0;
61       header.sigfigs = 0;
62       header.snaplen = daemon->edns_pktsz + 200; /* slop for IP/UDP headers */
63       header.network = 101; /* DLT_RAW http://www.tcpdump.org/linktypes.html */
64
65       if (errno != ENOENT ||
66           (daemon->dumpfd = creat(daemon->dump_file, S_IRUSR | S_IWUSR)) == -1 ||
67           !read_write(daemon->dumpfd, (void *)&header, sizeof(header), 0))
68         die(_("cannot create %s: %s"), daemon->dump_file, EC_FILE);
69     }
70   else if ((daemon->dumpfd = open(daemon->dump_file, O_APPEND | O_RDWR)) == -1 ||
71            !read_write(daemon->dumpfd, (void *)&header, sizeof(header), 1))
72     die(_("cannot access %s: %s"), daemon->dump_file, EC_FILE);
73   else if (header.magic_number != 0xa1b2c3d4)
74     die(_("bad header in %s"), daemon->dump_file, EC_FILE);
75   else
76     {
77       /* count existing records */
78       while (read_write(daemon->dumpfd, (void *)&pcap_header, sizeof(pcap_header), 1))
79         {
80           lseek(daemon->dumpfd, pcap_header.incl_len, SEEK_CUR);
81           packet_count++;
82         }
83     }
84 }
85
86 void dump_packet_udp(int mask, void *packet, size_t len,
87                      union mysockaddr *src, union mysockaddr *dst, int fd)
88 {
89   union mysockaddr fd_addr;
90   socklen_t addr_len = sizeof(fd_addr);
91
92   if (daemon->dumpfd != -1 && (mask & daemon->dump_mask))
93      {
94        /* if fd is negative it carries a port number (negated) 
95           which we use as a source or destination when not otherwise
96           specified so wireshark can ID the packet. 
97           If both src and dst are specified, set this to -1 to avoid
98           a spurious getsockname() call. */
99        int port = (fd < 0) ? -fd : -1;
100        
101        /* fd >= 0 is a file descriptor and the address of that file descriptor is used
102           in place of a NULL src or dst. */
103        if (fd >= 0 && getsockname(fd, (struct sockaddr *)&fd_addr, &addr_len) != -1)
104          {
105            if (!src)
106              src = &fd_addr;
107            
108            if (!dst)
109              dst = &fd_addr;
110          }
111        
112        do_dump_packet(mask, packet, len, src, dst, port, IPPROTO_UDP);
113      }
114 }
115
116 void dump_packet_icmp(int mask, void *packet, size_t len,
117                       union mysockaddr *src, union mysockaddr *dst)
118 {
119   if (daemon->dumpfd != -1 && (mask & daemon->dump_mask))
120     do_dump_packet(mask, packet, len, src, dst, -1, IPPROTO_ICMP);
121 }
122
123 static void do_dump_packet(int mask, void *packet, size_t len,
124                            union mysockaddr *src, union mysockaddr *dst, int port, int proto)
125 {
126   struct ip ip;
127   struct ip6_hdr ip6;
128   int family;
129   struct udphdr {
130     u16 uh_sport;               /* source port */
131     u16 uh_dport;               /* destination port */
132     u16 uh_ulen;                /* udp length */
133     u16 uh_sum;                 /* udp checksum */
134   } udp;
135   struct pcaprec_hdr_s pcap_header;
136   struct timeval time;
137   u32 i, sum;
138   void *iphdr;
139   size_t ipsz;
140   int rc;
141      
142   /* if port != -1 it carries a port number 
143      which we use as a source or destination when not otherwise
144      specified so wireshark can ID the packet. 
145      If both src and dst are specified, set this to -1 to avoid
146      a spurious getsockname() call. */
147   udp.uh_sport = udp.uh_dport = htons(port < 0 ? 0 : port);
148   
149   if (src)
150     family = src->sa.sa_family;
151   else
152     family = dst->sa.sa_family;
153
154   if (family == AF_INET6)
155     {
156       iphdr = &ip6;
157       ipsz = sizeof(ip6);
158       memset(&ip6, 0, sizeof(ip6));
159       
160       ip6.ip6_vfc = 6 << 4;
161       ip6.ip6_hops = 64;
162
163       if ((ip6.ip6_nxt = proto) == IPPROTO_UDP)
164         ip6.ip6_plen = htons(sizeof(struct udphdr) + len);
165       else
166         {
167           proto = ip6.ip6_nxt = IPPROTO_ICMPV6;
168           ip6.ip6_plen = htons(len);
169         }
170       
171       if (src)
172         {
173           memcpy(&ip6.ip6_src, &src->in6.sin6_addr, IN6ADDRSZ);
174           udp.uh_sport = src->in6.sin6_port;
175         }
176       
177       if (dst)
178         {
179           memcpy(&ip6.ip6_dst, &dst->in6.sin6_addr, IN6ADDRSZ);
180           udp.uh_dport = dst->in6.sin6_port;
181         }
182             
183       /* start UDP checksum */
184       for (sum = 0, i = 0; i < IN6ADDRSZ; i+=2)
185         {
186           sum += ntohs((ip6.ip6_src.s6_addr[i] << 8) + (ip6.ip6_src.s6_addr[i+1])) ;
187           sum += ntohs((ip6.ip6_dst.s6_addr[i] << 8) + (ip6.ip6_dst.s6_addr[i+1])) ; 
188         }
189     }
190   else
191     {
192       iphdr = &ip;
193       ipsz = sizeof(ip);
194       memset(&ip, 0, sizeof(ip));
195       
196       ip.ip_v = IPVERSION;
197       ip.ip_hl = sizeof(struct ip) / 4;
198       ip.ip_ttl = IPDEFTTL;
199
200       if ((ip.ip_p = proto) == IPPROTO_UDP)
201         ip.ip_len = htons(sizeof(struct ip) + sizeof(struct udphdr) + len);
202       else
203         {
204           ip.ip_len = htons(sizeof(struct ip) + len);
205           proto = ip.ip_p = IPPROTO_ICMP;
206         }
207       
208       if (src)
209         {
210           ip.ip_src = src->in.sin_addr;
211           udp.uh_sport = src->in.sin_port;
212         }
213
214       if (dst)
215         {
216           ip.ip_dst = dst->in.sin_addr;
217           udp.uh_dport = dst->in.sin_port;
218         }
219       
220       ip.ip_sum = 0;
221       for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
222         sum += ((u16 *)&ip)[i];
223       while (sum >> 16)
224         sum = (sum & 0xffff) + (sum >> 16);  
225       ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
226       
227       /* start UDP/ICMP checksum */
228       sum = ip.ip_src.s_addr & 0xffff;
229       sum += (ip.ip_src.s_addr >> 16) & 0xffff;
230       sum += ip.ip_dst.s_addr & 0xffff;
231       sum += (ip.ip_dst.s_addr >> 16) & 0xffff;
232     }
233   
234   if (len & 1)
235     ((unsigned char *)packet)[len] = 0; /* for checksum, in case length is odd. */
236
237   if (proto == IPPROTO_UDP)
238     {
239       /* Add Remaining part of the pseudoheader. Note that though the
240          IPv6 pseudoheader is very different to the IPv4 one, the 
241          net result of this calculation is correct as long as the 
242          packet length is less than 65536, which is fine for us. */
243       sum += htons(IPPROTO_UDP);
244       sum += htons(sizeof(struct udphdr) + len);
245       
246       udp.uh_sum = 0;
247       udp.uh_ulen = htons(sizeof(struct udphdr) + len);
248       
249       for (i = 0; i < sizeof(struct udphdr)/2; i++)
250         sum += ((u16 *)&udp)[i];
251       for (i = 0; i < (len + 1) / 2; i++)
252         sum += ((u16 *)packet)[i];
253       while (sum >> 16)
254         sum = (sum & 0xffff) + (sum >> 16);
255       udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
256
257       pcap_header.incl_len = pcap_header.orig_len = ipsz + sizeof(udp) + len;
258     }
259   else
260     {
261       /* ICMP - ICMPv6 packet is a superset of ICMP */
262       struct icmp6_hdr *icmp = packet;
263       
264       /* See comment in UDP code above. */
265       sum += htons(proto);
266       sum += htons(len);
267       
268       icmp->icmp6_cksum = 0;
269       for (i = 0; i < (len + 1) / 2; i++)
270         sum += ((u16 *)packet)[i];
271       while (sum >> 16)
272         sum = (sum & 0xffff) + (sum >> 16);
273       icmp->icmp6_cksum = (sum == 0xffff) ? sum : ~sum;
274
275       pcap_header.incl_len = pcap_header.orig_len = ipsz + len;
276     }
277     
278   rc = gettimeofday(&time, NULL);
279   pcap_header.ts_sec = time.tv_sec;
280   pcap_header.ts_usec = time.tv_usec;
281   
282   if (rc == -1 ||
283       !read_write(daemon->dumpfd, (void *)&pcap_header, sizeof(pcap_header), 0) ||
284       !read_write(daemon->dumpfd, iphdr, ipsz, 0) ||
285       (proto == IPPROTO_UDP && !read_write(daemon->dumpfd, (void *)&udp, sizeof(udp), 0)) ||
286       !read_write(daemon->dumpfd, (void *)packet, len, 0))
287     my_syslog(LOG_ERR, _("failed to write packet dump"));
288   else if (option_bool(OPT_EXTRALOG))
289     my_syslog(LOG_INFO, _("%u dumping packet %u mask 0x%04x"),  daemon->log_display_id, ++packet_count, mask);
290   else
291     my_syslog(LOG_INFO, _("dumping packet %u mask 0x%04x"), ++packet_count, mask);
292
293 }
294
295 #endif