Simon Kelley | c8e8f5c | 2021-01-24 21:59:37 +0000 | [diff] [blame] | 1 | /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley |
Simon Kelley | 6b17335 | 2018-05-08 18:32:14 +0100 | [diff] [blame] | 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 | static u32 packet_count; |
| 22 | |
| 23 | /* https://wiki.wireshark.org/Development/LibpcapFileFormat */ |
| 24 | struct 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 | |
| 34 | struct 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 | |
| 42 | void 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 Kelley | cc5cc8f | 2018-06-02 14:45:17 +0100 | [diff] [blame] | 67 | !read_write(daemon->dumpfd, (void *)&header, sizeof(header), 1)) |
Simon Kelley | 6b17335 | 2018-05-08 18:32:14 +0100 | [diff] [blame] | 68 | die(_("cannot access %s: %s"), daemon->dump_file, EC_FILE); |
Simon Kelley | cc5cc8f | 2018-06-02 14:45:17 +0100 | [diff] [blame] | 69 | else if (header.magic_number != 0xa1b2c3d4) |
| 70 | die(_("bad header in %s"), daemon->dump_file, EC_FILE); |
Simon Kelley | 6b17335 | 2018-05-08 18:32:14 +0100 | [diff] [blame] | 71 | 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 | |
| 82 | void dump_packet(int mask, void *packet, size_t len, union mysockaddr *src, union mysockaddr *dst) |
| 83 | { |
| 84 | struct ip ip; |
Simon Kelley | 6b17335 | 2018-05-08 18:32:14 +0100 | [diff] [blame] | 85 | struct ip6_hdr ip6; |
| 86 | int family; |
Simon Kelley | 6b17335 | 2018-05-08 18:32:14 +0100 | [diff] [blame] | 87 | struct udphdr { |
| 88 | u16 uh_sport; /* source port */ |
| 89 | u16 uh_dport; /* destination port */ |
| 90 | u16 uh_ulen; /* udp length */ |
| 91 | u16 uh_sum; /* udp checksum */ |
| 92 | } udp; |
| 93 | struct pcaprec_hdr_s pcap_header; |
| 94 | struct timeval time; |
| 95 | u32 i, sum; |
| 96 | void *iphdr; |
| 97 | size_t ipsz; |
| 98 | int rc; |
| 99 | |
| 100 | if (daemon->dumpfd == -1 || !(mask & daemon->dump_mask)) |
| 101 | return; |
| 102 | |
| 103 | /* So wireshark can Id the packet. */ |
| 104 | udp.uh_sport = udp.uh_dport = htons(NAMESERVER_PORT); |
| 105 | |
Simon Kelley | 6b17335 | 2018-05-08 18:32:14 +0100 | [diff] [blame] | 106 | if (src) |
| 107 | family = src->sa.sa_family; |
| 108 | else |
| 109 | family = dst->sa.sa_family; |
| 110 | |
| 111 | if (family == AF_INET6) |
| 112 | { |
| 113 | iphdr = &ip6; |
| 114 | ipsz = sizeof(ip6); |
| 115 | memset(&ip6, 0, sizeof(ip6)); |
| 116 | |
| 117 | ip6.ip6_vfc = 6 << 4; |
| 118 | ip6.ip6_plen = htons(sizeof(struct udphdr) + len); |
| 119 | ip6.ip6_nxt = IPPROTO_UDP; |
| 120 | ip6.ip6_hops = 64; |
| 121 | |
| 122 | if (src) |
| 123 | { |
| 124 | memcpy(&ip6.ip6_src, &src->in6.sin6_addr, IN6ADDRSZ); |
| 125 | udp.uh_sport = src->in6.sin6_port; |
| 126 | } |
| 127 | |
| 128 | if (dst) |
| 129 | { |
| 130 | memcpy(&ip6.ip6_dst, &dst->in6.sin6_addr, IN6ADDRSZ); |
| 131 | udp.uh_dport = dst->in6.sin6_port; |
| 132 | } |
| 133 | |
| 134 | /* start UDP checksum */ |
Simon Kelley | 1df73fe | 2020-03-05 17:41:04 +0000 | [diff] [blame] | 135 | for (sum = 0, i = 0; i < IN6ADDRSZ; i+=2) |
| 136 | { |
| 137 | sum += ip6.ip6_src.s6_addr[i] + (ip6.ip6_src.s6_addr[i+1] << 8) ; |
| 138 | sum += ip6.ip6_dst.s6_addr[i] + (ip6.ip6_dst.s6_addr[i+1] << 8) ; |
| 139 | |
| 140 | } |
Simon Kelley | 6b17335 | 2018-05-08 18:32:14 +0100 | [diff] [blame] | 141 | } |
| 142 | else |
Simon Kelley | 6b17335 | 2018-05-08 18:32:14 +0100 | [diff] [blame] | 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 |