1 /* 2 * Copyright (c) 2012 Gleb Smirnoff <glebius@FreeBSD.org> 3 * Copyright (c) 2002 Michael Shalayeff 4 * Copyright (c) 2001 Daniel Hartmeier 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT, 20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 25 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 26 * THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 * $OpenBSD: print-pfsync.c,v 1.38 2012/09/19 13:50:36 mikeb Exp $ 29 * $OpenBSD: pf_print_state.c,v 1.11 2012/07/08 17:48:37 lteo Exp $ 30 */ 31 32 #ifdef HAVE_CONFIG_H 33 #include "config.h" 34 #endif 35 36 #ifndef HAVE_NET_PFVAR_H 37 #error "No pf headers available" 38 #endif 39 #include <sys/endian.h> 40 #include <net/if.h> 41 #include <net/pfvar.h> 42 #include <net/if_pfsync.h> 43 #define TCPSTATES 44 #include <netinet/tcp_fsm.h> 45 46 #include <netdissect-stdinc.h> 47 #include <string.h> 48 49 #include "netdissect.h" 50 #include "interface.h" 51 #include "addrtoname.h" 52 53 static void pfsync_print(netdissect_options *, struct pfsync_header *, 54 const u_char *, u_int); 55 static void print_src_dst(netdissect_options *, 56 const struct pfsync_state_peer *, 57 const struct pfsync_state_peer *, uint8_t); 58 static void print_state(netdissect_options *, struct pfsync_state *); 59 60 u_int 61 pfsync_if_print(netdissect_options *ndo, const struct pcap_pkthdr *h, 62 register const u_char *p) 63 { 64 u_int caplen = h->caplen; 65 66 ts_print(ndo, &h->ts); 67 68 if (caplen < PFSYNC_HDRLEN) { 69 ND_PRINT((ndo, "[|pfsync]")); 70 goto out; 71 } 72 73 pfsync_print(ndo, (struct pfsync_header *)p, 74 p + sizeof(struct pfsync_header), 75 caplen - sizeof(struct pfsync_header)); 76 out: 77 if (ndo->ndo_xflag) { 78 hex_print(ndo, "\n\t", p, caplen); 79 } 80 safeputchar(ndo, '\n'); 81 return (caplen); 82 } 83 84 void 85 pfsync_ip_print(netdissect_options *ndo , const u_char *bp, u_int len) 86 { 87 struct pfsync_header *hdr = (struct pfsync_header *)bp; 88 89 if (len < PFSYNC_HDRLEN) 90 ND_PRINT((ndo, "[|pfsync]")); 91 else 92 pfsync_print(ndo, hdr, bp + sizeof(struct pfsync_header), 93 len - sizeof(struct pfsync_header)); 94 } 95 96 struct pfsync_actions { 97 const char *name; 98 size_t len; 99 void (*print)(netdissect_options *, const void *); 100 }; 101 102 static void pfsync_print_clr(netdissect_options *, const void *); 103 static void pfsync_print_state(netdissect_options *, const void *); 104 static void pfsync_print_ins_ack(netdissect_options *, const void *); 105 static void pfsync_print_upd_c(netdissect_options *, const void *); 106 static void pfsync_print_upd_req(netdissect_options *, const void *); 107 static void pfsync_print_del_c(netdissect_options *, const void *); 108 static void pfsync_print_bus(netdissect_options *, const void *); 109 static void pfsync_print_tdb(netdissect_options *, const void *); 110 111 struct pfsync_actions actions[] = { 112 { "clear all", sizeof(struct pfsync_clr), pfsync_print_clr }, 113 { "insert", sizeof(struct pfsync_state), pfsync_print_state }, 114 { "insert ack", sizeof(struct pfsync_ins_ack), pfsync_print_ins_ack }, 115 { "update", sizeof(struct pfsync_ins_ack), pfsync_print_state }, 116 { "update compressed", sizeof(struct pfsync_upd_c), 117 pfsync_print_upd_c }, 118 { "request uncompressed", sizeof(struct pfsync_upd_req), 119 pfsync_print_upd_req }, 120 { "delete", sizeof(struct pfsync_state), pfsync_print_state }, 121 { "delete compressed", sizeof(struct pfsync_del_c), 122 pfsync_print_del_c }, 123 { "frag insert", 0, NULL }, 124 { "frag delete", 0, NULL }, 125 { "bulk update status", sizeof(struct pfsync_bus), 126 pfsync_print_bus }, 127 { "tdb", 0, pfsync_print_tdb }, 128 { "eof", 0, NULL }, 129 }; 130 131 static void 132 pfsync_print(netdissect_options *ndo, struct pfsync_header *hdr, 133 const u_char *bp, u_int len) 134 { 135 struct pfsync_subheader *subh; 136 int count, plen, i; 137 u_int alen; 138 139 plen = ntohs(hdr->len); 140 141 ND_PRINT((ndo, "PFSYNCv%d len %d", hdr->version, plen)); 142 143 if (hdr->version != PFSYNC_VERSION) 144 return; 145 146 plen -= sizeof(*hdr); 147 148 while (plen > 0) { 149 if (len < sizeof(*subh)) 150 break; 151 152 subh = (struct pfsync_subheader *)bp; 153 bp += sizeof(*subh); 154 len -= sizeof(*subh); 155 plen -= sizeof(*subh); 156 157 if (subh->action >= PFSYNC_ACT_MAX) { 158 ND_PRINT((ndo, "\n act UNKNOWN id %d", 159 subh->action)); 160 return; 161 } 162 163 count = ntohs(subh->count); 164 ND_PRINT((ndo, "\n %s count %d", actions[subh->action].name, 165 count)); 166 alen = actions[subh->action].len; 167 168 if (subh->action == PFSYNC_ACT_EOF) 169 return; 170 171 if (actions[subh->action].print == NULL) { 172 ND_PRINT((ndo, "\n unimplemented action %hhu", 173 subh->action)); 174 return; 175 } 176 177 for (i = 0; i < count; i++) { 178 if (len < alen) { 179 len = 0; 180 break; 181 } 182 183 if (ndo->ndo_vflag) 184 actions[subh->action].print(ndo, bp); 185 186 bp += alen; 187 len -= alen; 188 plen -= alen; 189 } 190 } 191 192 if (plen > 0) { 193 ND_PRINT((ndo, "\n ...")); 194 return; 195 } 196 if (plen < 0) { 197 ND_PRINT((ndo, "\n invalid header length")); 198 return; 199 } 200 if (len > 0) 201 ND_PRINT((ndo, "\n invalid packet length")); 202 } 203 204 static void 205 pfsync_print_clr(netdissect_options *ndo, const void *bp) 206 { 207 const struct pfsync_clr *clr = bp; 208 209 ND_PRINT((ndo, "\n\tcreatorid: %08x", htonl(clr->creatorid))); 210 if (clr->ifname[0] != '\0') 211 ND_PRINT((ndo, " interface: %s", clr->ifname)); 212 } 213 214 static void 215 pfsync_print_state(netdissect_options *ndo, const void *bp) 216 { 217 struct pfsync_state *st = (struct pfsync_state *)bp; 218 219 safeputchar(ndo, '\n'); 220 print_state(ndo, st); 221 } 222 223 static void 224 pfsync_print_ins_ack(netdissect_options *ndo, const void *bp) 225 { 226 const struct pfsync_ins_ack *iack = bp; 227 228 ND_PRINT((ndo, "\n\tid: %016jx creatorid: %08x", 229 (uintmax_t)be64toh(iack->id), ntohl(iack->creatorid))); 230 } 231 232 static void 233 pfsync_print_upd_c(netdissect_options *ndo, const void *bp) 234 { 235 const struct pfsync_upd_c *u = bp; 236 237 ND_PRINT((ndo, "\n\tid: %016jx creatorid: %08x", 238 (uintmax_t)be64toh(u->id), ntohl(u->creatorid))); 239 if (ndo->ndo_vflag > 2) { 240 ND_PRINT((ndo, "\n\tTCP? :")); 241 print_src_dst(ndo, &u->src, &u->dst, IPPROTO_TCP); 242 } 243 } 244 245 static void 246 pfsync_print_upd_req(netdissect_options *ndo, const void *bp) 247 { 248 const struct pfsync_upd_req *ur = bp; 249 250 ND_PRINT((ndo, "\n\tid: %016jx creatorid: %08x", 251 (uintmax_t)be64toh(ur->id), ntohl(ur->creatorid))); 252 } 253 254 static void 255 pfsync_print_del_c(netdissect_options *ndo, const void *bp) 256 { 257 const struct pfsync_del_c *d = bp; 258 259 ND_PRINT((ndo, "\n\tid: %016jx creatorid: %08x", 260 (uintmax_t)be64toh(d->id), ntohl(d->creatorid))); 261 } 262 263 static void 264 pfsync_print_bus(netdissect_options *ndo, const void *bp) 265 { 266 const struct pfsync_bus *b = bp; 267 uint32_t endtime; 268 int min, sec; 269 const char *status; 270 271 endtime = ntohl(b->endtime); 272 sec = endtime % 60; 273 endtime /= 60; 274 min = endtime % 60; 275 endtime /= 60; 276 277 switch (b->status) { 278 case PFSYNC_BUS_START: 279 status = "start"; 280 break; 281 case PFSYNC_BUS_END: 282 status = "end"; 283 break; 284 default: 285 status = "UNKNOWN"; 286 break; 287 } 288 289 ND_PRINT((ndo, "\n\tcreatorid: %08x age: %.2u:%.2u:%.2u status: %s", 290 htonl(b->creatorid), endtime, min, sec, status)); 291 } 292 293 static void 294 pfsync_print_tdb(netdissect_options *ndo, const void *bp) 295 { 296 const struct pfsync_tdb *t = bp; 297 298 ND_PRINT((ndo, "\n\tspi: 0x%08x rpl: %ju cur_bytes: %ju", 299 ntohl(t->spi), (uintmax_t )be64toh(t->rpl), 300 (uintmax_t )be64toh(t->cur_bytes))); 301 } 302 303 static void 304 print_host(netdissect_options *ndo, struct pf_addr *addr, uint16_t port, 305 sa_family_t af, const char *proto) 306 { 307 char buf[48]; 308 309 if (inet_ntop(af, addr, buf, sizeof(buf)) == NULL) 310 ND_PRINT((ndo, "?")); 311 else 312 ND_PRINT((ndo, "%s", buf)); 313 314 if (port) 315 ND_PRINT((ndo, ".%hu", ntohs(port))); 316 } 317 318 static void 319 print_seq(netdissect_options *ndo, const struct pfsync_state_peer *p) 320 { 321 if (p->seqdiff) 322 ND_PRINT((ndo, "[%u + %u](+%u)", ntohl(p->seqlo), 323 ntohl(p->seqhi) - ntohl(p->seqlo), ntohl(p->seqdiff))); 324 else 325 ND_PRINT((ndo, "[%u + %u]", ntohl(p->seqlo), 326 ntohl(p->seqhi) - ntohl(p->seqlo))); 327 } 328 329 static void 330 print_src_dst(netdissect_options *ndo, const struct pfsync_state_peer *src, 331 const struct pfsync_state_peer *dst, uint8_t proto) 332 { 333 334 if (proto == IPPROTO_TCP) { 335 if (src->state <= TCPS_TIME_WAIT && 336 dst->state <= TCPS_TIME_WAIT) 337 ND_PRINT((ndo, " %s:%s", tcpstates[src->state], 338 tcpstates[dst->state])); 339 else if (src->state == PF_TCPS_PROXY_SRC || 340 dst->state == PF_TCPS_PROXY_SRC) 341 ND_PRINT((ndo, " PROXY:SRC")); 342 else if (src->state == PF_TCPS_PROXY_DST || 343 dst->state == PF_TCPS_PROXY_DST) 344 ND_PRINT((ndo, " PROXY:DST")); 345 else 346 ND_PRINT((ndo, " <BAD STATE LEVELS %u:%u>", 347 src->state, dst->state)); 348 if (ndo->ndo_vflag > 1) { 349 ND_PRINT((ndo, "\n\t")); 350 print_seq(ndo, src); 351 if (src->wscale && dst->wscale) 352 ND_PRINT((ndo, " wscale %u", 353 src->wscale & PF_WSCALE_MASK)); 354 ND_PRINT((ndo, " ")); 355 print_seq(ndo, dst); 356 if (src->wscale && dst->wscale) 357 ND_PRINT((ndo, " wscale %u", 358 dst->wscale & PF_WSCALE_MASK)); 359 } 360 } else if (proto == IPPROTO_UDP && src->state < PFUDPS_NSTATES && 361 dst->state < PFUDPS_NSTATES) { 362 const char *states[] = PFUDPS_NAMES; 363 364 ND_PRINT((ndo, " %s:%s", states[src->state], states[dst->state])); 365 } else if (proto != IPPROTO_ICMP && src->state < PFOTHERS_NSTATES && 366 dst->state < PFOTHERS_NSTATES) { 367 /* XXX ICMP doesn't really have state levels */ 368 const char *states[] = PFOTHERS_NAMES; 369 370 ND_PRINT((ndo, " %s:%s", states[src->state], states[dst->state])); 371 } else { 372 ND_PRINT((ndo, " %u:%u", src->state, dst->state)); 373 } 374 } 375 376 static void 377 print_state(netdissect_options *ndo, struct pfsync_state *s) 378 { 379 struct pfsync_state_peer *src, *dst; 380 struct pfsync_state_key *sk, *nk; 381 int min, sec; 382 383 if (s->direction == PF_OUT) { 384 src = &s->src; 385 dst = &s->dst; 386 sk = &s->key[PF_SK_STACK]; 387 nk = &s->key[PF_SK_WIRE]; 388 if (s->proto == IPPROTO_ICMP || s->proto == IPPROTO_ICMPV6) 389 sk->port[0] = nk->port[0]; 390 } else { 391 src = &s->dst; 392 dst = &s->src; 393 sk = &s->key[PF_SK_WIRE]; 394 nk = &s->key[PF_SK_STACK]; 395 if (s->proto == IPPROTO_ICMP || s->proto == IPPROTO_ICMPV6) 396 sk->port[1] = nk->port[1]; 397 } 398 ND_PRINT((ndo, "\t%s ", s->ifname)); 399 ND_PRINT((ndo, "proto %u ", s->proto)); 400 401 print_host(ndo, &nk->addr[1], nk->port[1], s->af, NULL); 402 if (PF_ANEQ(&nk->addr[1], &sk->addr[1], s->af) || 403 nk->port[1] != sk->port[1]) { 404 ND_PRINT((ndo, " (")); 405 print_host(ndo, &sk->addr[1], sk->port[1], s->af, NULL); 406 ND_PRINT((ndo, ")")); 407 } 408 if (s->direction == PF_OUT) 409 ND_PRINT((ndo, " -> ")); 410 else 411 ND_PRINT((ndo, " <- ")); 412 print_host(ndo, &nk->addr[0], nk->port[0], s->af, NULL); 413 if (PF_ANEQ(&nk->addr[0], &sk->addr[0], s->af) || 414 nk->port[0] != sk->port[0]) { 415 ND_PRINT((ndo, " (")); 416 print_host(ndo, &sk->addr[0], sk->port[0], s->af, NULL); 417 ND_PRINT((ndo, ")")); 418 } 419 420 print_src_dst(ndo, src, dst, s->proto); 421 422 if (ndo->ndo_vflag > 1) { 423 uint64_t packets[2]; 424 uint64_t bytes[2]; 425 uint32_t creation = ntohl(s->creation); 426 uint32_t expire = ntohl(s->expire); 427 428 sec = creation % 60; 429 creation /= 60; 430 min = creation % 60; 431 creation /= 60; 432 ND_PRINT((ndo, "\n\tage %.2u:%.2u:%.2u", creation, min, sec)); 433 sec = expire % 60; 434 expire /= 60; 435 min = expire % 60; 436 expire /= 60; 437 ND_PRINT((ndo, ", expires in %.2u:%.2u:%.2u", expire, min, sec)); 438 439 bcopy(s->packets[0], &packets[0], sizeof(uint64_t)); 440 bcopy(s->packets[1], &packets[1], sizeof(uint64_t)); 441 bcopy(s->bytes[0], &bytes[0], sizeof(uint64_t)); 442 bcopy(s->bytes[1], &bytes[1], sizeof(uint64_t)); 443 ND_PRINT((ndo, ", %ju:%ju pkts, %ju:%ju bytes", 444 be64toh(packets[0]), be64toh(packets[1]), 445 be64toh(bytes[0]), be64toh(bytes[1]))); 446 if (s->anchor != ntohl(-1)) 447 ND_PRINT((ndo, ", anchor %u", ntohl(s->anchor))); 448 if (s->rule != ntohl(-1)) 449 ND_PRINT((ndo, ", rule %u", ntohl(s->rule))); 450 } 451 if (ndo->ndo_vflag > 1) { 452 uint64_t id; 453 454 bcopy(&s->id, &id, sizeof(uint64_t)); 455 ND_PRINT((ndo, "\n\tid: %016jx creatorid: %08x", 456 (uintmax_t )be64toh(id), ntohl(s->creatorid))); 457 } 458 } 459