Merge authors: goetz <goetz@goetz> Stefan Götz (stefan.goetz) Related merge proposals: https://code.launchpad.net/~stefan.goetz/hipl/fw-cache-port-cleanup/+merge/39235 proposed by: Stefan Götz (stefan.goetz) review: Approve - Diego Biurrun (diego-biurrun) review: Approve - Stefan Götz (stefan.goetz) ------------------------------------------------------------ revno: 5151 [merge] committer: Diego Biurrun <diego@xxxxxxxxxx> branch nick: trunk timestamp: Fri 2010-11-12 18:49:42 +0100 message: Merge firewall cache port cleanup branch from Stefan Götz; minor fixes from me. added: firewall/file_buffer.c firewall/file_buffer.h firewall/file_buffer_inline.h firewall/line_parser.c firewall/line_parser.h firewall/line_parser_inline.h firewall/mem_area.h test/check_firewall.c test/firewall/ test/firewall/file_buffer.c test/firewall/line_parser.c test/firewall/port_bindings.c test/fw_port_bindings_performance.c renamed: firewall/cache_port.c => firewall/port_bindings.c firewall/cache_port.h => firewall/port_bindings.h modified: .bzrignore Makefile.am firewall/firewall.c firewall/lsi.c lib/core/icomm.h firewall/port_bindings.c firewall/port_bindings.h -- lp:hipl https://code.launchpad.net/~hipl-core/hipl/trunk Your team HIPL core team is subscribed to branch lp:hipl. To unsubscribe from this branch go to https://code.launchpad.net/~hipl-core/hipl/trunk/+edit-subscription
=== modified file '.bzrignore' --- .bzrignore 2010-10-29 12:51:08 +0000 +++ .bzrignore 2010-11-12 17:49:42 +0000 @@ -48,6 +48,7 @@ test/auth_performance test/certteststub test/dh_performance +test/fw_port_bindings_performance test/hc_performance tools/hipconf tools/hipdnskeyparse === modified file 'Makefile.am' --- Makefile.am 2010-11-12 16:42:54 +0000 +++ Makefile.am 2010-11-12 17:49:42 +0000 @@ -56,6 +56,11 @@ endif +### test programs ### +noinst_PROGRAMS = test/fw_port_bindings_performance + + + ### libraries ### lib_LTLIBRARIES = lib/core/libhipcore.la @@ -65,17 +70,23 @@ check_PROGRAMS = if HIP_UNITTESTS -TESTS += check_lib_core -check_PROGRAMS += check_lib_core +TESTS += check_firewall \ + check_lib_core +check_PROGRAMS += check_firewall \ + check_lib_core endif ### source declarations ### -test_auth_performance_SOURCES = test/auth_performance.c -test_certteststub_SOURCES = test/certteststub.c -test_dh_performance_SOURCES = test/dh_performance.c -test_hc_performance_SOURCES = test/hc_performance.c +test_auth_performance_SOURCES = test/auth_performance.c +test_certteststub_SOURCES = test/certteststub.c +test_dh_performance_SOURCES = test/dh_performance.c +test_fw_port_bindings_performance_SOURCES = test/fw_port_bindings_performance.c \ + firewall/file_buffer.c \ + firewall/line_parser.c \ + firewall/port_bindings.c +test_hc_performance_SOURCES = test/hc_performance.c tools_hipconf_SOURCES = tools/hipconf.c tools_pisacert_SOURCES = tools/pisacert.c @@ -120,18 +131,20 @@ endif firewall_hipfw_SOURCES = firewall/cache.c \ - firewall/cache_port.c \ firewall/conntrack.c \ firewall/dlist.c \ firewall/esp_prot_api.c \ firewall/esp_prot_config.c \ firewall/esp_prot_conntrack.c \ firewall/esp_prot_fw_msg.c \ + firewall/file_buffer.c \ firewall/firewall.c \ firewall/firewall_control.c \ firewall/helpers.c \ firewall/hslist.c \ + firewall/line_parser.c \ firewall/lsi.c \ + firewall/port_bindings.c \ firewall/reinject.c \ firewall/rule_management.c \ firewall/sysopp.c \ @@ -187,6 +200,11 @@ test/lib/core/hit.c \ test/lib/core/straddr.c +check_firewall_SOURCES = test/check_firewall.c \ + test/firewall/file_buffer.c \ + test/firewall/line_parser.c \ + test/firewall/port_bindings.c + # Initialize LDADD lists empty, because modules might add entries to LDADD. The # module LDADDs need to be included before the standard LDADDs, because modules # can depend on the core code. @@ -202,15 +220,17 @@ ### library dependencies ### -check_lib_core_LDADD = lib/core/libhipcore.la -firewall_hipfw_LDADD += lib/core/libhipcore.la -hipd_hipd_LDADD += lib/core/libhipcore.la -test_auth_performance_LDADD = lib/core/libhipcore.la -test_certteststub_LDADD = lib/core/libhipcore.la -test_dh_performance_LDADD = lib/core/libhipcore.la -test_hc_performance_LDADD = lib/core/libhipcore.la -tools_hipconf_LDADD += lib/core/libhipcore.la -tools_pisacert_LDADD = lib/core/libhipcore.la +check_lib_core_LDADD = lib/core/libhipcore.la +check_firewall_LDADD = lib/core/libhipcore.la +firewall_hipfw_LDADD += lib/core/libhipcore.la +hipd_hipd_LDADD += lib/core/libhipcore.la +test_auth_performance_LDADD = lib/core/libhipcore.la +test_certteststub_LDADD = lib/core/libhipcore.la +test_dh_performance_LDADD = lib/core/libhipcore.la +test_fw_port_bindings_performance_LDADD = lib/core/libhipcore.la +test_hc_performance_LDADD = lib/core/libhipcore.la +tools_hipconf_LDADD += lib/core/libhipcore.la +tools_pisacert_LDADD = lib/core/libhipcore.la dist_sbin_SCRIPTS = tools/hipdnskeyparse/hipdnskeyparse \ tools/hipdnsproxy/hipdnsproxy \ === added file 'firewall/file_buffer.c' --- firewall/file_buffer.c 1970-01-01 00:00:00 +0000 +++ firewall/file_buffer.c 2010-11-12 17:49:42 +0000 @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2010 Aalto University and RWTH Aachen University. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file + * @author Stefan Goetz <stefan.goetz@xxxxxxxxxxxxxxxxx> + */ + +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "lib/core/debug.h" +#include "file_buffer.h" + +/** + * Always allocate this many more bytes for the memory buffer than is needed + * the actual file contents. + * This avoids having to re-allocate the buffer for very small increases in the + * file size. + */ +static const unsigned int HIP_FB_HEADROOM = 4096; + +/** + * Allocate at most this many bytes, i.e., the maximum supported file size. + * This is an arbitrary number used for sanity checking. + */ +static const unsigned long HIP_FB_MAX_SIZE = 1024 * 1024 * 1024; + +/** + * (Re-)allocates the file buffer so that it can hold a complete copy of its + * file in memory. + * After the function completes successfully, the memory area associated with + * the file buffer points to an allocated region of memory that is at least as + * large as the size of the file buffer's file at the time of invocation. + * The contents of the memory area are undefined. + * + * If the size of a file cannot be determined (lseek() does not work on proc + * files), the buffer size is increased incrementally. + * + * @param fb the file buffer to use. + * @return If the function completes successfully, it returns 0. + * If fb is NULL, -1 is returned. + * If internal errors occur, other negative values are returned. + */ +static int hip_fb_resize(struct hip_file_buffer *const fb) +{ + int err = 0; + + if (fb) { + off_t file_size = 0; + + free(fb->ma.start); + fb->ma.start = NULL; + + /* First, we try to determine the current file size for the new buffer size. + * If that fails (it does, e.g., for proc files), we just increase the + * current buffer size. */ + errno = 0; + file_size = lseek(fb->fd, 0, SEEK_END); + if (file_size != -1 || EINVAL == errno) { + if (file_size != -1) { + fb->buffer_size = file_size + HIP_FB_HEADROOM; // add a little head room + } else if (EINVAL == errno) { + if (fb->buffer_size < HIP_FB_HEADROOM) { + fb->buffer_size = HIP_FB_HEADROOM; + } else { + fb->buffer_size *= 2; + } + } + + // fb->buffer_size is now adjusted, but maybe not positive or very large? + if (fb->buffer_size > 0 && fb->buffer_size <= HIP_FB_MAX_SIZE) { + // fb->buffer_size is now the size we want to allocate + fb->ma.start = malloc(fb->buffer_size); + if (fb->ma.start) { + return 0; + } else { + HIP_ERROR("Allocating %d bytes of memory for file data failed\n", + fb->buffer_size); + err = -4; + } + } else { + HIP_ERROR("The file buffer size %d is too large to be supported\n"); + err = -3; + } + } else { + HIP_ERROR("Determining file size via lseek() failed: %s\n", strerror(errno)); + err = -2; + } + fb->buffer_size = 0; + } else { + err = -1; + } + + return err; +} + +/** + * Initializes a file buffer that holds the specified file. + * + * A file buffer is used to load and hold the contents of a file in + * memory (for simplified access or improved performance). + * The memory buffer is allocated so that the whole file fits in it. + * Any changes to the memory buffer are not written back to the file and remain + * local to the memory buffer. + * Note that this is useful primarily with files that cannot be mapped into + * memory via mmap(), such as files in /proc. + * For regular files, using mmap() is vastly more efficient. + * + * This function allocates resources, in particular memory, for the returned + * object. + * To free these resources and to avoid memory leaks, it is imperative to call + * hip_fb_delete() when the object created here is no longer used. + * + * @param fb a pointer to a valid, allocated instance of struct hip_file_buffer. + * Upon successful completion, the function writes file-specific context data + * to the location referenced by fb. + * @param file_name the name of the file to open and load into memory. + * @return a 0 if the file could be opened and successfully buffered. + * -1 is returned if fb is NULL or if file_name is NULL. + * -2 is returned if the specified file cannot be opened for reading. + * -3 is returned if an internal error occurs. + */ +int hip_fb_create(struct hip_file_buffer *const fb, + const char *const file_name) +{ + int err = 0; + + if (fb && file_name) { + memset(fb, 0, sizeof(*fb)); // set all fields to 0/NULL + fb->fd = open(file_name, O_RDONLY); + if (fb->fd != -1) { + if (hip_fb_reload(fb) == 0) { + return 0; + } else { + err = -3; + } + } else { + HIP_ERROR("Opening the file %s for reading via open() failed with the error %s\n", + file_name, strerror(errno)); + err = -2; + } + hip_fb_delete(fb); + } else { + err = -1; + } + + return err; +} + +/** + * De-allocates the resources associated with a file buffer object in + * hip_fb_create(). + * This function does not de-allocated the memory pointed to by fb. + * After calling this function, the result of calling any other hip_fb_...() + * function on the file buffer fb is undefined. + * + * @param fb the file buffer to delete. If fb is NULL, this function has no + * effect. + */ +void hip_fb_delete(struct hip_file_buffer *const fb) +{ + if (fb) { + if (fb->fd != -1) { + close(fb->fd); + fb->fd = -1; + } + free(fb->ma.start); + fb->ma.start = NULL; + fb->ma.end = NULL; + } +} + +/** + * Make modifications to the file since the last invocation of hip_fb_create() or + * hip_fb_reload() visible in the buffer. + * + * @warning + * Note that this function may change the start and end pointers in the memory + * area returned by hip_fb_get_mem_area()! + * + * @param fb the file buffer to use. + * @return 0 when the function completes successfully. + * If fb is NULL, -1 is returned. + * If an internal error occurs, -2 is returned. + */ +int hip_fb_reload(struct hip_file_buffer *const fb) +{ + if (!fb) { + return -1; + } + + while (1) { + ssize_t bytes; + off_t seek_offset; + + // can we re-read the whole file into the memory buffer? + seek_offset = lseek(fb->fd, 0, SEEK_SET); + if (-1 == seek_offset) { + HIP_ERROR("Resetting the read position on file descriptor %d via lseek() failed with the error %s\n", + fb->fd, errno, strerror(errno)); + break; + } + + bytes = read(fb->fd, fb->ma.start, fb->buffer_size); + if (bytes == -1) { + HIP_ERROR("Reading the contents of the file descriptor %d via read() into a memory buffer of size %d failed with the error %s\n", + fb->fd, fb->buffer_size, strerror(errno)); + break; + } else if ((size_t)bytes == fb->buffer_size) { + // we can't fit the file into the memory buffer -> resize it + if (hip_fb_resize(fb) == 0) { + // successful resize -> retry reading + continue; + } else { + // error resizing -> return error + break; + } + } else { + // successfully read the file contents into the buffer + fb->ma.end = fb->ma.start + bytes; + return 0; + } + } + + fb->ma.end = NULL; + + return -2; +} === added file 'firewall/file_buffer.h' --- firewall/file_buffer.h 1970-01-01 00:00:00 +0000 +++ firewall/file_buffer.h 2010-11-12 17:49:42 +0000 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2010 Aalto University and RWTH Aachen University. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file + * @author Stefan Goetz <stefan.goetz@xxxxxxxxxxxxxxxxx> + */ + +#ifndef HIP_FIREWALL_FILE_BUFFER_H +#define HIP_FIREWALL_FILE_BUFFER_H + +#include "mem_area.h" + +struct hip_file_buffer; + +int hip_fb_create(struct hip_file_buffer *const fb, + const char *const file_name); +void hip_fb_delete(struct hip_file_buffer *const fb); +static inline const struct hip_mem_area *hip_fb_get_mem_area(const struct hip_file_buffer *const fb); +int hip_fb_reload(struct hip_file_buffer *const fb); + +#include "firewall/file_buffer_inline.h" + +#endif /* HIP_FIREWALL_FILE_BUFFER_H */ === added file 'firewall/file_buffer_inline.h' --- firewall/file_buffer_inline.h 1970-01-01 00:00:00 +0000 +++ firewall/file_buffer_inline.h 2010-11-12 17:49:42 +0000 @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2010 Aalto University and RWTH Aachen University. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file + * @author Stefan Goetz <stefan.goetz@xxxxxxxxxxxxxxxxx> + */ + +#ifndef HIP_FIREWALL_FILE_BUFFER_INLINE_H +#define HIP_FIREWALL_FILE_BUFFER_INLINE_H + +/* On the one hand, the contents of this file are part of the public interface + * and thus only their declaration should go into the public header file. + * On the other hand, these functions should be inlineable so their definitions + * have to appear in a header file. + * To achieve inlineability and still hide the implementation, we use this + * secondary header file that is not part of the public interface. */ +#ifndef HIP_FIREWALL_FILE_BUFFER_H +#error This file must not be included directly because it contains implementation details. It may only be included by file_buffer.h. +#endif + +#include <sys/types.h> +#include <stddef.h> + +#include "mem_area.h" + +/** + * A file buffer object represents an open file and its associated memory + * buffer. + */ +struct hip_file_buffer { + /** + * The memory area holding the file contents. + * Its start field points to the first byte of file data and the beginning + * of the allocated memory buffer. + * Its end field points to the last byte of file data + 1. + */ + struct hip_mem_area ma; + /* + * The number of bytes in the allocated buffer that ma.start points to. + * buffer_size is equal to or greater than (ma.end - ma.start). + */ + size_t buffer_size; + /* + * The file descriptor for the file backing the buffer. + */ + int fd; +}; + +/** + * Retrieve the memory area in which the file contents are stored. + * + * There is a 1:1 relationship between the passed in fb object and the returned + * pointer. + * That is, calling this function on the same fb object will always return the + * same struct hip_mem_area pointer. + * Thus, you may assume that the returned struct hip_mem_area pointer has the + * same life time as its associated struct hip_file_buffer object. + * However, hip_fb_reload() may change the start and end address in the + * returned struct hip_mem_area object! + * + * @param fb the file buffer object holding the memory area to retrieve. + * @return a pointer to the struct hip_mem_area object associated with the + * given file buffer object. + * If the passed in file buffer pointer is invalid, this function returns + * NULL. + */ +static inline const struct hip_mem_area *hip_fb_get_mem_area(const struct hip_file_buffer *const fb) +{ + if (fb) { + return &fb->ma; + } + return NULL; +} + +#endif /* HIP_FIREWALL_FILE_BUFFER_INLINE_H */ === modified file 'firewall/firewall.c' --- firewall/firewall.c 2010-10-15 15:29:14 +0000 +++ firewall/firewall.c 2010-10-24 21:32:10 +0000 @@ -83,7 +83,6 @@ #include "hipd/hipd.h" #include "config.h" #include "cache.h" -#include "cache_port.h" #include "common_types.h" #include "conntrack.h" #include "esp_prot_api.h" @@ -94,6 +93,7 @@ #include "lsi.h" #include "midauth.h" #include "pisa.h" +#include "port_bindings.h" #include "reinject.h" #include "rule_management.h" #include "user_ipsec_api.h" @@ -546,7 +546,7 @@ // Initializing local cache database hip_firewall_cache_init_hldb(); // Initializing local port cache database - hip_firewall_port_cache_init_hldb(); + hip_port_bindings_init(true); /* Initialize raw sockets for packet reinjection */ hip_firewall_init_raw_sockets(); @@ -674,7 +674,7 @@ } hip_firewall_cache_delete_hldb(1); - hip_firewall_port_cache_uninit_hldb(); + hip_port_bindings_uninit(); hip_fw_uninit_system_based_opp_mode(); hip_fw_flush_iptables(); /* rules have to be removed first, otherwise HIP packets won't pass through === added file 'firewall/line_parser.c' --- firewall/line_parser.c 1970-01-01 00:00:00 +0000 +++ firewall/line_parser.c 2010-11-12 17:49:42 +0000 @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2010 Aalto University and RWTH Aachen University. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file + * @author Stefan Goetz <stefan.goetz@xxxxxxxxxxxxxxxxx> + */ + +#include <stdlib.h> + +#include "file_buffer.h" +#include "line_parser.h" + +/** + * Initializes a parser that iterates over the lines of a given memory area. + * + * A line parser object is used to linearly iterate over the lines in a memory + * area that holds text. + * The memory area contents are not modified and the returned line pointers are + * terminated by newline characters, not null characters. + * + * When this function returns successfully, hip_lp_first() can be called + * immediately to start parsing. + * This function allocates resources, in particular memory, for the returned + * object. + * To free these resources and to avoid memory leaks, it is imperative to call + * hip_lp_delete() when the object created here is no longer used. + * + * @param lp a pointer to a valid, allocated instance of struct hip_line_parser. + * Upon successful completion, the function writes parser-specific context + * data to the location referenced by lp. + * @param ma the memory area to interpret as text and to parse by lines. + * @return 0 if the line parser lp was successfully initialized. + * This function return -1 if lp is NULL or if ma is NULL. + */ +int hip_lp_create(struct hip_line_parser *const lp, + const struct hip_mem_area *const ma) +{ + if (lp && ma) { + lp->ma = ma; + lp->cur = NULL; + return 0; + } + + return -1; +} + +/** + * Releases the resources allocated for a line parser object in + * hip_lp_create(). + * This does not include the memory pointed to by lp or the struct hip_mem_area + * object this parser was created with or the memory backing that memory area). + * + * @param lp the line parser object to delete. + * If lp is NULL, calling this function has no effect. + */ +void hip_lp_delete(struct hip_line_parser *const lp) +{ + if (lp) { + lp->ma = NULL; + lp->cur = NULL; + } +} === added file 'firewall/line_parser.h' --- firewall/line_parser.h 1970-01-01 00:00:00 +0000 +++ firewall/line_parser.h 2010-11-12 17:49:42 +0000 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2010 Aalto University and RWTH Aachen University. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file + * @author Stefan Goetz <stefan.goetz@xxxxxxxxxxxxxxxxx> + */ + +#ifndef HIP_FIREWALL_LINE_PARSER_H +#define HIP_FIREWALL_LINE_PARSER_H + +#include "mem_area.h" + +struct hip_line_parser; + +int hip_lp_create(struct hip_line_parser *const lp, + const struct hip_mem_area *const ma); +void hip_lp_delete(struct hip_line_parser *const lp); +static inline char *hip_lp_first(struct hip_line_parser *const lp); +static inline char *hip_lp_next(struct hip_line_parser *const lp); + +#include "firewall/line_parser_inline.h" + +#endif /* HIP_FIREWALL_LINE_PARSER_H */ === added file 'firewall/line_parser_inline.h' --- firewall/line_parser_inline.h 1970-01-01 00:00:00 +0000 +++ firewall/line_parser_inline.h 2010-11-12 17:49:42 +0000 @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2010 Aalto University and RWTH Aachen University. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file + * @author Stefan Goetz <stefan.goetz@xxxxxxxxxxxxxxxxx> + */ + +#ifndef HIP_FIREWALL_LINE_PARSER_INLINE_H +#define HIP_FIREWALL_LINE_PARSER_INLINE_H + +/* On the one hand, the contents of this file are part of the public interface + * and thus only their declaration should go into the public header file. + * On the other hand, these functions should be inlineable so their definitions + * have to appear in a header file. + * To achieve inlineability and still hide the implementation, we use this + * secondary header file that is not part of the public interface. */ +#ifndef HIP_FIREWALL_LINE_PARSER_H +#error This file must not be included directly because it contains implementation details. It may only be included by line_parser.h. +#endif + +#include <string.h> + +#include "lib/core/debug.h" +#include "file_buffer.h" + +/** + * Represents the parsing state on a memory area object. + */ +struct hip_line_parser { + /** + * The memory area this parser operates on. + */ + const struct hip_mem_area *ma; + /** + * The current parsing position. + * If NULL, hip_lp_first() needs to be called. + * If != NULL, points to the start of line in the memory buffer. + */ + char *cur; +}; + +/** + * Start a new parsing pass with a line parser and return the first line in the + * buffer. + * The buffer is not modified and the line is terminated by a newline + * character (not a null character). + * + * A parsing pass consists of starting it via hip_lp_first() and iterating over + * the lines in the file via hip_lp_next() until it returns NULL. + * + * @param lp the line parser to use. + * @return a pointer to the first line in the file or NULL if no line is + * available. + */ +static inline char *hip_lp_first(struct hip_line_parser *const lp) +{ + if (!lp || + !lp->ma) { + return NULL; + } + + lp->cur = lp->ma->start; + + return lp->cur; +} + +/** + * Get the next line in a parsing pass with a line parser. + * + * Each invocation of this function returns a pointer to consecutive lines in + * the buffer to parse. + * After the last line has been reached, NULL is returned. + * In that case, parsing can restart by calling hip_lp_first(). + * + * @param lp the line parser parser to use. + * @return a pointer to a line in the buffer or NULL if there are no more lines + * available. + */ +static inline char *hip_lp_next(struct hip_line_parser *const lp) +{ + size_t remaining; + + if (!lp || + !lp->cur || + !lp->ma || + !lp->ma->start || + !lp->ma->end || + lp->cur < lp->ma->start || + lp->cur >= lp->ma->end) { + return NULL; + } + + remaining = lp->ma->end - lp->cur; + lp->cur = memchr(lp->cur, '\n', remaining); + + // given the rest of the parsing code, we should always find a \n, but + // let's check to be sure + if (lp->cur) { + // cur should not point to the new-line character but to the next one: + lp->cur += 1; + // is there text on the line here or are we at the end? + if (lp->cur >= lp->ma->end) { + lp->cur = NULL; + } + } + + return lp->cur; +} + +#endif /* HIP_FIREWALL_LINE_PARSER_INLINE_H */ === modified file 'firewall/lsi.c' --- firewall/lsi.c 2010-10-15 15:29:14 +0000 +++ firewall/lsi.c 2010-10-24 22:20:24 +0000 @@ -64,7 +64,7 @@ #include "lib/core/prefix.h" #include "lib/core/protodefs.h" #include "cache.h" -#include "cache_port.h" +#include "port_bindings.h" #include "firewall.h" #include "lsi.h" #include "reinject.h" @@ -316,11 +316,9 @@ int verdict = 1; int ip_hdr_size = 0; int portDest = 0; - int process_as_lsi = 0; fw_cache_hl_t *entry = NULL; - const struct firewall_port_cache_hl *port_cache_entry = NULL; + enum hip_port_binding port_binding = HIP_PORT_INFO_UNKNOWN; const struct ip6_hdr *ip6_hdr = NULL; - char proto[PROTO_STRING_MAX]; struct in6_addr src_addr, dst_addr; ip6_hdr = (const struct ip6_hdr *) m->payload; @@ -329,11 +327,9 @@ switch (ip6_hdr->ip6_nxt) { case IPPROTO_UDP: portDest = ((const struct udphdr *) ((m->payload) + ip_hdr_size))->dest; - strcpy(proto, "udp6"); break; case IPPROTO_TCP: portDest = ((const struct tcphdr *) ((m->payload) + ip_hdr_size))->dest; - strcpy(proto, "tcp6"); break; case IPPROTO_ICMPV6: HIP_DEBUG("ICMPv6 packet\n"); @@ -343,50 +339,45 @@ break; } - /* port caching */ - port_cache_entry = hip_firewall_port_cache_db_match(portDest, - ip6_hdr->ip6_nxt); + port_binding = hip_port_bindings_get(ip6_hdr->ip6_nxt, + portDest); - if (port_cache_entry && - (port_cache_entry->traffic_type == - FIREWALL_PORT_CACHE_IPV6_TRAFFIC)) { + if (port_binding == HIP_PORT_INFO_IPV6BOUND) { + HIP_DEBUG("Port %d is bound to an IPv6 address -> accepting packet\n", portDest); verdict = 1; - HIP_DEBUG("Cached port, accepting\n"); - goto out_err; - } + } else if (port_binding == HIP_PORT_INFO_IPV6UNBOUND) { + HIP_DEBUG("Port %d is unbound or bound to an IPv4 address -> looking up in cache\n", portDest); + HIP_IFEL(!(entry = hip_firewall_cache_db_match(ip_dst, ip_src, + FW_CACHE_HIT, 1)), + -1, "Failed to obtain from cache\n"); - if (lsi_support) { /* Currently preferring LSIs over opp. connections */ - process_as_lsi = 1; - } - - HIP_IFEL(!(entry = hip_firewall_cache_db_match(ip_dst, ip_src, - FW_CACHE_HIT, 1)), - -1, "Failed to obtain from cache\n"); - - if (process_as_lsi) { - HIP_DEBUG("Trying lsi transformation\n"); - HIP_DEBUG_LSI("lsi_our: ", &entry->lsi_our); - HIP_DEBUG_LSI("lsi_peer: ", &entry->lsi_peer); - IPV4_TO_IPV6_MAP(&entry->lsi_our, &dst_addr); - IPV4_TO_IPV6_MAP(&entry->lsi_peer, &src_addr); - HIP_IFEL(hip_reinject_packet(&src_addr, &dst_addr, m, 6, 1), -1, - "Failed to reinject with LSIs\n"); - HIP_DEBUG("Successful LSI transformation.\n"); - - if (ip6_hdr->ip6_nxt == IPPROTO_ICMPV6) { - verdict = 1; /* broadcast: dst may be ipv4 or ipv6 */ + if (lsi_support) { + HIP_DEBUG("Trying lsi transformation\n"); + HIP_DEBUG_LSI("lsi_our: ", &entry->lsi_our); + HIP_DEBUG_LSI("lsi_peer: ", &entry->lsi_peer); + IPV4_TO_IPV6_MAP(&entry->lsi_our, &dst_addr); + IPV4_TO_IPV6_MAP(&entry->lsi_peer, &src_addr); + HIP_IFEL(hip_reinject_packet(&src_addr, &dst_addr, m, 6, 1), -1, + "Failed to reinject with LSIs\n"); + HIP_DEBUG("Successful LSI transformation.\n"); + + if (ip6_hdr->ip6_nxt == IPPROTO_ICMPV6) { + verdict = 1; /* broadcast: dst may be ipv4 or ipv6 */ + } else { + verdict = 0; /* drop original */ + } } else { - verdict = 0; /* drop original */ + HIP_DEBUG("Trying sys opp transformation\n"); + HIP_DEBUG_IN6ADDR("ip_src: ", &entry->ip_peer); + HIP_DEBUG_IN6ADDR("ip_dst: ", &entry->ip_our); + HIP_IFEL(hip_reinject_packet(&entry->ip_peer, &entry->ip_our, m, 6, 1), + -1, "Failed to reinject with IP addrs\n"); + HIP_DEBUG("Successfull sysopp transformation. Drop orig\n"); + verdict = 0; } } else { - HIP_DEBUG("Trying sys opp transformation\n"); - HIP_DEBUG_IN6ADDR("ip_src: ", &entry->ip_peer); - HIP_DEBUG_IN6ADDR("ip_dst: ", &entry->ip_our); - HIP_IFEL(hip_reinject_packet(&entry->ip_peer, &entry->ip_our, m, 6, 1), - -1, "Failed to reinject with IP addrs\n"); - HIP_DEBUG("Successfull sysopp transformation. Drop orig\n"); - verdict = 0; + HIP_DIE("hip_port_bindings_get() returned unknown return value %d\n", port_binding); } out_err: === added file 'firewall/mem_area.h' --- firewall/mem_area.h 1970-01-01 00:00:00 +0000 +++ firewall/mem_area.h 2010-11-12 17:49:42 +0000 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2010 Aalto University and RWTH Aachen University. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file + * @author Stefan Goetz <stefan.goetz@xxxxxxxxxxxxxxxxx> + */ + +#ifndef HIP_FIREWALL_MEM_AREA_H +#define HIP_FIREWALL_MEM_AREA_H + +/** + * A range of memory with a defined start and end. + * + * This structure is useful where the information is needed about a specific + * memory area, e.g., for a file loaded into memory. + */ +struct hip_mem_area { + char *start; + char *end; +}; + +#endif === renamed file 'firewall/cache_port.c' => 'firewall/port_bindings.c' --- firewall/cache_port.c 2010-10-15 15:29:14 +0000 +++ firewall/port_bindings.c 2010-11-12 17:49:42 +0000 @@ -25,277 +25,465 @@ /** * @file - * Cache TCP and UDP port information for incoming HIP-related connections for - * LSIs. When hipfw sees an incoming HIT-based connection, it needs to figure out if - * it needs to be translated to LSI or not. LSI translation is done only when there is - * no IPv6 application bound the corresponding TCP or UDP port. The port information - * can be read from /proc but consumes time. To avoid this overhead, hipfw caches - * the port information after the first read. Notice that cache is static and hipfw - * must be restarted if there are changes in the port numbers. This is described in - * more detail in <a - * href="http://hipl.hiit.fi/hipl/thesis_teresa_finez.pdf";>T. Finez, - * Backwards Compatibility Experimentation with Host Identity Protocol - * and Legacy Software and Networks , final project, December 2008</a>. - * - * @brief Cache TCP and UDP port numbers for inbound HIP-related connections to optimize LSI translation - * - * @author Miika Komu <miika@xxxxxx> + * @brief Look up whether a port corresponds to a local bound socket, which influences LSI handling. + * + * @author Miika Komu <miika@xxxxxx>, Stefan Goetz <stefan.goetz@xxxxxxxxxxxxxxxxx> */ -#include <stdio.h> +#include <netinet/in.h> +#include <errno.h> +#include <signal.h> #include <stdint.h> +#include <stdlib.h> #include <string.h> -#include <netinet/in.h> +#include <time.h> +#include <unistd.h> -#include "lib/core/builder.h" +#include "lib/core/common.h" #include "lib/core/debug.h" -#include "lib/core/hashtable.h" -#include "lib/core/icomm.h" -#include "lib/core/list.h" -#include "lib/core/prefix.h" -#include "lib/tool/lutil.h" -#include "cache.h" -#include "cache_port.h" - - -static HIP_HASHTABLE *firewall_port_cache_db = NULL; - -/** - * Check from the proc file system whether a local port is attached - * to an IPv4 or IPv6 address. This is required to determine whether - * incoming packets should be diverted to an LSI. - * - * @param port_dest the port number of the socket - * @param *proto protocol type - * @return 1 if it finds the required socket, 0 otherwise - * - * @note this is used only from the firewall, so move this there - */ -static int hip_get_proto_info(in_port_t port_dest, char *proto) -{ - FILE *fd = NULL; - char line[500], sub_string_addr_hex[8], path[11 + sizeof(proto)]; - char *fqdn_str = NULL, *separator = NULL, *sub_string_port_hex = NULL; - int lineno = 0, index_addr_port = 0, exists = 0, result; - uint32_t result_addr; - struct in_addr addr; - List list; - - if (!proto) { - return 0; - } - - if (!strcmp(proto, "tcp6") || !strcmp(proto, "tcp")) { - index_addr_port = 15; - } else if (!strcmp(proto, "udp6") || !strcmp(proto, "udp")) { - index_addr_port = 10; - } else { - return 0; - } - - strcpy(path, "/proc/net/"); - strcat(path, proto); - fd = fopen(path, "r"); - - initlist(&list); - while (fd && getwithoutnewline(line, 500, fd) != NULL && !exists) { - lineno++; - - destroy(&list); - initlist(&list); - - if (lineno == 1 || strlen(line) <= 1) { - continue; - } - - extractsubstrings(line, &list); - - fqdn_str = getitem(&list, index_addr_port); - if (fqdn_str) { - separator = strrchr(fqdn_str, ':'); - } - - if (!separator) { - continue; - } - - sub_string_port_hex = strtok(separator, ":"); - sscanf(sub_string_port_hex, "%X", &result); - HIP_DEBUG("Result %i\n", result); - HIP_DEBUG("port dest %i\n", port_dest); - if (result == port_dest) { - strncpy(sub_string_addr_hex, fqdn_str, 8); - sscanf(sub_string_addr_hex, "%X", &result_addr); - addr.s_addr = result_addr; - if (IS_LSI32(addr.s_addr)) { - exists = 2; - break; - } else { - exists = 1; - break; - } - } - } /* end of while */ - if (fd) { - fclose(fd); - } - destroy(&list); - - return exists; -} - -/** - * add a default entry in the firewall port cache. - * - * @param key the hash key (a string consisting of concatenation of the port, an underscore and the protocol) - * @param value the value for the hash key (LSI mode value) - * - * @return zero on success or non-zero on failure - */ -static int hip_port_cache_add_new_entry(const char *key, int value) -{ - struct firewall_port_cache_hl *new_entry = NULL; +#include "lib/core/ife.h" +#include "line_parser.h" +#include "port_bindings.h" + +/** + * The number of seconds after which hip_port_bindings_trigger_reload() is + * called periodically. + * The smaller this number, the more up-to-date information is returned by + * hip_port_bindings_get(). + * At the same time, a small interval also causes the somewhat expensive + * hip_port_bindings_reload() to be called more frequently. + */ +const unsigned int INVALIDATION_INTERVAL = 1; + +/** + * Indicates whether the caches should be invalidated. + * This is periodically set to 1 by a timer and reset the next time a lookup is + * performed. + */ +volatile sig_atomic_t cache_invalidation_flag = 1; + +/** + * Pointer to the port bindings cache. + * + * The cache is a two-dimensional array. + * The first dimension is the transport protocol for which a port can be bound + * (supported are TCP and UDP). + * The second dimension is the port number itself. + * The value is a uint8_t representation of an enum hip_port_binding value + */ +static uint8_t *cache = NULL; + +static const unsigned int CACHE_SIZE_PROTOS = 2; +static const unsigned int CACHE_SIZE_PORTS = 1 << (sizeof(in_port_t) * 8); +static unsigned int cache_size_entries = 0; +static unsigned int cache_size_bytes = 0; + +/** + * Allocate and initializes the cache resources. + * If this function has not been called first, the results of calling + * cache_get() and cache_set() are undefined. + * + * @return 0 if the function completes successfully. + * If the memory for the cache could not be allocated, this function returns + * -1. + */ +static int init_cache(void) +{ + HIP_ASSERT(!cache); + + cache_size_entries = CACHE_SIZE_PROTOS * CACHE_SIZE_PORTS; + cache_size_bytes = cache_size_entries * sizeof(*cache); + + // check that the conversion used in the cache from enum hip_port_binding + // to uint8_t is consistent + HIP_ASSERT(HIP_PORT_INFO_IPV6UNBOUND == (enum hip_port_binding)(uint8_t)HIP_PORT_INFO_IPV6UNBOUND); + HIP_ASSERT(HIP_PORT_INFO_IPV6BOUND == (enum hip_port_binding)(uint8_t)HIP_PORT_INFO_IPV6BOUND); + + /* We zero the cache on allocation assuming that HIP_PORT_INFO_UNKNOWN + is 0 and thus the whole cache initially has that value. */ + HIP_ASSERT((uint8_t)HIP_PORT_INFO_UNKNOWN == 0); + cache = calloc(1, cache_size_bytes); + if (cache) { + return 0; + } else { + HIP_ERROR("Allocating the port bindings cache failed\n"); + return -1; + } +} + +/** + * Release the cache resources. + * After calling this function, the results of calling cache_get() and + * cache_set() are undefined. + */ +static void uninit_cache(void) +{ + free(cache); + cache = NULL; +} + +/** + * Determines the index of a cache entry. + * The cache array should only be indexed via this function. + * + * The flat cache entry index can be used to access the cache as a + * one-dimensional array. + * Using it is not strictly necessary because it would be possible and more + * beautiful to behold to access the cache as a two-dimensional array, but the + * one-dimensional flat index determined here can also be used for bounds + * checking. + * + * @param protocol the protocol the specified port belongs to. + * The value is the same as used in the IPv4 'protocol' and the IPv6 'Next + * Header' fields. + * The only supported values are 6 for TCP and 17 for UDP. + * @param port the port in host byte order to get the port binding for. + * Valid values range from 0 to 2^16-1. + * @return the index of the cache entry for @a protocol and @a port. + */ +static inline unsigned int get_cache_index(const uint8_t protocol, + const uint16_t port) +{ + unsigned int index = 0; + unsigned int protocol_offset = 0; + + // determine the offset into the first (protocol) dimension + if (IPPROTO_TCP == protocol) { + protocol_offset = 0; + } else if (IPPROTO_UDP == protocol) { + protocol_offset = 1; + } else { + HIP_DIE("Invalid protocol"); + } + + // calculate the index + index = (protocol_offset * CACHE_SIZE_PORTS) + port; + + return index; +} + +/** + * Cache binding state on the port of a given protocol. + * + * This function is called after looking up port binding status from the /proc + * file system. + * After it has been called, a call to hip_firewall_port_cache_set() with the + * same protocol and port returns the previously set port binding. + * + * @param protocol the protocol the specified port belongs to. + * The value is the same as used in the IPv4 'protocol' and the IPv6 'Next + * Header' fields. + * The only supported values are 6 for TCP and 17 for UDP. + * @param port the port in host byte order to set the port binding for. + * Valid values range from 0 to 2^16-1. + * @param binding the binding to store in the cache. + */ +static void set_cache_entry(const uint8_t protocol, + const uint16_t port, + const enum hip_port_binding binding) +{ + // fail gracefully if the cache is not allocated + if (cache) { + // calculate index of cache entry + const unsigned int index = get_cache_index(protocol, port); + + // convert the port binding to the cache storage type + const uint8_t value = (uint8_t)binding; + + cache[index] = value; + } +} + +/** + * Retrieve port binding for a given protocol from the cache. + * + * Looking up the port binding from the /proc file systems is relatively + * expensive. + * Thus, we use this cache to speed up the lookup. + * + * This function is called before looking up the port binding from the /proc + * file system. + * + * @param protocol the protocol the specified port belongs to. + * The value is the same as used in the IPv4 'protocol' and the IPv6 'Next + * Header' fields. + * The only supported values are 6 for TCP and 17 for UDP. + * @param port the port in host byte order to set the port binding for. + * Valid values range from 0 to 2^16-1. + * @return If the port binding was previously stored, it is returned. + * If the port binding was not previously stored or the cache is not + * available, HIP_PORT_INFO_UNKNOWN is returned. + */ +static enum hip_port_binding get_cache_entry(const uint8_t protocol, + const uint16_t port) +{ + enum hip_port_binding binding = HIP_PORT_INFO_UNKNOWN; + + // fail gracefully if cache is not available + if (cache) { + const unsigned int index = get_cache_index(protocol, port); + + binding = (enum hip_port_binding)cache[index]; + } + + return binding; +} + +/** + * Invalidate all cache entries. + * + * After calling this function, all valid invocations of get_cache_entry() + * return HIP_PORT_INFO_UNKNOWN. + */ +static void invalidate_cache(void) +{ + if (cache) { + memset(cache, HIP_PORT_INFO_UNKNOWN, cache_size_bytes); + } +} + + + + + + +static struct hip_file_buffer tcp6_file; +static struct hip_file_buffer udp6_file; + +/** + * Handles ALRM signals and triggers caches to be reloaded. + * After this function has been called, the next call to + * hip_port_bindings_get() calls hip_port_bindings_reload() to invalidate the + * port and the file caches before performing a port lookup. + * This tries to strike a balance between the cost of + * hip_port_bindings_reload() and the freshness of the lookup information + * returned by hip_port_bindings_get(). + * This function is called every INVALIDATION_INTERVAL seconds. + */ +static void hip_port_bindings_trigger_reload(UNUSED const int sig) +{ + cache_invalidation_flag = 1; +} + +/** + * Load the latest information from /proc. + * This consists of handling two separate caching layers: + * a) re-reading the file contents in the tcp6/udp6 file buffer objects and + * b) invalidating the lookup cache. + * On the one hand, this operation should ideally be called for every call to + * hip_port_bindings_get() to retrieve up-to-date information from /proc + * about which ports are bound. + * On the other hand, this operation is about 300 times more expensive than + * parsing the /proc file and even more expensive compared to a cache lookup. + * hip_port_bindings_trigger_reload() tries to balance this conflict. + * After calling this function, the cache is empty and the file buffers contain + * the up-to-date file contents from /proc. + * + * @todo TODO efficiency could be increased by narrowing this down from + * reloading the files and invalidating the caches of all protocols to + * individual protocols. + * + * @return If this function completes successfully, it returns 0. + * If one of the proc files could not be reloaded from the file system, this + * function returns -1. + */ +static int hip_port_bindings_reload(void) +{ int err = 0; - HIP_DEBUG("\n"); - new_entry = (struct firewall_port_cache_hl *) (hip_cache_create_hl_entry()); - memcpy(new_entry->port_and_protocol, key, strlen(key)); - new_entry->traffic_type = value; - hip_ht_add(firewall_port_cache_db, new_entry); - - return err; + invalidate_cache(); + + err = hip_fb_reload(&tcp6_file); + err |= hip_fb_reload(&udp6_file); + + return (err == 0) ? 0 : -1; } /** - * Search in the port cache database. The key composed of port and protocol - * - * @param port the TCP or UDP port to search for - * @param proto the protocol (IPPROTO_UDP, IPPROTO_TCP or IPPROTO_ICMPV6) - * - * @return the cache entry if found or NULL otherwise + * Look up the port binding from the proc file system. + * + * @param protocol protocol type + * @param port the port number of the socket + * @return the traffic type associated with the given port. */ -struct firewall_port_cache_hl *hip_firewall_port_cache_db_match(in_port_t port, - int proto) +static enum hip_port_binding hip_port_bindings_get_from_proc(const uint8_t protocol, + const uint16_t port) { - struct firewall_port_cache_hl *found_entry = NULL; - char key[FIREWALL_PORT_CACHE_KEY_LENGTH]; - char protocol[10], proto_for_bind[10]; - int bindto = FIREWALL_PORT_CACHE_IPV4_TRAFFIC; //3 - default to ipv4, non-LSI traffic - - memset(protocol, 0, sizeof(protocol)); - memset(proto_for_bind, 0, sizeof(proto_for_bind)); - memset(key, 0, sizeof(key)); - - switch (proto) { + const unsigned int PORT_STR_OFFSET = 39; + const unsigned int PORT_STR_LEN = 4; + enum hip_port_binding result = HIP_PORT_INFO_IPV6UNBOUND; + const struct hip_mem_area *ma; + char *line; + // the files /proc/net/{udp,tcp}6 are line-based and the line number of the + // port to look up is not known in advance + // -> use a parser that lets us iterate over the lines in the files + struct hip_line_parser lp; + + switch (protocol) { + case IPPROTO_TCP: + ma = hip_fb_get_mem_area(&tcp6_file); + break; case IPPROTO_UDP: - strcpy(protocol, "udp"); - strcpy(proto_for_bind, "udp6"); - break; - case IPPROTO_TCP: - strcpy(protocol, "tcp"); - strcpy(proto_for_bind, "tcp6"); - break; - case IPPROTO_ICMPV6: - strcpy(protocol, "icmp"); - break; - default: - goto out_err; - break; - } - - //assemble the key - sprintf(key, "%i", (int) port); - memcpy(key + strlen(key), "_", 1); - memcpy(key + strlen(key), protocol, strlen(protocol)); - - found_entry = hip_ht_find(firewall_port_cache_db, key); - - if (proto == IPPROTO_ICMPV6) { - goto out_err; - } - - if (!found_entry) { - bindto = hip_get_proto_info(ntohs(port), proto_for_bind); - hip_port_cache_add_new_entry(key, bindto); - found_entry = hip_ht_find(firewall_port_cache_db, key); - } else { - HIP_DEBUG("Matched port using hash\n"); - } + ma = hip_fb_get_mem_area(&udp6_file); + break; + } + hip_lp_create(&lp, ma); + + // Note that here we blindly parse whatever is in the file buffer. + // This may not be up-to-date compared to the actual /proc file. + // We rely on someone else calling hip_port_bindings_reload() to + // reload the file contents for us so that we return some at least roughly + // up-to-date information. + line = hip_lp_first(&lp); + + // the first line only contains headers, no port information, skip it + line = hip_lp_next(&lp); + + // is the current line valid and is it long enough to hold a port binding? + while (line && ma->end > (line + PORT_STR_OFFSET + PORT_STR_LEN)) { + const unsigned int PORT_BASE_HEX = 16; + unsigned long proc_port = 0; + // note that strtoul() is about 10 times faster than sscanf(). + errno = 0; + proc_port = strtoul(line + PORT_STR_OFFSET, NULL, PORT_BASE_HEX); + if (0 == errno) { + if (proc_port == port) { + result = HIP_PORT_INFO_IPV6BOUND; + break; + } + } else { + HIP_ERROR("Unable to parse port number in line '%.*s' from /proc/net/%s6, errno = %d\n", + PORT_STR_OFFSET + PORT_STR_LEN, line, + IPPROTO_TCP == protocol ? "tcp" : "udp", errno); + } + line = hip_lp_next(&lp); + } + + hip_lp_delete(&lp); + return result; +} + +/** + * Initialize the port binding lookup and allocate any necessary resources. + * + * @param enable_cache if not 0, use an internal cache that is consulted on + * lookups in favor of parsing the /proc file. + * If this lookup cache is not enabled, every lookup results in parsing the + * proc file. + * Note however, that the /proc file itself is cached in memory and only + * reloaded at a certain interval. + * Within this interval, hip_port_bindings_get() might return a different + * port binding status than the one in the actual /proc file. + * @return 0 if the function completes successfully. + * If enable_cache is true but the cache could not be allocated or initialized + * this function returns -1. + * If one of the /proc files could not be opened or buffered in memory + * successfully, this function returns -2. + */ +int hip_port_bindings_init(const bool enable_cache) +{ + int err; + + // The cache is built such that it can be disabled just by not initializing + // it here. + if (enable_cache) { + HIP_IFEL(init_cache() != 0, -1, + "Initializing the port bindings cache failed\n") + } + + HIP_IFEL(hip_fb_create(&tcp6_file, "/proc/net/tcp6") != 0, -2, + "Buffering tcp6 proc file in memory failed\n"); + HIP_IFEL(hip_fb_create(&udp6_file, "/proc/net/udp6") != 0, -2, + "Buffering udp6 proc file in memory failed\n"); + + return 0; out_err: - return found_entry; -} - -/** - * Generate the hash information that is used to index the table - * - * @param ptr pointer to the hit used to assemble the hash - * - * @return hash value - */ -static unsigned long hip_firewall_port_hash_key(const void *ptr) -{ - const char *key; - uint8_t hash[HIP_AH_SHA_LEN]; - - key = (const char *) - &((const struct firewall_port_cache_hl *) ptr)->port_and_protocol; - hip_build_digest(HIP_DIGEST_SHA1, key, sizeof(*key), hash); - return *((unsigned long *) hash); -} - -/** - * Compare two keys for the hashtable - * - * Note that when this function is called, the hashes of the two hash table - * entries provided as arguments are known to be equal. - * The point of this function is to allow the hash table to determine whether - * the entries (or rather the part used to calculate the hash) themselves are - * equal or whether they are different and this is just a hash collision. - * - * @param ptr1 pointer to the first key - * @param ptr2 pointer to the second key - * - * @return 0 if keys identical, otherwise != 0 - */ -static int hip_firewall_match_port_cache_key(const void *ptr1, const void *ptr2) -{ - return strncmp((const char *)ptr1, (const char *)ptr2, FIREWALL_PORT_CACHE_KEY_LENGTH); -} - -/** - * Initialize port cache database - * - */ -void hip_firewall_port_cache_init_hldb(void) -{ - firewall_port_cache_db = hip_ht_init(hip_firewall_port_hash_key, - hip_firewall_match_port_cache_key); -} - -/** - * Initialize port cache database - * - */ -void hip_firewall_port_cache_uninit_hldb(void) -{ - int i; - struct firewall_port_cache_hl *this = NULL; - hip_list_t *item = NULL; - hip_list_t *tmp = NULL; - - HIP_DEBUG("Start hldb delete\n"); - HIP_LOCK_HT(&firewall_port_cache_db); - - list_for_each_safe(item, tmp, firewall_port_cache_db, i) - { - HIP_DEBUG("xx\n"); - this = (struct firewall_port_cache_hl *) list_entry(item); - hip_ht_delete(firewall_port_cache_db, this); - free(this); - HIP_DEBUG("yy\n"); + hip_port_bindings_uninit(); + return err; +} + +/** + * Release any resources allocated for port binding lookups. + */ +void hip_port_bindings_uninit(void) +{ + hip_fb_delete(&tcp6_file); + hip_fb_delete(&udp6_file); + + uninit_cache(); +} + +/** + * Determine whether the given port is bound under the given protocol to an + * IPv6 address on the local host. + * + * For example, on a system with a running IPv6-capable web server, this + * function returns HIP_PORT_INFO_IPV6BOUND. + * If there is no web server or it only supports (or binds to) IPv4 addresses, + * this function returns HIP_PORT_INFO_IPV6UNBOUND. + * + * Note that due to internal caching, hip_port_bindings_get() might return for + * a certain caching interval a different port binding status than the one + * reported in the actual /proc file (see hip_port_bindings_trigger_reload()). + * + * The binary test/fw_port_bindings_performance benchmarks the elements that + * influence the performance of the hip_port_bindings_* code. + * Please have a look at the numbers it generates when changing this code under + * performance aspects. + * + * @param protocol the protocol to check the port binding for. + * The values are equivalent to those found in the 'Protocol' field of the + * IPv4 header and the 'Next Header' field of the IPv6 header. + * The only supported values are those for TCP (6) and UDP (17). + * @param port the port to look up. + * The value is expected in network byte order, as it is found in protocol + * headers. + * @return HIP_PORT_INFO_IPV6BOUND if the given port is bound under the given + * protocol to an IPv6 address. + * HIP_PORT_INFO_IPV6UNBOUND if it is not. + */ +enum hip_port_binding hip_port_bindings_get(const uint8_t protocol, + const in_port_t port) +{ + enum hip_port_binding binding = HIP_PORT_INFO_IPV6UNBOUND; + + // check input parameters + if (IPPROTO_TCP == protocol || + IPPROTO_UDP == protocol) { + const uint16_t port_hbo = ntohs(port); + + // Make sure we return (sort of) up-to-date information. + // This is the one potentially slow operation here. + // The others (hip_port_bindings_get_from_proc() and the cache access + // functions) are (intended to be) very fast. + if (cache_invalidation_flag) { + // prevent further cache invalidation + cache_invalidation_flag = 0; + // invalidate all caches and reload the /proc-file contents + hip_port_bindings_reload(); + // start a timer that sets cache_invalidation_flag back to 1 + signal(SIGALRM, hip_port_bindings_trigger_reload); + alarm(INVALIDATION_INTERVAL); + } + + // check the cache before checking /proc + // note that the cache might be switched off (see + // hip_port_bindings_init()) or was just invalidated by + // hip_port_bindings_reload() + binding = get_cache_entry(protocol, port_hbo); + + if (HIP_PORT_INFO_UNKNOWN == binding) { + binding = hip_port_bindings_get_from_proc(protocol, port_hbo); + set_cache_entry(protocol, port_hbo, binding); + } + } else { + HIP_ERROR("Protocol %d not supported\n", protocol); } - HIP_UNLOCK_HT(&firewall_port_cache_db); - hip_ht_uninit(firewall_port_cache_db); - HIP_DEBUG("End hldbdb delete\n"); + + // check return value + HIP_ASSERT(HIP_PORT_INFO_IPV6UNBOUND == binding || + HIP_PORT_INFO_IPV6BOUND == binding); + + return binding; } === renamed file 'firewall/cache_port.h' => 'firewall/port_bindings.h' --- firewall/cache_port.h 2010-10-15 15:29:14 +0000 +++ firewall/port_bindings.h 2010-11-12 17:49:42 +0000 @@ -23,16 +23,51 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -#ifndef HIP_FIREWALL_CACHE_PORT_H -#define HIP_FIREWALL_CACHE_PORT_H +/** + * @file + * The port binding says whether a port is locally bound to an IPv6 + * application or not. + * This allows the firewall to determine whether an incoming HIT-based packet + * is meant to go to a local IPv6 port or not. + * If not, the packet needs to be converted to IPv4 and sent to an LSI. + * More details can be found in <a + * href="http://hipl.hiit.fi/hipl/thesis_teresa_finez.pdf";>T. Finez, + * Backwards Compatibility Experimentation with Host Identity Protocol + * and Legacy Software and Networks , final project, December 2008</a>. + * + * @author Miika Komu <miika@xxxxxx>, Stefan Goetz <stefan.goetz@xxxxxxxxxxxxxxxxx> + */ + +#ifndef HIP_FIREWALL_PORT_BINDINGS_H +#define HIP_FIREWALL_PORT_BINDINGS_H #include <netinet/in.h> - -#include "lib/core/icomm.h" - -void hip_firewall_port_cache_init_hldb(void); -struct firewall_port_cache_hl *hip_firewall_port_cache_db_match(in_port_t port, - int proto); -void hip_firewall_port_cache_uninit_hldb(void); - -#endif /* HIP_CACHE_H */ +#include <stdbool.h> + +/** + * The binding state of a particular TCP or UDP port under IPv6 on the local + * host. + */ +enum hip_port_binding { + /** + * It is not known which network protocol the port is bound under. + */ + HIP_PORT_INFO_UNKNOWN = 0, + /** + * The port is not bound to an IPv6 address (but potentially to an + * IPv4 address). + */ + HIP_PORT_INFO_IPV6UNBOUND, + /** + * The port is bound to an IPv6 address (and potentially to an IPv4 + * address) + */ + HIP_PORT_INFO_IPV6BOUND, +}; + +int hip_port_bindings_init(const bool enable_cache); +void hip_port_bindings_uninit(void); +enum hip_port_binding hip_port_bindings_get(const uint8_t proto, + const in_port_t port); + +#endif /* HIP_FIREWALL_PORT_BINDINGS_H */ === modified file 'lib/core/icomm.h' --- lib/core/icomm.h 2010-10-15 15:29:14 +0000 +++ lib/core/icomm.h 2010-10-15 22:56:42 +0000 @@ -197,18 +197,4 @@ #define FLUSH_HA_INFO_DB 1 - -/****** FIREWALL ******/ - -/*----Firewall cache----*/ -/*Values for the port cache of the firewall*/ -#define FIREWALL_PORT_CACHE_IPV6_TRAFFIC 1 -#define FIREWALL_PORT_CACHE_IPV4_TRAFFIC 3 -#define FIREWALL_PORT_CACHE_KEY_LENGTH 20 - -struct firewall_port_cache_hl { - char port_and_protocol[FIREWALL_PORT_CACHE_KEY_LENGTH]; //key - int traffic_type; //value -}; - #endif /* HIP_LIB_CORE_ICOMM_H */ === added file 'test/check_firewall.c' --- test/check_firewall.c 1970-01-01 00:00:00 +0000 +++ test/check_firewall.c 2010-11-12 17:49:42 +0000 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2010 Aalto University and RWTH Aachen University. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <check.h> +#include <stdlib.h> + +/* Import test suite functions from their respective C files via forward + * declarations. + * Since each test C file exports only one such function which is only used + * right here, a dedicated header file for each of them adds unnecessary file + * clutter in this particular case of unit tests. + * Do not adopt this HFAS (header-file-avoidance-scheme) (TM) in HIPL production + * code as header files are generally a good idea, just not here. */ +extern Suite *firewall_file_buffer(void); +extern Suite *firewall_line_parser(void); +extern Suite *firewall_port_bindings(void); + +int main(void) +{ + int number_failed; + SRunner *sr = srunner_create(firewall_file_buffer()); + srunner_add_suite(sr, firewall_line_parser()); + srunner_add_suite(sr, firewall_port_bindings()); + + srunner_run_all(sr, CK_NORMAL); + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} === added directory 'test/firewall' === added file 'test/firewall/file_buffer.c' --- test/firewall/file_buffer.c 1970-01-01 00:00:00 +0000 +++ test/firewall/file_buffer.c 2010-11-12 17:49:42 +0000 @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2010 Aalto University and RWTH Aachen University. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <assert.h> +#include <check.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "firewall/file_buffer.h" +#include "firewall/file_buffer.c" + +// these tests do not clean up after themselves because they assume that +// check runs them in dedicated processes so the OS does the cleanup + +START_TEST(test_hip_fb_create_valid) +{ + struct hip_file_buffer fb; + + fail_unless(hip_fb_create(&fb, "/etc/fstab") == 0, + "Opening the file '/etc/fstab' failed - does it exist?"); +} +END_TEST + +START_TEST(test_hip_fb_create_null_buffer) +{ + fail_unless(hip_fb_create(NULL, "/etc/fstab") == -1, NULL); +} +END_TEST + +START_TEST(test_hip_fb_create_null_file_name) +{ + struct hip_file_buffer fb; + + fail_unless(hip_fb_create(&fb, NULL) == -1, NULL); +} +END_TEST + +START_TEST(test_hip_fb_create_missing_file) +{ + struct hip_file_buffer fb; + + fail_unless(hip_fb_create(&fb, + "XhUp8vH5sbye3izn25XbY3EVu9VcAsNC42WOCVYxAkMXiFo7cuh4Zsp2jHgfJ2OsBUTamYDuSyB9oSuAYEfBJA6EaEXBpNCp2l88Zo2yaWaLw6pB5mh61dlBDQZjaqlS") == -2, + NULL); +} +END_TEST + +START_TEST(test_hip_fb_create_check_file_integrity) +{ + struct hip_file_buffer fb; + const struct hip_mem_area *ma = NULL; + int fd = -1; + const char FILE_NAME_PATTERN[] = "XXXXXX"; + char file_name[sizeof(FILE_NAME_PATTERN)]; + const char file_contents[] = "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"; + size_t bytes_written = 0; + + strncpy(file_name, FILE_NAME_PATTERN, sizeof(file_name)); + + // create a file and write well-known data into it + fd = mkstemp(file_name); + assert(fd != -1); + bytes_written = write(fd, file_contents, sizeof(file_contents)); + assert(sizeof(file_contents) == bytes_written); + close(fd); + + fail_unless(hip_fb_create(&fb, file_name) == 0, NULL); + fail_unless((ma = hip_fb_get_mem_area(&fb)) != NULL, NULL); + fail_unless((ma->end - ma->start) == sizeof(file_contents)); + fail_unless(memcmp(ma->start, file_contents, sizeof(file_contents)) == 0, + NULL); + + fd = open(file_name, O_RDWR | O_APPEND); + assert(fd != -1); + bytes_written = write(fd, file_contents, sizeof(file_contents)); + assert(sizeof(file_contents) == bytes_written); + close(fd); + + fail_unless(hip_fb_reload(&fb) == 0, NULL); + fail_unless(ma == hip_fb_get_mem_area(&fb), NULL); + fail_unless((ma->end - ma->start) == sizeof(file_contents) * 2); + fail_unless(memcmp(ma->start, file_contents, sizeof(file_contents)) == 0, + NULL); + fail_unless(memcmp(ma->start + sizeof(file_contents), file_contents, + sizeof(file_contents)) == 0, NULL); + + remove(file_name); +} +END_TEST + +START_TEST(test_hip_fb_delete_valid) +{ + struct hip_file_buffer fb; + int err = 0; + + err = hip_fb_create(&fb, "/etc/fstab"); + assert(0 == err); + hip_fb_delete(&fb); +} +END_TEST + +START_TEST(test_hip_fb_delete_null_fb) +{ + hip_fb_delete(NULL); +} +END_TEST + +START_TEST(test_hip_fb_reload_null_fb) +{ + fail_unless(hip_fb_reload(NULL) == -1, NULL); +} +END_TEST + +// For unknown reasons, this file does not compile without the following, +// seemingly useless forward declaration +Suite *firewall_file_buffer(void); + +Suite *firewall_file_buffer(void) +{ + Suite *s = suite_create("firewall/file_buffer"); + + TCase *tc_core = tcase_create("Core"); + tcase_add_test(tc_core, test_hip_fb_create_valid); + tcase_add_test(tc_core, test_hip_fb_create_null_buffer); + tcase_add_test(tc_core, test_hip_fb_create_null_file_name); + tcase_add_test(tc_core, test_hip_fb_create_missing_file); + tcase_add_test(tc_core, test_hip_fb_create_check_file_integrity); + tcase_add_test(tc_core, test_hip_fb_delete_valid); + tcase_add_test(tc_core, test_hip_fb_delete_null_fb); + tcase_add_test(tc_core, test_hip_fb_reload_null_fb); + suite_add_tcase(s, tc_core); + + return s; +} === added file 'test/firewall/line_parser.c' --- test/firewall/line_parser.c 1970-01-01 00:00:00 +0000 +++ test/firewall/line_parser.c 2010-11-12 17:49:42 +0000 @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2010 Aalto University and RWTH Aachen University. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <assert.h> +#include <check.h> + +#include "firewall/line_parser.h" +#include "firewall/line_parser.c" + +// four lines, each 50 characters long (including \n) +char data[] = "I'm not knocking your want to carry that home \n\ +Took it with you when you moved and got it broke \n\ +Found the pieces we counted them all alone \n\ +Didn't add up forgot to carry a zero "; +// four pointers to the beginning of the lines +char *const lines[4] = { + data, + data + 50, + data + 100, + data + 150 +}; +// the memory area describing data +const struct hip_mem_area ma = { data, data + sizeof(data) }; + +// these tests do not clean up after themselves because they assume that +// check runs them in dedicated processes so the OS does the cleanup +START_TEST(test_hip_lp_create_valid) +{ + struct hip_line_parser lp; + + fail_unless(hip_lp_create(&lp, &ma) == 0, NULL); +} +END_TEST + +START_TEST(test_hip_lp_create_null_lp) +{ + fail_unless(hip_lp_create(NULL, &ma) == -1, NULL); +} +END_TEST + +START_TEST(test_hip_lp_create_null_ma) +{ + struct hip_line_parser lp; + + fail_unless(hip_lp_create(&lp, NULL) == -1, NULL); +} +END_TEST + +START_TEST(test_hip_lp_delete_valid) +{ + struct hip_line_parser lp; + int err = 0; + + err = hip_lp_create(&lp, &ma); + assert(0 == err); + hip_lp_delete(&lp); +} +END_TEST + +START_TEST(test_hip_lp_delete_null_lp) +{ + hip_lp_delete(NULL); +} +END_TEST + +START_TEST(test_hip_lp_first_valid) +{ + struct hip_line_parser lp; + int err = 0; + + err = hip_lp_create(&lp, &ma); + assert(0 == err); + fail_unless(hip_lp_first(&lp) == lines[0], NULL); +} +END_TEST + +START_TEST(test_hip_lp_first_null_lp) +{ + fail_unless(hip_lp_first(NULL) == NULL, NULL); +} +END_TEST + +START_TEST(test_hip_lp_next_valid) +{ + struct hip_line_parser lp; + int err = 0; + char *first = NULL; + + err = hip_lp_create(&lp, &ma); + assert(0 == err); + first = hip_lp_first(&lp); + assert(first == lines[0]); + + fail_unless(hip_lp_next(&lp) == lines[1], NULL); + fail_unless(hip_lp_next(&lp) == lines[2], NULL); + fail_unless(hip_lp_next(&lp) == lines[3], NULL); + fail_unless(hip_lp_next(&lp) == NULL, NULL); +} +END_TEST + +START_TEST(test_hip_lp_next_null_lp) +{ + fail_unless(hip_lp_next(NULL) == NULL, NULL); +} +END_TEST + +// For unknown reasons, this file does not compile without the following, +// seemingly useless forward declaration +Suite *firewall_line_parser(void); + +Suite *firewall_line_parser(void) +{ + Suite *s = suite_create("firewall/line_parser"); + + TCase *tc_core = tcase_create("Core"); + tcase_add_test(tc_core, test_hip_lp_create_valid); + tcase_add_test(tc_core, test_hip_lp_create_null_lp); + tcase_add_test(tc_core, test_hip_lp_create_null_ma); + tcase_add_test(tc_core, test_hip_lp_delete_valid); + tcase_add_test(tc_core, test_hip_lp_delete_null_lp); + tcase_add_test(tc_core, test_hip_lp_first_valid); + tcase_add_test(tc_core, test_hip_lp_first_null_lp); + tcase_add_test(tc_core, test_hip_lp_next_valid); + tcase_add_test(tc_core, test_hip_lp_next_null_lp); + suite_add_tcase(s, tc_core); + + return s; +} === added file 'test/firewall/port_bindings.c' --- test/firewall/port_bindings.c 1970-01-01 00:00:00 +0000 +++ test/firewall/port_bindings.c 2010-11-12 17:49:42 +0000 @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2010 Aalto University and RWTH Aachen University. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <netinet/in.h> +#include <assert.h> +#include <check.h> +#include <stdio.h> + +#include "firewall/port_bindings.h" +#include "firewall/port_bindings.c" + +// these tests do not clean up after themselves because they assume that +// check runs them in dedicated processes so the OS does the cleanup. +START_TEST(test_hip_port_bindings_init_with_cache) +{ + fail_unless(hip_port_bindings_init(true) == 0, NULL); +} +END_TEST + +START_TEST(test_hip_port_bindings_init_without_cache) +{ + fail_unless(hip_port_bindings_init(false) == 0, NULL); +} +END_TEST + +START_TEST(test_hip_port_bindings_uninit_with_cache) +{ + int err = 0; + + err = hip_port_bindings_init(true); + assert(0 == err); + + hip_port_bindings_uninit(); +} +END_TEST + +START_TEST(test_hip_port_bindings_uninit_without_cache) +{ + int err = 0; + + err = hip_port_bindings_init(false); + assert(0 == err); + + hip_port_bindings_uninit(); +} +END_TEST + +static void test_hip_port_bindings_get(const bool enable_cache) +{ + int err = 0; + enum hip_port_binding binding; + + err = hip_port_bindings_init(enable_cache); + assert(0 == err); + + binding = hip_port_bindings_get(IPPROTO_TCP, htons(631)); + fail_unless(HIP_PORT_INFO_IPV6UNBOUND == binding || + HIP_PORT_INFO_IPV6BOUND == binding, NULL); + if (HIP_PORT_INFO_IPV6UNBOUND == binding) { + printf("hip_port_bindings_get() reports your CUPS port 631 to not be bound under IPv6.\n" + " This is only correct if you do not have CUPS installed or deliberately disabled\n" + " IPv6 support.\n"); + } else { + printf("hip_port_bindings_get() reports your CUPS port 631 to be bound under IPv6. This\n" + " is only correct if you have CUPS running and IPv6 support enabled.\n"); + } +} + +START_TEST(test_hip_port_bindings_get_with_cache) +{ + test_hip_port_bindings_get(true); +} +END_TEST + +START_TEST(test_hip_port_bindings_get_without_cache) +{ + test_hip_port_bindings_get(false); +} +END_TEST + +// For unknown reasons, this file does not compile without the following, +// seemingly useless forward declaration +Suite *firewall_port_bindings(void); + +Suite *firewall_port_bindings(void) +{ + Suite *s = suite_create("firewall/port_bindings"); + + TCase *tc_core = tcase_create("Core"); + tcase_add_test(tc_core, test_hip_port_bindings_init_with_cache); + tcase_add_test(tc_core, test_hip_port_bindings_init_without_cache); + tcase_add_test(tc_core, test_hip_port_bindings_uninit_with_cache); + tcase_add_test(tc_core, test_hip_port_bindings_uninit_without_cache); + tcase_add_test(tc_core, test_hip_port_bindings_get_with_cache); + tcase_add_test(tc_core, test_hip_port_bindings_get_without_cache); + suite_add_tcase(s, tc_core); + + return s; +} === added file 'test/fw_port_bindings_performance.c' --- test/fw_port_bindings_performance.c 1970-01-01 00:00:00 +0000 +++ test/fw_port_bindings_performance.c 2010-11-12 17:49:42 +0000 @@ -0,0 +1,329 @@ +/* + * Copyright (c) 2010 Aalto University and RWTH Aachen University. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file + * @author Stefan Goetz <stefan.goetz@xxxxxxxxxxxxxxxxx> + */ + +#include <assert.h> +#include <stdio.h> +#include <time.h> + +#include "firewall/file_buffer.h" +#include "firewall/line_parser.h" +#include "firewall/port_bindings.h" + +static double time_clock(const unsigned int iterations) +{ + clock_t start, end, tmp; + unsigned int i; + + start = clock(); + for (i = 0; i < iterations; i += 1) { + tmp = clock(); + } + end = clock(); + + return (((double) (end - start)) / CLOCKS_PER_SEC) / iterations; +} + +static double time_hip_fb_create_delete(const unsigned int iterations, + const char *file_name) +{ + clock_t start, end; + unsigned int i; + struct hip_file_buffer fb; + + start = clock(); + for (i = 0; i < iterations; i += 1) { + if (hip_fb_create(&fb, file_name) == 0) { + hip_fb_delete(&fb); + } + } + end = clock(); + + return (((double) (end - start)) / CLOCKS_PER_SEC) / iterations; +} + +static double time_hip_fb_reload(const unsigned int iterations, + const char *file_name) +{ + clock_t start, end; + unsigned int i; + struct hip_file_buffer fb; + int err; + + err = hip_fb_create(&fb, file_name); + assert(0 == err); + + start = clock(); + for (i = 0; i < iterations; i += 1) { + err = hip_fb_reload(&fb); + assert(err == 0); + } + end = clock(); + + hip_fb_delete(&fb); + + return (((double) (end - start)) / CLOCKS_PER_SEC) / iterations; +} + +static double time_hip_lp_create_delete(const unsigned int iterations) +{ + clock_t start, end; + unsigned int i; + struct hip_mem_area ma = { 0, 0 }; + struct hip_line_parser lp; + + start = clock(); + for (i = 0; i < iterations; i += 1) { + if (hip_lp_create(&lp, &ma) == 0) { + hip_lp_delete(&lp); + } + } + end = clock(); + + return (((double) (end - start)) / CLOCKS_PER_SEC) / iterations; +} + +static double time_hip_lp_first(const unsigned int iterations) +{ + clock_t start, end; + unsigned int i; + struct hip_line_parser lp; + char *line; + struct hip_mem_area ma = { 0, 0 }; + int err; + + err = hip_lp_create(&lp, &ma); + assert(0 == err); + + start = clock(); + for (i = 0; i < iterations; i += 1) { + line = hip_lp_first(&lp); + } + end = clock(); + + hip_lp_delete(&lp); + + return (((double) (end - start)) / CLOCKS_PER_SEC) / iterations; +} + +static double time_hip_lp_next(const unsigned int iterations, + const char *file_name) +{ + clock_t start, end; + unsigned int i; + struct hip_file_buffer fb; + struct hip_line_parser lp; + char *line; + int err; + + err = hip_fb_create(&fb, file_name); + assert(0 == err); + err = hip_lp_create(&lp, hip_fb_get_mem_area(&fb)); + assert(0 == err); + line = hip_lp_first(&lp); + assert(line != NULL); + + start = clock(); + for (i = 0; i < iterations; i += 1) { + line = hip_lp_next(&lp); + } + end = clock(); + + hip_lp_delete(&lp); + hip_fb_delete(&fb); + + return (((double) (end - start)) / CLOCKS_PER_SEC) / iterations; +} + +static double time_hip_lp_parse_file(const unsigned int iterations, + const char *file_name) +{ + clock_t start, end; + unsigned int i; + struct hip_file_buffer fb; + struct hip_line_parser lp; + char *line; + int err; + + err = hip_fb_create(&fb, file_name); + assert(0 == err); + err = hip_lp_create(&lp, hip_fb_get_mem_area(&fb)); + assert(0 == err); + + start = clock(); + for (i = 0; i < iterations; i += 1) { + line = hip_lp_first(&lp); + while (line != NULL) { + line = hip_lp_next(&lp); + } + } + end = clock(); + + hip_lp_delete(&lp); + hip_fb_delete(&fb); + + return (((double) (end - start)) / CLOCKS_PER_SEC) / iterations; +} + +static double time_hip_port_binding_create_delete(const unsigned int iterations, + const bool enable_cache) +{ + clock_t start, end; + unsigned int i; + + start = clock(); + for (i = 0; i < iterations; i += 1) { + hip_port_bindings_init(enable_cache); + hip_port_bindings_uninit(); + } + end = clock(); + + return (((double) (end - start)) / CLOCKS_PER_SEC) / iterations; +} + +static double time_hip_port_bindings_get(const unsigned int iterations, + const uint8_t proto, + const in_port_t port, + const bool enable_cache) +{ + clock_t start, end; + unsigned int i; + enum hip_port_binding pi; + + hip_port_bindings_init(enable_cache); + + start = clock(); + for (i = 0; i < iterations; i += 1) { + pi = hip_port_bindings_get(proto, port); + } + end = clock(); + + hip_port_bindings_uninit(); + + return (((double) (end - start)) / CLOCKS_PER_SEC) / iterations; +} + +int main(void) +{ + const unsigned int iterations = 10000; + const char *file_name = "/proc/net/tcp6"; + const uint8_t proto = 6; + const in_port_t port = 0xFFFF; + bool enable_cache = false; + + printf("Testing clock reading used for benchmarks:\n" + " - call clock()\n" + " ==> time_clock(%d): %fs\n\n", iterations, + time_clock(iterations)); + + printf("Testing file buffer allocation and de-allocation:\n" + " - call hip_fb_create() to\n" + " - zero the file buffer object\n" + " - open the file\n" + " - call hip_fb_reload() to\n" + " - allocate a memory buffer for the file data (if new fb object)\n" + " - read the file data into the memory buffer\n" + " - call hip_fb_delete() to\n" + " - de-allocate the file buffer object\n" + " ==> time_hip_fb_create_delete(%d, %s): %fs\n\n", iterations, + file_name, time_hip_fb_create_delete(iterations, file_name)); + + printf("Testing file buffer file access:\n" + " - call hip_fb_reload() to\n" + " - allocate a memory buffer for the file data (if new fb object)\n" + " - read the file data into the memory buffer\n" + " ==> time_hip_fb_reload(%d, %s): %fs\n\n", iterations, file_name, + time_hip_fb_reload(iterations, file_name)); + + printf("Testing line parser allocation and de-allocation:\n" + " - call hip_lp_create() to\n" + " - allocate a line parser object\n" + " - call hip_fb_create() (s.a.)\n" + " - call hip_lp_delete() to\n" + " - de-allocate the line parser object\n" + " ==> time_hip_lp_create_delete(%d): %fs\n\n", iterations, + time_hip_lp_create_delete(iterations)); + + printf("Testing line parser parsing function:\n" + " - call hip_lp_first() to\n" + " - retrieve a pointer to the beginning of the file\n" + " ==> time_hip_lp_first(%d): %fs\n\n", iterations, + time_hip_lp_first(iterations)); + + printf("Testing line parser parsing function:\n" + " - call hip_lp_next() to\n" + " - search for the next line\n" + " - retrieve a pointer to the beginning of the line\n" + " ==> time_hip_lp_next(%d, %s): %fs\n\n", iterations, file_name, + time_hip_lp_next(iterations, file_name)); + + printf("Testing line parser whole file parsing:\n" + " - call hip_lp_first() (s.a.)\n" + " - call hip_lp_next() (s.a.) until the end of the file\n" + " ==> time_hip_lp_parse_file(%d, %s): %fs\n\n", iterations, file_name, + time_hip_lp_parse_file(iterations, file_name)); + + printf("Testing port binding allocation and de-allocation without cache:\n" + " - call hip_port_bindings_init() to\n" + " - create tcp6 and udp6 line parser objects\n" + " - call hip_port_bindings_uninit() to\n" + " - delete tcp6 and udp6 line parser objects\n" + " ==> time_hip_port_binding_create_delete(%d, %d): %fs\n\n", + iterations, enable_cache, + time_hip_port_binding_create_delete(iterations, enable_cache)); + + printf("Testing port binding parsing without cache:\n" + " - call hip_port_bindings_get() to\n" + " - parse proc file\n" + " ==> time_hip_port_bindings_get(%d, %d, 0x%X, %d): %fs\n\n", + iterations, proto, port, enable_cache, + time_hip_port_bindings_get(iterations, proto, port, enable_cache)); + + enable_cache = true; + printf("Testing port binding allocation and de-allocation with cache:\n" + " - call hip_port_bindings_init() to\n" + " - allocate and zero cache\n" + " - create tcp6 and udp6 line parser objects\n" + " - call hip_port_bindings_uninit() to\n" + " - delete tcp6 and udp6 line parser objects\n" + " - de-allocate cache\n" + " ==> time_hip_port_binding_create_delete(%d, %d): %fs\n\n", + iterations, enable_cache, + time_hip_port_binding_create_delete(iterations, enable_cache)); + + printf("Testing port binding parsing with cache:\n" + " - call hip_port_bindings_get() to\n" + " - check cache for port binding\n" + " - parse proc file if no cache entry\n" + " ==> time_hip_port_bindings_get(%d, %d, 0x%X, %d): %fs\n\n", + iterations, proto, port, enable_cache, + time_hip_port_bindings_get(iterations, proto, port, enable_cache)); + + return 0; +}