blob: d1442f0f38d5f92f748df77743cd9bd1bb5503ba [file] [log] [blame]
Tarun Kundu12e3b2e2024-08-15 16:16:53 -07001/* dnsmasq is Copyright (c) 2000-2024 Simon Kelley
Simon Kelley6b173352018-05-08 18:32:14 +01002
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
Tarun Kundu12e3b2e2024-08-15 16:16:53 -070021#include <netinet/icmp6.h>
22
Simon Kelley6b173352018-05-08 18:32:14 +010023static u32 packet_count;
Tarun Kundu12e3b2e2024-08-15 16:16:53 -070024static void do_dump_packet(int mask, void *packet, size_t len,
25 union mysockaddr *src, union mysockaddr *dst, int port, int proto);
Simon Kelley6b173352018-05-08 18:32:14 +010026
27/* https://wiki.wireshark.org/Development/LibpcapFileFormat */
28struct 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
38struct 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
46void 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 ||
Simon Kelleycc5cc8f2018-06-02 14:45:17 +010071 !read_write(daemon->dumpfd, (void *)&header, sizeof(header), 1))
Simon Kelley6b173352018-05-08 18:32:14 +010072 die(_("cannot access %s: %s"), daemon->dump_file, EC_FILE);
Simon Kelleycc5cc8f2018-06-02 14:45:17 +010073 else if (header.magic_number != 0xa1b2c3d4)
74 die(_("bad header in %s"), daemon->dump_file, EC_FILE);
Simon Kelley6b173352018-05-08 18:32:14 +010075 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
Tarun Kundu12e3b2e2024-08-15 16:16:53 -070086void 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
116void 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
123static void do_dump_packet(int mask, void *packet, size_t len,
124 union mysockaddr *src, union mysockaddr *dst, int port, int proto)
Simon Kelley6b173352018-05-08 18:32:14 +0100125{
126 struct ip ip;
Simon Kelley6b173352018-05-08 18:32:14 +0100127 struct ip6_hdr ip6;
128 int family;
Simon Kelley6b173352018-05-08 18:32:14 +0100129 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;
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700141
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);
Simon Kelley6b173352018-05-08 18:32:14 +0100148
Simon Kelley6b173352018-05-08 18:32:14 +0100149 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;
Simon Kelley6b173352018-05-08 18:32:14 +0100161 ip6.ip6_hops = 64;
162
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700163 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
Simon Kelley6b173352018-05-08 18:32:14 +0100171 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 */
Simon Kelley1df73fe2020-03-05 17:41:04 +0000184 for (sum = 0, i = 0; i < IN6ADDRSZ; i+=2)
185 {
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700186 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])) ;
Simon Kelley1df73fe2020-03-05 17:41:04 +0000188 }
Simon Kelley6b173352018-05-08 18:32:14 +0100189 }
190 else
Simon Kelley6b173352018-05-08 18:32:14 +0100191 {
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;
Simon Kelley6b173352018-05-08 18:32:14 +0100198 ip.ip_ttl = IPDEFTTL;
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700199
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 }
Simon Kelley6b173352018-05-08 18:32:14 +0100207
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
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700227 /* start UDP/ICMP checksum */
Simon Kelley6b173352018-05-08 18:32:14 +0100228 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
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700237 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;
Simon Kelley6b173352018-05-08 18:32:14 +0100256
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700257 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
Simon Kelley6b173352018-05-08 18:32:14 +0100278 rc = gettimeofday(&time, NULL);
279 pcap_header.ts_sec = time.tv_sec;
280 pcap_header.ts_usec = time.tv_usec;
Simon Kelley6b173352018-05-08 18:32:14 +0100281
282 if (rc == -1 ||
283 !read_write(daemon->dumpfd, (void *)&pcap_header, sizeof(pcap_header), 0) ||
284 !read_write(daemon->dumpfd, iphdr, ipsz, 0) ||
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700285 (proto == IPPROTO_UDP && !read_write(daemon->dumpfd, (void *)&udp, sizeof(udp), 0)) ||
Simon Kelley6b173352018-05-08 18:32:14 +0100286 !read_write(daemon->dumpfd, (void *)packet, len, 0))
287 my_syslog(LOG_ERR, _("failed to write packet dump"));
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700288 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);
Simon Kelley6b173352018-05-08 18:32:14 +0100290 else
Tarun Kundu12e3b2e2024-08-15 16:16:53 -0700291 my_syslog(LOG_INFO, _("dumping packet %u mask 0x%04x"), ++packet_count, mask);
Simon Kelley6b173352018-05-08 18:32:14 +0100292
293}
294
295#endif