1 /* $NetBSD: npfd_log.c,v 1.12 2017/10/16 11:17:45 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 2015 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Mindaugas Rasiukevicius. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __RCSID("$NetBSD: npfd_log.c,v 1.12 2017/10/16 11:17:45 christos Exp $"); 34 35 #include <sys/types.h> 36 #include <sys/param.h> 37 #include <sys/stat.h> 38 39 #include <net/if.h> 40 41 #include <stdio.h> 42 #include <string.h> 43 #include <err.h> 44 #include <inttypes.h> 45 #include <limits.h> 46 #include <stdlib.h> 47 #include <unistd.h> 48 #include <syslog.h> 49 #include <stdbool.h> 50 51 #include <pcap/pcap.h> 52 #include "npfd.h" 53 54 struct npfd_log { 55 char ifname[IFNAMSIZ]; 56 char path[MAXPATHLEN]; 57 char *filter; 58 int snaplen; 59 pcap_t *pcap; 60 pcap_dumper_t *dumper; 61 }; 62 63 static void 64 npfd_log_setfilter(npfd_log_t *ctx) 65 { 66 struct bpf_program bprog; 67 68 if (ctx->filter == NULL) 69 return; 70 71 if (pcap_compile(ctx->pcap, &bprog, ctx->filter, 1, 0) == -1) 72 errx(EXIT_FAILURE, "pcap_compile failed for `%s': %s", 73 ctx->filter, pcap_geterr(ctx->pcap)); 74 if (pcap_setfilter(ctx->pcap, &bprog) == -1) 75 errx(EXIT_FAILURE, "pcap_setfilter failed: %s", 76 pcap_geterr(ctx->pcap)); 77 pcap_freecode(&bprog); 78 } 79 80 static FILE * 81 npfd_log_gethdr(npfd_log_t *ctx, struct pcap_file_header*hdr) 82 { 83 FILE *fp = fopen(ctx->path, "r"); 84 85 hdr->magic = 0; 86 if (fp == NULL) 87 return NULL; 88 89 #define TCPDUMP_MAGIC 0xa1b2c3d4 90 91 switch (fread(hdr, sizeof(*hdr), 1, fp)) { 92 case 0: 93 hdr->magic = 0; 94 fclose(fp); 95 return NULL; 96 case 1: 97 if (hdr->magic != TCPDUMP_MAGIC || 98 hdr->version_major != PCAP_VERSION_MAJOR || 99 hdr->version_minor != PCAP_VERSION_MINOR || 100 hdr->sigfigs != (u_int)pcap_get_tstamp_precision(ctx->pcap)) 101 goto out; 102 break; 103 default: 104 goto out; 105 } 106 107 return fp; 108 out: 109 fclose(fp); 110 hdr->magic = (uint32_t)-1; 111 return NULL; 112 } 113 114 static int 115 npfd_log_getsnaplen(npfd_log_t *ctx) 116 { 117 struct pcap_file_header hdr; 118 FILE *fp = npfd_log_gethdr(ctx, &hdr); 119 if (fp == NULL) 120 return hdr.magic == (uint32_t)-1 ? -1 : 0; 121 fclose(fp); 122 return hdr.snaplen; 123 } 124 125 static int 126 npfd_log_validate(npfd_log_t *ctx) 127 { 128 struct pcap_file_header hdr; 129 FILE *fp = npfd_log_gethdr(ctx, &hdr); 130 size_t o, no; 131 132 if (fp == NULL) { 133 if (hdr.magic == 0) 134 return 0; 135 goto rename; 136 } 137 138 struct stat st; 139 if (fstat(fileno(fp), &st) == -1) 140 goto rename; 141 142 size_t count = 0; 143 for (o = sizeof(hdr);; count++) { 144 struct { 145 uint32_t sec; 146 uint32_t usec; 147 uint32_t caplen; 148 uint32_t len; 149 } pkt; 150 switch (fread(&pkt, sizeof(pkt), 1, fp)) { 151 case 0: 152 syslog(LOG_INFO, "%zu packets read from `%s'", count, 153 ctx->path); 154 fclose(fp); 155 return hdr.snaplen; 156 case 1: 157 no = o + sizeof(pkt) + pkt.caplen; 158 if (pkt.caplen > hdr.snaplen) 159 goto fix; 160 if (no > (size_t)st.st_size) 161 goto fix; 162 if (fseeko(fp, pkt.caplen, SEEK_CUR) != 0) 163 goto fix; 164 o = no; 165 break; 166 default: 167 goto fix; 168 } 169 } 170 171 fix: 172 fclose(fp); 173 no = st.st_size - o; 174 syslog(LOG_INFO, "%zu packets read from `%s', %zu extra bytes", 175 count, ctx->path, no); 176 if (no < 10240) { 177 syslog(LOG_WARNING, 178 "Incomplete last packet in `%s', truncating", 179 ctx->path); 180 if (truncate(ctx->path, (off_t)o) == -1) { 181 syslog(LOG_ERR, "Cannot truncate `%s': %m", ctx->path); 182 goto rename; 183 } 184 } else { 185 syslog(LOG_ERR, "Corrupt file `%s'", ctx->path); 186 goto rename; 187 } 188 fclose(fp); 189 return hdr.snaplen; 190 rename: 191 fclose(fp); 192 char tmp[MAXPATHLEN]; 193 snprintf(tmp, sizeof(tmp), "%s.XXXXXX", ctx->path); 194 int fd; 195 if ((fd = mkstemp(tmp)) == -1) { 196 syslog(LOG_ERR, "Can't make temp file `%s': %m", tmp); 197 return -1; 198 } 199 close(fd); 200 if (rename(ctx->path, tmp) == -1) { 201 syslog(LOG_ERR, "Can't rename `%s' to `%s': %m", 202 ctx->path, tmp); 203 return -1; 204 } 205 syslog(LOG_ERR, "Renamed to `%s'", tmp); 206 return 0; 207 } 208 209 210 npfd_log_t * 211 npfd_log_create(const char *filename, const char *ifname, const char *filter, 212 int snaplen) 213 { 214 npfd_log_t *ctx; 215 216 if ((ctx = calloc(1, sizeof(*ctx))) == NULL) 217 err(EXIT_FAILURE, "malloc failed"); 218 219 snprintf(ctx->ifname, sizeof(ctx->ifname), "%s", ifname); 220 if (filename == NULL) 221 snprintf(ctx->path, sizeof(ctx->path), NPFD_LOG_PATH "/%s.pcap", 222 ctx->ifname); 223 else 224 snprintf(ctx->path, sizeof(ctx->path), "%s", filename); 225 226 if (filter != NULL) { 227 ctx->filter = strdup(filter); 228 if (ctx->filter == NULL) 229 err(EXIT_FAILURE, "malloc failed"); 230 } 231 ctx->snaplen = snaplen; 232 233 /* Open a live capture handle in non-blocking mode. */ 234 npfd_log_pcap_reopen(ctx); 235 236 /* Open the log file */ 237 npfd_log_file_reopen(ctx, false); 238 return ctx; 239 } 240 241 242 bool 243 npfd_log_pcap_reopen(npfd_log_t *ctx) 244 { 245 char errbuf[PCAP_ERRBUF_SIZE]; 246 int snaplen = ctx->snaplen; 247 248 if (ctx->pcap != NULL) 249 pcap_close(ctx->pcap); 250 else 251 syslog(LOG_INFO, "reopening pcap socket"); 252 253 ctx->pcap = pcap_create(ctx->ifname, errbuf); 254 if (ctx->pcap == NULL) 255 errx(EXIT_FAILURE, "pcap_create failed: %s", errbuf); 256 257 if (pcap_setnonblock(ctx->pcap, 1, errbuf) == -1) 258 errx(EXIT_FAILURE, "pcap_setnonblock failed: %s", errbuf); 259 260 int sl = npfd_log_getsnaplen(ctx); 261 if (sl == -1) 262 errx(EXIT_FAILURE, "corrupt log file `%s'", ctx->path); 263 264 if (sl != 0 && sl != snaplen) { 265 warnx("Overriding snaplen from %d to %d from `%s'", snaplen, 266 sl, ctx->path); 267 snaplen = sl; 268 } 269 270 if (pcap_set_snaplen(ctx->pcap, snaplen) == -1) 271 errx(EXIT_FAILURE, "pcap_set_snaplen failed: %s", 272 pcap_geterr(ctx->pcap)); 273 274 if (pcap_set_timeout(ctx->pcap, 1000) == -1) 275 errx(EXIT_FAILURE, "pcap_set_timeout failed: %s", 276 pcap_geterr(ctx->pcap)); 277 278 if (pcap_activate(ctx->pcap) == -1) 279 errx(EXIT_FAILURE, "pcap_activate failed: %s", 280 pcap_geterr(ctx->pcap)); 281 282 npfd_log_setfilter(ctx); 283 return true; 284 } 285 286 bool 287 npfd_log_file_reopen(npfd_log_t *ctx, bool die) 288 { 289 mode_t omask = umask(077); 290 291 if (ctx->dumper) 292 pcap_dump_close(ctx->dumper); 293 /* 294 * Open a log file to write for a given interface and dump there. 295 */ 296 switch (npfd_log_validate(ctx)) { 297 case -1: 298 syslog(LOG_ERR, "Giving up"); 299 exit(EXIT_FAILURE); 300 /*NOTREACHED*/ 301 case 0: 302 ctx->dumper = pcap_dump_open(ctx->pcap, ctx->path); 303 break; 304 default: 305 ctx->dumper = pcap_dump_open_append(ctx->pcap, ctx->path); 306 break; 307 } 308 (void)umask(omask); 309 310 if (ctx->dumper == NULL) { 311 if (die) 312 errx(EXIT_FAILURE, "pcap_dump_open failed for `%s': %s", 313 ctx->path, pcap_geterr(ctx->pcap)); 314 syslog(LOG_ERR, "pcap_dump_open failed for `%s': %s", 315 ctx->path, pcap_geterr(ctx->pcap)); 316 return false; 317 } 318 return true; 319 } 320 321 void 322 npfd_log_destroy(npfd_log_t *ctx) 323 { 324 if (ctx->dumper) 325 pcap_dump_close(ctx->dumper); 326 if (ctx->pcap) 327 pcap_close(ctx->pcap); 328 free(ctx); 329 } 330 331 int 332 npfd_log_getsock(npfd_log_t *ctx) 333 { 334 return pcap_get_selectable_fd(ctx->pcap); 335 } 336 337 void 338 npfd_log_flush(npfd_log_t *ctx) 339 { 340 if (!ctx->dumper) 341 return; 342 if (pcap_dump_flush(ctx->dumper) == -1) 343 syslog(LOG_ERR, "pcap_dump_flush failed for `%s': %m", 344 ctx->path); 345 } 346 347 348 int 349 npfd_log(npfd_log_t *ctx) 350 { 351 pcap_dumper_t *dumper = ctx->dumper; 352 353 return pcap_dispatch(ctx->pcap, PCAP_NPACKETS, pcap_dump, 354 (void *)dumper); 355 } 356 357 void 358 npfd_log_stats(npfd_log_t *ctx) 359 { 360 pcap_t *pcap = ctx->pcap; 361 struct pcap_stat ps; 362 363 if (pcap_stats(pcap, &ps) == -1) { 364 syslog(LOG_ERR, "pcap_stats failed: %s", pcap_geterr(pcap)); 365 return; 366 } 367 syslog(LOG_INFO, "packet statistics: %s: %u received, %u dropped", 368 ctx->ifname, ps.ps_recv, ps.ps_drop); 369 } 370