/* * 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. */ /* * * Interfaces and filters management * */ #include #include #include #include #include "my_fltdefs.h" #include "pktflt.h" #include "filters.h" #include "filter_stats.h" /* error messages */ #include "pktfltmsg.h" extern HANDLE SystemLog; extern DWORD log_buf_size; struct pf_interface *interfaces = NULL; /* logging */ char *log_buf; /* prototypes */ DWORD WINAPI LoggingThread(PVOID event); /* search an interface among existing ones. Creates a new pf_interface structure if not found */ struct pf_interface *get_interface(unsigned short index, char create_flag) { struct pf_interface *current = interfaces; struct pf_interface *prev = NULL; while (current) { if (current->index == index) break; prev = current; current = current->next; } if (current == NULL && !create_flag) return current; if (current == NULL) { current = (struct pf_interface *) malloc(1 * sizeof(struct pf_interface)); /* init a pf_interface struct */ current->next = NULL; current->index = (unsigned char) index; current->system_index = -1; current->ih = NULL; current->inAction = -1; /* undefined */ current->outAction = -1; /* undefined */ current->inFiltersNo = 0; current->outFiltersNo = 0; current->inFilters = NULL; current->outFilters = NULL; current->small_frags = 0; #if 0 current->strong_host = 0; #endif /* GF_STRONGHOST */ #if 0 current->check_frags = 0; #endif /* GF_FRAGCACHE */ if (prev != NULL) prev->next = current; else interfaces = current; } return current; } /* open the interface corresponding to a given filter */ char open_interface(struct pf_filter *pf_filter) { struct pf_interface *iface = pf_filter->iface; IP_ADAPTER_INFO *adapt_info; DWORD status; DWORD status2; ULONG ip_addr; unsigned char index; unsigned long size; HANDLE logThread; HANDLE logEvent; /* logging */ /* create event */ logEvent = CreateEvent(NULL, FALSE, FALSE, NULL); status = PfMakeLog(logEvent); /* create log buffer */ log_buf = malloc(log_buf_size * sizeof(char)); if (log_buf) { memset(log_buf, 0, log_buf_size); status = PfSetLogBuffer(log_buf, log_buf_size, 20, 1, &status2, &status2, &status2); /* start logging thread */ logThread = (HANDLE) _beginthreadex(NULL, 0, LoggingThread, logEvent, 0, NULL); } if ((iface->inAction == -1) || (iface->outAction == -1)) { /* one (or both) default rule(s) have been specified */ if (iface->inAction == -1) { client_printf("Warning: no default input rule, "); if (pf_filter->filter_flags & FILTER_IN) { if (pf_filter->filter_flags & FILTER_PASS) { iface->inAction = PF_ACTION_DROP; client_printf("assuming default is block\n"); } else /* FILTER_DROP */ { iface->inAction = PF_ACTION_FORWARD; client_printf("assuming default is pass\n"); } } else { iface->inAction = PF_ACTION_DROP; /* default is block */ client_printf("default policy is block\n"); } } if (iface->outAction == -1) { client_printf("Warning: no default output rule, "); if (pf_filter->filter_flags & FILTER_OUT) { if (pf_filter->filter_flags & FILTER_PASS) { iface->outAction = PF_ACTION_DROP; client_printf("assuming default is block\n"); } else /* PF_ACTION_DROP */ { iface->outAction = PF_ACTION_FORWARD; client_printf("assuming default is pass\n"); } } else { iface->outAction = PF_ACTION_DROP; /* default is block */ client_printf("default policy is block\n"); } } } #if 0 /* for debugging purpose. The call to PfCreateInterface() is blocking * for about 2 minutes when the service is launched at startup */ ReportEvent(SystemLog, EVENTLOG_INFORMATION_TYPE, 0, PKTFLT_STARTING, NULL, 0, 0, NULL, NULL); #endif status = PfCreateInterface(0 /* no name */, iface->inAction /* default input action */, iface->outAction /* default output action */, TRUE /* log */, FALSE /* XXX TRUE*/ /* unique */, &(iface->ih) /* interface handler */); CHECK_ERROR(status, "PfCreateInterface"); #if 0 /* for debugging purpose. The call to PfCreateInterface() is blocking * for about 2 minutes when the service is launched at startup */ ReportEvent(SystemLog, EVENTLOG_INFORMATION_TYPE, 0, PKTFLT_STARTED, NULL, 0, 0, NULL, NULL); #endif /* setting global flags */ if (iface->small_frags) { status = PfAddGlobalFilterToInterface(iface->ih, GF_FRAGMENTS); CHECK_ERROR(status, "PfAddGlobalFilterToInterface"); } #if 0 if (iface->strong_host) { status = PfAddGlobalFilterToInterface(iface->ih, GF_STRONGHOST); CHECK_ERROR(status, "PfAddGlobalFilterToInterface"); } #endif /* GF_STRONGHOST */ #if 0 if (iface->check_frags) { status = PfAddGlobalFilterToInterface(iface->ih, GF_FRAGCACHE); CHECK_ERROR(status, "PfAddGlobalFilterToInterface"); } #endif /* GF_FRAGCACHE */ /* try with only one adapter */ size = sizeof(IP_ADAPTER_INFO); adapt_info = malloc(size); status = GetAdaptersInfo(adapt_info, &size); if (status != ERROR_SUCCESS) { /* we need more space */ free(adapt_info); adapt_info = malloc(size); status = GetAdaptersInfo(adapt_info, &size); CHECK_ERROR(status, "GetAdaptersInfo"); } /* find the appropriate IP_ADAPTER_INFO structure */ index = iface->index; while (index--) /* XXX we could also compare index and the ComboIndex field value */ adapt_info = adapt_info->Next; /* memorize system index (IP_ADAPTER_INFO) */ iface->system_index = adapt_info->Index; /* adapter type */ switch (adapt_info->Type) { case MIB_IF_TYPE_ETHERNET: sprintf(iface->name, "eth%d", adapt_info->ComboIndex); break; case MIB_IF_TYPE_PPP: sprintf(iface->name, "ppp%d", adapt_info->ComboIndex); break; case MIB_IF_TYPE_SLIP: sprintf(iface->name, "sl%d", adapt_info->ComboIndex); break; case MIB_IF_TYPE_LOOPBACK: /* XXX Probably never returned, only with GetIfTable() API */ sprintf(iface->name, "lo%d", adapt_info->ComboIndex); break; case MIB_IF_TYPE_TOKENRING: sprintf(iface->name, "tr%d", adapt_info->ComboIndex); break; case MIB_IF_TYPE_FDDI: sprintf(iface->name, "fd%d", adapt_info->ComboIndex); break; default: sprintf(iface->name, "if%d", adapt_info->ComboIndex); break; } /* XXX Using INADDR_ANY seems to support IP address change on adapter */ ip_addr = inet_addr("0.0.0.0"); status = PfBindInterfaceToIndex(iface->ih, iface->system_index, PF_IPV4, (PBYTE) &ip_addr); CHECK_ERROR(status, "PfBindInterfaceToIndex"); return 1; } /* close a given interface, removing all filters on it */ char close_interface(struct pf_filter *pf_filter) { struct pf_interface *current; struct pf_interface *prev = NULL; struct pf_filter *to_free; struct pf_filter *next_to_free; DWORD status; current = interfaces; while (current && current->index != pf_filter->iface->index) { prev = current; current = current->next; } if (!current) /* specified interface doesn't exist */ return 0; if ((current == interfaces) && (current->next == NULL)) /* reset the interfaces head pointer */ interfaces = NULL; if (prev) prev->next = current->next; if (!current->ih) /* sanity check */ return 0; status = PfDeleteInterface(current->ih); if (status != NO_ERROR) return 0; /* free all filters */ to_free = current->inFilters; while (to_free) { free(to_free->filter); next_to_free = to_free->next; free(to_free); to_free = next_to_free; } to_free = current->outFilters; while (to_free) { free(to_free->filter); next_to_free = to_free->next; free(to_free); to_free = next_to_free; } free(current); return 1; } /* close all interfaces, removing *all* filters on *all* interfaces */ char close_all_interfaces(void) { struct pf_interface *current; struct pf_interface *current_to_free; struct pf_filter *to_free; struct pf_filter *next_to_free; DWORD status; current = interfaces; while (current) { if (current->ih) { status = PfDeleteInterface(current->ih); if (status != NO_ERROR) /* error when deleting interface */ return 0; } else /* we've lost the handle on the interface */ return 0; /* free all filters */ to_free = current->inFilters; while (to_free) { free(to_free->filter); next_to_free = to_free->next; free(to_free); to_free = next_to_free; } to_free = current->outFilters; while (to_free) { free(to_free->filter); next_to_free = to_free->next; free(to_free); to_free = next_to_free; } current_to_free = current; current = current->next; free(current_to_free); } interfaces = NULL; return 1; } /* add a filter to an interface. Eventually open the interface if it is not yet opened */ char add_filter_to_interface(struct pf_filter *pf_filter) { DWORD status; struct pf_filter *prev = NULL; char ret; if (pf_filter->iface->ih == NULL) { ret = open_interface(pf_filter); if (ret == 0) return -1; } if (pf_filter->filter_flags & FILTER_IN) prev = pf_filter->iface->inFilters; else /* FILTER_OUT */ prev = pf_filter->iface->outFilters; while (1) { if (prev && prev->next) prev = prev->next; else break; } if (prev) prev->next = pf_filter; else { /* first inbound or outbound filter */ if (pf_filter->filter_flags & FILTER_IN) pf_filter->iface->inFilters = pf_filter; else /* FILTER_OUT */ pf_filter->iface->outFilters = pf_filter; } if (prev) pf_filter->filter->dwRule = prev->filter->dwRule + 1; else { if (pf_filter->filter_flags & FILTER_IN) pf_filter->filter->dwRule = IN_FILTERS_START; else /* FILTER_OUT */ pf_filter->filter->dwRule = OUT_FILTERS_START; } if (pf_filter->filter_flags & FILTER_IN) { status = PfAddFiltersToInterface(pf_filter->iface->ih, 1, pf_filter->filter, 0, NULL, NULL); if (status == NO_ERROR) pf_filter->iface->inFiltersNo++; } else /* FILTER_OUT */ { status = PfAddFiltersToInterface(pf_filter->iface->ih, 0, NULL, 1, pf_filter->filter, NULL); if (status == NO_ERROR) pf_filter->iface->outFiltersNo++; } CHECK_ERROR(status, "PfAddFiltersToInterface"); return 1; } /* delete a filter on interface */ char delete_filter_on_interface(unsigned char rule, struct pf_filter *pf_filter) { DWORD status; struct pf_filter *prev = NULL; struct pf_filter *current; char input_rule = 0; char output_rule = 0; if (rule < OUT_FILTERS_START) { /* input filter */ current = pf_filter->iface->inFilters; input_rule = 1; } else { /* output filter */ current = pf_filter->iface->outFilters; output_rule = 1; } while (current && (current->filter->dwRule != rule)) { prev = current; current = current->next; } if (!current) /* rule to delete does not exist */ return 0; if (input_rule) status = PfRemoveFiltersFromInterface(current->iface->ih, 1, current->filter, 0, NULL); else status = PfRemoveFiltersFromInterface(current->iface->ih, 0, NULL, 1, current->filter); if (status == NO_ERROR) { if (prev) /* update the list */ prev->next = current->next; else { if (input_rule) current->iface->inFilters = NULL; else current->iface->outFilters = NULL; } if (input_rule) { if (current->iface->inFiltersNo) current->iface->inFiltersNo--; } else { if (current->iface->outFiltersNo) current->iface->outFiltersNo--; } free(current->filter); free(current); return 1; } else return 0; }