/* * Copyright (c) 2001 Jean-Baptiste Marchand, Hervé Schauer Consultants. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Jean-Baptiste Marchand * at Hervé Schauer Consultants. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * * Print filtering statistics * */ #include #include #include "my_fltdefs.h" #include "pktflt.h" extern char* icmp_type_keyword[]; /* print back to the client a given filter on a given interface */ void print_interface_rule( struct pf_interface *iface, struct pf_filter *pf_filter, unsigned char numbers) { ULONG proto; char ip_proto = 0; char *dotted_ip; unsigned int ipv4_mask; PF_FILTER_DESCRIPTOR *filter = pf_filter->filter; if (numbers) client_printf("rule %d: ", filter->dwRule); if (iface->inAction == PF_ACTION_FORWARD) client_printf("block "); else /* PF_ACTION_DROP */ client_printf("pass "); if (pf_filter->filter_flags & FILTER_IN) client_printf("in "); else /* FILTER_OUT */ client_printf("out "); client_printf("on %s", iface->name); proto = filter->dwProtocol; switch (proto) { case FILTER_PROTO_TCP: client_printf("proto tcp "); break; case FILTER_PROTO_UDP: client_printf("proto udp "); break; case FILTER_PROTO_ICMP: client_printf("proto icmp "); break; case FILTER_PROTO_ANY: /* nothing */ break; default: /* decimal IP protocol number */ client_printf("proto %d ", proto); ip_proto = 1; break; } if (proto == FILTER_PROTO_TCP || proto == FILTER_PROTO_UDP) { if ((*filter->DstAddr == 0) && (*filter->SrcAddr == 0) && (filter->wDstPort == FILTER_TCPUDP_PORT_ANY) && (filter->wSrcPort == FILTER_TCPUDP_PORT_ANY)) { client_printf("all "); goto established; } } if (proto == FILTER_PROTO_ICMP || ip_proto) { if ((*filter->DstAddr == 0) && (*filter->SrcAddr ==0)) client_printf("all "); goto established; } if (*filter->SrcAddr == 0) client_printf("from any "); else { dotted_ip = inet_ntoa( * (struct in_addr *) filter->SrcAddr); client_printf("from %s", dotted_ip); ipv4_mask = ntohl(* (unsigned int *) filter->SrcMask); if (ipv4_mask != ANY_MASK) { unsigned char mask = 0; while (ipv4_mask) { ipv4_mask = ipv4_mask << 1; mask++; } client_printf("/%d ", mask); } else client_printf(" "); } if ((proto == FILTER_PROTO_TCP || proto == FILTER_PROTO_UDP) && (filter->wSrcPort != FILTER_TCPUDP_PORT_ANY)) { client_printf("port "); if (pf_filter->src_flags & TCP_UDP_EQ_PORT) client_printf("= %d ", filter->wSrcPort); if (pf_filter->src_flags & TCP_UDP_LT_PORT) client_printf("< %d ", filter->wSrcPort + 1); if (pf_filter->src_flags & TCP_UDP_GT_PORT) client_printf("> %d ", filter->wSrcPort - 1); if (pf_filter->src_flags & TCP_UDP_LE_PORT) client_printf("<= %d ", filter->wSrcPort); if (pf_filter->src_flags & TCP_UDP_GE_PORT) client_printf(">= %d ", filter->wSrcPort); if (pf_filter->src_flags & TCP_UDP_INTERVAL_PORT) client_printf("%d >< %d ", filter->wSrcPort - 1, filter->wSrcPortHighRange + 1); } if (*filter->DstAddr == 0) client_printf("to any "); else { dotted_ip = inet_ntoa( * (struct in_addr *) filter->DstAddr); client_printf("to %s", dotted_ip); ipv4_mask = ntohl(* (unsigned int *) filter->DstMask); if (ipv4_mask != ANY_MASK) { unsigned char mask = 0; while (ipv4_mask) { ipv4_mask = ipv4_mask << 1; mask++; } client_printf("/%d ", mask); } else client_printf(" "); } if ((proto == FILTER_PROTO_TCP || proto == FILTER_PROTO_UDP) && (filter->wDstPort != FILTER_TCPUDP_PORT_ANY)) { client_printf("port "); if (pf_filter->dst_flags & TCP_UDP_EQ_PORT) client_printf("= %d ", filter->wDstPort); if (pf_filter->dst_flags & TCP_UDP_LT_PORT) client_printf("< %d ", filter->wDstPort + 1); if (pf_filter->dst_flags & TCP_UDP_GT_PORT) client_printf("> %d ", filter->wDstPort - 1); if (pf_filter->dst_flags & TCP_UDP_LE_PORT) client_printf("<= %d ", filter->wDstPort); if (pf_filter->dst_flags & TCP_UDP_GE_PORT) client_printf(">= %d ", filter->wDstPort); if (pf_filter->dst_flags & TCP_UDP_INTERVAL_PORT) client_printf("%d >< %d ", filter->wDstPort - 1, filter->wDstPortHighRange + 1); } established: if ((proto == FILTER_PROTO_TCP) && (filter->dwFilterFlags == FD_FLAGS_NOSYN)) client_printf("established"); if (proto == FILTER_PROTO_ICMP) { if ((filter->wSrcPort == FILTER_ICMP_TYPE_ANY) && (filter->wDstPort == FILTER_ICMP_CODE_ANY)) /* nothing */ ; else { client_printf("icmp-type %s ", icmp_type_keyword[filter->wSrcPort]); if (filter->wDstPort == FILTER_ICMP_CODE_ANY) client_printf("code any "); else client_printf("code %d ", filter->wDstPort); } } } /* print back to the client the current rules on a given interface */ char get_interface_rules(struct pf_interface *iface, unsigned char numbers) { struct pf_filter *pf_filter; if (iface->small_frags == GF_FRAGMENTS) { client_printf("# global options\n"); client_printf("option small_frags on %s\n\n", iface->name); } #if 0 if (iface->strong_host == GF_STRONGHOST) client_printf("option strong_host on %s\n", iface->name); #endif /* GF_STRONGHOST */ #if 0 if (iface->check_frags == GF_FRAGCACHE) client_printf("option check_frags on %s\n", iface->name); #endif /* GF_FRAGCACHE */ /* input rules */ client_printf("# default policy \n"); if (iface->inAction == PF_ACTION_FORWARD) client_printf("pass in on %s all\n", iface->name); else /* PF_ACTION_DROP */ client_printf("block in on %s all\n", iface->name); if (iface->outAction == PF_ACTION_FORWARD) client_printf("pass out on %s all\n\n", iface->name); else /* PF_ACTION_DROP */ client_printf("block out on %s all\n\n", iface->name); /* input rules */ pf_filter = iface->inFilters; if (pf_filter) client_printf("# input rules\n"); while (pf_filter) { print_interface_rule(iface, pf_filter, numbers); client_printf("\n"); pf_filter = pf_filter->next; } /* output rules */ pf_filter = iface->outFilters; if (pf_filter) client_printf("\n# output rules\n"); while (pf_filter) { print_interface_rule(iface, pf_filter, numbers); client_printf("\n"); pf_filter = pf_filter->next; } return 1; } /* print back to the client the filtering statistics for a given interface */ char get_interface_statistics(struct pf_interface *iface, unsigned char detailed) { PF_INTERFACE_STATS *pf_stats; PF_FILTER_STATS *filter_stats; struct pf_filter *pf_filter; ULONG orig_size; ULONG size; DWORD status; DWORD i; size = 1 * sizeof(PF_INTERFACE_STATS); orig_size = size; pf_stats = (PPF_INTERFACE_STATS) malloc(size); /* this call can fail */ status = PfGetInterfaceStatistics(iface->ih, pf_stats, &size, FALSE /* XXX don't reset informations */); if (status != NO_ERROR) { if (status == PFERROR_BUFFER_TOO_SMALL) { client_printf("PfGetInterfaceStatistics: buffer too small, even for stats\n"); return -1; } } if (size > orig_size) { /* we need more space because there is more than one filter */ free(pf_stats); pf_stats = (PPF_INTERFACE_STATS) malloc (size); status = PfGetInterfaceStatistics(iface->ih, pf_stats, &size, FALSE); if (status != NO_ERROR) { client_printf("PfGetInterfaceStatistics: error %d \n", status); return -1; } } /* XXX dirty hack for alignment issues XXX */ filter_stats = (PPF_FILTER_STATS) (((char *) pf_stats->FilterInfo) - 8); client_printf("Interface: %s\n", iface->name); client_printf("Global:\n"); #if 0 if (iface->check_frags == GF_FRAGCACHE) client_printf(" Fragmentation cache requests: %d\n", pf_stats->dwFrag); #endif /* GF_FRAGCACHE */ if (iface->small_frags == GF_FRAGMENTS) /* number of dropped packet composed by small fragments (fragments with a size < 16, 8 being the only valid size) This counter is only valid when the global filtering option GF_FRAGMENTS was activated. */ client_printf(" Fragmented packets dropped : %d\n", pf_stats->dwFrag); /* number of dropped packets because of invalid _destination_ address */ #if 0 if (iface->strong_host == GF_STRONGHOST) client_printf(" Packets with invalid destination address dropped: %d\n", pf_stats->dwSpoof); #endif /* GF_STRONGHOST */ client_printf("Input filters: \n"); if (iface->inAction == PF_ACTION_FORWARD) client_printf(" Default action: pass\n"); else /* PF_ACTION_DROP */ client_printf(" Default action: block\n"); if (iface->inFiltersNo) client_printf(" Active filters: %d\n", iface->inFiltersNo); client_printf(" Dropped packets: %d\n", pf_stats->dwInDrops); if (detailed) { if (pf_stats->dwNumInFilters > 0) client_printf(" Details of rules matches:\n"); pf_filter = iface->inFilters; for ( i = 0; i < iface->inFiltersNo; i++) { if (pf_filter->filter->dwRule == filter_stats->info.dwRule) { print_interface_rule(iface, pf_filter, 0 /* no rules numbers */); client_printf(": %d matches\n", filter_stats->dwNumPacketsFiltered); } else /* can happen with rules multiple defined */ ; filter_stats++; pf_filter = pf_filter->next; } } /* liSYN is documented in MSDN as the number of SYN packets _discarded_. However, my tests have shown that liSYN contains the number of incoming TCP segments with the SYN flag set (and without the ACK flag set) _accepted_. liSYN is useful only for accounting _accepted_ incoming TCP connections. These TCP segments can either be accepted by a specific rule that accept incoming TCP connections or by a "pass in on ethx all" default input rule. */ client_printf(" TCP SYN segments accepted: %d\n", pf_stats->liSYN); client_printf("Output filters:\n"); if (iface->outAction == PF_ACTION_FORWARD) client_printf(" Default action: pass\n"); else /* PF_ACTION_DROP */ client_printf(" Default action: block\n"); if (iface->outFiltersNo) client_printf(" Active filters: %d\n", iface->outFiltersNo); client_printf(" Dropped packets: %d \n", pf_stats->dwOutDrops); if (detailed) { if (iface->outFiltersNo > 0) client_printf(" Details of rules matches\n"); pf_filter = iface->outFilters; for ( i = 0; i < iface->outFiltersNo; i++) { print_interface_rule(iface, pf_filter, 0 /* no rules numbers */); client_printf(" %d matches\n", filter_stats->dwNumPacketsFiltered); filter_stats++; pf_filter = pf_filter->next; } } return 1; }