blob: 1dae1c4235cf253f1fd91f406c8d396419882580 [file] [log] [blame]
Simon Kelley6b173352018-05-08 18:32:14 +01001/* dnsmasq is Copyright (c) 2000-2018 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
21static u32 packet_count;
22
23/* https://wiki.wireshark.org/Development/LibpcapFileFormat */
24struct pcap_hdr_s {
25 u32 magic_number; /* magic number */
26 u16 version_major; /* major version number */
27 u16 version_minor; /* minor version number */
28 u32 thiszone; /* GMT to local correction */
29 u32 sigfigs; /* accuracy of timestamps */
30 u32 snaplen; /* max length of captured packets, in octets */
31 u32 network; /* data link type */
32};
33
34struct pcaprec_hdr_s {
35 u32 ts_sec; /* timestamp seconds */
36 u32 ts_usec; /* timestamp microseconds */
37 u32 incl_len; /* number of octets of packet saved in file */
38 u32 orig_len; /* actual length of packet */
39};
40
41
42void dump_init(void)
43{
44 struct stat buf;
45 struct pcap_hdr_s header;
46 struct pcaprec_hdr_s pcap_header;
47
48 packet_count = 0;
49
50 if (stat(daemon->dump_file, &buf) == -1)
51 {
52 /* doesn't exist, create and add header */
53 header.magic_number = 0xa1b2c3d4;
54 header.version_major = 2;
55 header.version_minor = 4;
56 header.thiszone = 0;
57 header.sigfigs = 0;
58 header.snaplen = daemon->edns_pktsz + 200; /* slop for IP/UDP headers */
59 header.network = 101; /* DLT_RAW http://www.tcpdump.org/linktypes.html */
60
61 if (errno != ENOENT ||
62 (daemon->dumpfd = creat(daemon->dump_file, S_IRUSR | S_IWUSR)) == -1 ||
63 !read_write(daemon->dumpfd, (void *)&header, sizeof(header), 0))
64 die(_("cannot create %s: %s"), daemon->dump_file, EC_FILE);
65 }
66 else if ((daemon->dumpfd = open(daemon->dump_file, O_APPEND | O_RDWR)) == -1 ||
Simon Kelleycc5cc8f2018-06-02 14:45:17 +010067 !read_write(daemon->dumpfd, (void *)&header, sizeof(header), 1))
Simon Kelley6b173352018-05-08 18:32:14 +010068 die(_("cannot access %s: %s"), daemon->dump_file, EC_FILE);
Simon Kelleycc5cc8f2018-06-02 14:45:17 +010069 else if (header.magic_number != 0xa1b2c3d4)
70 die(_("bad header in %s"), daemon->dump_file, EC_FILE);
Simon Kelley6b173352018-05-08 18:32:14 +010071 else
72 {
73 /* count existing records */
74 while (read_write(daemon->dumpfd, (void *)&pcap_header, sizeof(pcap_header), 1))
75 {
76 lseek(daemon->dumpfd, pcap_header.incl_len, SEEK_CUR);
77 packet_count++;
78 }
79 }
80}
81
82void dump_packet(int mask, void *packet, size_t len, union mysockaddr *src, union mysockaddr *dst)
83{
84 struct ip ip;
85#ifdef HAVE_IPV6
86 struct ip6_hdr ip6;
87 int family;
88#endif
89 struct udphdr {
90 u16 uh_sport; /* source port */
91 u16 uh_dport; /* destination port */
92 u16 uh_ulen; /* udp length */
93 u16 uh_sum; /* udp checksum */
94 } udp;
95 struct pcaprec_hdr_s pcap_header;
96 struct timeval time;
97 u32 i, sum;
98 void *iphdr;
99 size_t ipsz;
100 int rc;
101
102 if (daemon->dumpfd == -1 || !(mask & daemon->dump_mask))
103 return;
104
105 /* So wireshark can Id the packet. */
106 udp.uh_sport = udp.uh_dport = htons(NAMESERVER_PORT);
107
108#ifdef HAVE_IPV6
109 if (src)
110 family = src->sa.sa_family;
111 else
112 family = dst->sa.sa_family;
113
114 if (family == AF_INET6)
115 {
116 iphdr = &ip6;
117 ipsz = sizeof(ip6);
118 memset(&ip6, 0, sizeof(ip6));
119
120 ip6.ip6_vfc = 6 << 4;
121 ip6.ip6_plen = htons(sizeof(struct udphdr) + len);
122 ip6.ip6_nxt = IPPROTO_UDP;
123 ip6.ip6_hops = 64;
124
125 if (src)
126 {
127 memcpy(&ip6.ip6_src, &src->in6.sin6_addr, IN6ADDRSZ);
128 udp.uh_sport = src->in6.sin6_port;
129 }
130
131 if (dst)
132 {
133 memcpy(&ip6.ip6_dst, &dst->in6.sin6_addr, IN6ADDRSZ);
134 udp.uh_dport = dst->in6.sin6_port;
135 }
136
137 /* start UDP checksum */
138 for (sum = 0, i = 0; i < IN6ADDRSZ; i++)
139 sum += ((u16 *)&ip6.ip6_src)[i];
140 }
141 else
142#endif
143 {
144 iphdr = &ip;
145 ipsz = sizeof(ip);
146 memset(&ip, 0, sizeof(ip));
147
148 ip.ip_v = IPVERSION;
149 ip.ip_hl = sizeof(struct ip) / 4;
150 ip.ip_len = htons(sizeof(struct ip) + sizeof(struct udphdr) + len);
151 ip.ip_ttl = IPDEFTTL;
152 ip.ip_p = IPPROTO_UDP;
153
154 if (src)
155 {
156 ip.ip_src = src->in.sin_addr;
157 udp.uh_sport = src->in.sin_port;
158 }
159
160 if (dst)
161 {
162 ip.ip_dst = dst->in.sin_addr;
163 udp.uh_dport = dst->in.sin_port;
164 }
165
166 ip.ip_sum = 0;
167 for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
168 sum += ((u16 *)&ip)[i];
169 while (sum >> 16)
170 sum = (sum & 0xffff) + (sum >> 16);
171 ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
172
173 /* start UDP checksum */
174 sum = ip.ip_src.s_addr & 0xffff;
175 sum += (ip.ip_src.s_addr >> 16) & 0xffff;
176 sum += ip.ip_dst.s_addr & 0xffff;
177 sum += (ip.ip_dst.s_addr >> 16) & 0xffff;
178 }
179
180 if (len & 1)
181 ((unsigned char *)packet)[len] = 0; /* for checksum, in case length is odd. */
182
183 udp.uh_sum = 0;
184 udp.uh_ulen = htons(sizeof(struct udphdr) + len);
185 sum += htons(IPPROTO_UDP);
186 sum += htons(sizeof(struct udphdr) + len);
187 for (i = 0; i < sizeof(struct udphdr)/2; i++)
188 sum += ((u16 *)&udp)[i];
189 for (i = 0; i < (len + 1) / 2; i++)
190 sum += ((u16 *)packet)[i];
191 while (sum >> 16)
192 sum = (sum & 0xffff) + (sum >> 16);
193 udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
194
195 rc = gettimeofday(&time, NULL);
196 pcap_header.ts_sec = time.tv_sec;
197 pcap_header.ts_usec = time.tv_usec;
198 pcap_header.incl_len = pcap_header.orig_len = ipsz + sizeof(udp) + len;
199
200 if (rc == -1 ||
201 !read_write(daemon->dumpfd, (void *)&pcap_header, sizeof(pcap_header), 0) ||
202 !read_write(daemon->dumpfd, iphdr, ipsz, 0) ||
203 !read_write(daemon->dumpfd, (void *)&udp, sizeof(udp), 0) ||
204 !read_write(daemon->dumpfd, (void *)packet, len, 0))
205 my_syslog(LOG_ERR, _("failed to write packet dump"));
206 else
207 my_syslog(LOG_INFO, _("dumping UDP packet %u mask 0x%04x"), ++packet_count, mask);
208
209}
210
211#endif