| /* |
| ************************************************************************** |
| * Copyright (c) 2014-2015, 2019-2021 The Linux Foundation. All rights reserved. |
| * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. |
| * |
| * Permission to use, copy, modify, and/or distribute this software for |
| * any purpose with or without fee is hereby granted, provided that the |
| * above copyright notice and this permission notice appear in all copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
| * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| ************************************************************************** |
| */ |
| #ifndef ECM_TYPES_H_ |
| #define ECM_TYPES_H_ |
| |
| #include <linux/printk.h> |
| |
| /* |
| * Common ECM macro to handle the kernel macro name change from kernel version 4.9 and above. |
| * GRE_VERSION_1701 and GRE_VERSION_PPTP macros in kernel version < 4.9 needs to be converted |
| * to big endian since GRE_VERSION with which it compares is in big endian |
| */ |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0)) |
| #define ECM_GRE_VERSION_0 __cpu_to_be16(GRE_VERSION_1701) |
| #define ECM_GRE_VERSION_1 __cpu_to_be16(GRE_VERSION_PPTP) |
| typedef struct gre_hdr_pptp ecm_gre_hdr_pptp; |
| #else |
| #define ECM_GRE_VERSION_0 GRE_VERSION_0 |
| #define ECM_GRE_VERSION_1 GRE_VERSION_1 |
| typedef struct pptp_gre_header ecm_gre_hdr_pptp; |
| #endif |
| |
| /* |
| * Flow/Return direction types. |
| */ |
| enum ecm_conn_dir { |
| ECM_CONN_DIR_FLOW, |
| ECM_CONN_DIR_RETURN, |
| ECM_CONN_DIR_MAX |
| }; |
| |
| /* |
| * Rule update types. |
| */ |
| enum ecm_rule_update_type { |
| ECM_RULE_UPDATE_TYPE_CONNMARK, |
| ECM_RULE_UPDATE_TYPE_MAX |
| }; |
| |
| /* |
| * The ECM IP address is an array of 4 32 bit numbers. |
| * This is enough to record both an IPv6 address aswell as an IPv4 address. |
| * IPv4 addresses are stored encoded in an IPv6 as the usual ::FFFF:x:y/96 |
| * We store IP addresses in host order format and NOT network order which is different to Linux network internals. |
| */ |
| typedef uint32_t ip_addr_t[4]; |
| |
| /* |
| * 32/64 bits pointer type |
| */ |
| #ifdef __LP64__ |
| typedef uint64_t ecm_ptr_t; |
| #else |
| typedef uint32_t ecm_ptr_t; |
| #endif |
| |
| #define ECM_IP_ADDR_OCTAL_FMT "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x" |
| #define ECM_IP_ADDR_DOT_FMT "%u.%u.%u.%u" |
| |
| #define ECM_IP_ADDR_TO_OCTAL(ipaddrt) ((uint16_t *)ipaddrt)[7], ((uint16_t *)ipaddrt)[6], ((uint16_t *)ipaddrt)[5], ((uint16_t *)ipaddrt)[4], ((uint16_t *)ipaddrt)[3], ((uint16_t *)ipaddrt)[2], ((uint16_t *)ipaddrt)[1], ((uint16_t *)ipaddrt)[0] |
| |
| #define ECM_IP_ADDR_TO_DOT(ipaddrt) ((uint8_t *)ipaddrt)[3], ((uint8_t *)ipaddrt)[2], ((uint8_t *)ipaddrt)[1], ((uint8_t *)ipaddrt)[0] |
| |
| #define ECM_IP_ADDR_MATCH(a, b) \ |
| ((a[0] == b[0]) && (a[1] == b[1]) && (a[2] == b[2]) && (a[3] == b[3])) |
| |
| #define ECM_IP_ADDR_MASK_MATCH(addr, mask) \ |
| (((addr[0] & mask[0]) == mask[0]) && ((addr[1] & mask[1]) == mask[1]) && \ |
| ((addr[2] & mask[2]) == mask[2]) && ((addr[3] & mask[3]) == mask[3])) |
| |
| #define ECM_PORT_MASK_MATCH(port, mask) ((port & mask) == mask) |
| #define ECM_PROTO_MASK_MATCH(proto, mask) ((proto & mask) == mask) |
| |
| #define ECM_MAC_ADDR_MATCH(a, b) \ |
| ((((uint16_t *)a)[0] == (((uint16_t *)b)[0])) && \ |
| (((uint16_t *)a)[1] == (((uint16_t *)b)[1])) && \ |
| (((uint16_t *)a)[2] == (((uint16_t *)b)[2]))) |
| |
| #define ECM_IP_ADDR_IS_V4(a) \ |
| ((a[1] == 0x0000ffff) && !a[2] && !a[3]) |
| |
| #define ECM_IP_ADDR_IS_NULL(a) \ |
| ((a[0] | a[1] | a[2] | a[3]) == 0) |
| |
| #define ECM_IP_ADDR_NULL {0, 0, 0, 0} |
| |
| #define ECM_MAC_ADDR_STR_BUFF_SIZE 18 /* This is the size of a string in the format of aa:bb:cc:dd:ee:ff */ |
| #define ECM_IP_ADDR_STR_BUFF_SIZE 40 /* This is the size of a string in the format of aaaa:bbbb:cccc:0000:1111:dddd:eeee:ffff */ |
| #define ECM_IP_ADDR_DOT_FMT_STR_BUFF_SIZE 16 /* This is the size of a string in the format of 192.168.100.200 */ |
| |
| /* |
| * Type checking functions for various forms of IP address |
| * Placing these in a macro, enables the compiler to check |
| * that the caller is doing the right thing. |
| */ |
| static inline void ecm_type_check_ecm_ip_addr(ip_addr_t ipaddr){} |
| static inline void ecm_type_check_linux_ipv4(__be32 ipaddr){} |
| static inline void ecm_type_check_ae_ipv4(uint32_t addr){} |
| static inline void ecm_type_check_linux_ipv6(struct in6_addr in6){} |
| static inline void ecm_type_check_ae_ipv6(uint32_t ip6[4]){} |
| |
| /* |
| * This macro copies ip_addr_t's |
| * It's usually quicker than a memcpy(). |
| */ |
| #define __ECM_IP_ADDR_COPY_NO_CHECK(d, s) \ |
| { \ |
| d[0] = s[0]; \ |
| d[1] = s[1]; \ |
| d[2] = s[2]; \ |
| d[3] = s[3]; \ |
| } |
| |
| #define ECM_IP_ADDR_COPY(d,s) \ |
| { \ |
| ecm_type_check_ecm_ip_addr(d); \ |
| ecm_type_check_ecm_ip_addr(s); \ |
| __ECM_IP_ADDR_COPY_NO_CHECK(d,s); \ |
| } |
| |
| #define ECM_IP_ADDR_HASH(h, a) \ |
| { \ |
| ecm_type_check_ecm_ip_addr(a); \ |
| h = a[0] ^ a[1] ^ a[2] ^ a[3]; \ |
| } |
| |
| /* |
| * This macro converts from Linux IPv4 address (network order) to ECM ip_addr_t |
| */ |
| #define ECM_NIN4_ADDR_TO_IP_ADDR(ipaddrt, nin4) \ |
| { \ |
| ecm_type_check_ecm_ip_addr(ipaddrt); \ |
| ecm_type_check_linux_ipv4(nin4); \ |
| ipaddrt[0] = ntohl(nin4); \ |
| ipaddrt[1] = 0x0000ffff; \ |
| ipaddrt[2] = 0x00000000; \ |
| ipaddrt[3] = 0x00000000; \ |
| } |
| |
| /* |
| * This macro converts from ECM ip_addr_t to Linux network IPv4 address |
| */ |
| #define ECM_IP_ADDR_TO_NIN4_ADDR(nin4, ipaddrt) \ |
| { \ |
| nin4 = 0; \ |
| ecm_type_check_linux_ipv4(nin4); \ |
| ecm_type_check_ecm_ip_addr(ipaddrt); \ |
| DEBUG_ASSERT(!ipaddrt[3] && !ipaddrt[2] && (ipaddrt[1] == 0x0000ffff), "Not IPv4 address: " ECM_IP_ADDR_OCTAL_FMT "\n", ECM_IP_ADDR_TO_OCTAL(ipaddrt)); \ |
| nin4 = htonl(ipaddrt[0]); \ |
| } |
| |
| /* |
| * This macro converts from Linux IPv4 address (host order) to ECM ip_addr_t |
| */ |
| #define ECM_HIN4_ADDR_TO_IP_ADDR(ipaddrt, hin4) \ |
| { \ |
| ecm_type_check_linux_ipv4(hin4); \ |
| ecm_type_check_ecm_ip_addr(ipaddrt); \ |
| ipaddrt[0] = hin4; \ |
| ipaddrt[1] = 0x0000ffff; \ |
| ipaddrt[2] = 0x00000000; \ |
| ipaddrt[3] = 0x00000000; \ |
| } |
| |
| /* |
| * This macro converts from ECM ip_addr_t to Linux Host IPv4 address |
| */ |
| #define ECM_IP_ADDR_TO_HIN4_ADDR(hin4, ipaddrt) \ |
| { \ |
| ecm_type_check_linux_ipv4(hin4); \ |
| ecm_type_check_ecm_ip_addr(ipaddrt); \ |
| DEBUG_ASSERT(!ipaddrt[3] && !ipaddrt[2] && (ipaddrt[1] == 0x0000ffff), "Not IPv4 address: " ECM_IP_ADDR_OCTAL_FMT "\n", ECM_IP_ADDR_TO_OCTAL(ipaddrt)); \ |
| hin4 = ipaddrt[0]; \ |
| } |
| |
| #define ECM_LINUX6_TO_IP_ADDR(d,s) \ |
| { \ |
| ecm_type_check_ecm_ip_addr(d); \ |
| ecm_type_check_ae_ipv6(s); \ |
| __ECM_IP_ADDR_COPY_NO_CHECK(d,s); \ |
| } |
| |
| #define ECM_IP_ADDR_TO_LINUX6(d,s) \ |
| { \ |
| ecm_type_check_ae_ipv6(d); \ |
| ecm_type_check_ecm_ip_addr(s); \ |
| __ECM_IP_ADDR_COPY_NO_CHECK(d,s); \ |
| } |
| |
| /* |
| * This macro converts from Linux IPv6 address (network order) to ECM ip_addr_t |
| */ |
| #define ECM_NIN6_ADDR_TO_IP_ADDR(ipaddrt, nin6) \ |
| { \ |
| ecm_type_check_ecm_ip_addr(ipaddrt); \ |
| ecm_type_check_linux_ipv6(nin6); \ |
| ipaddrt[0] = ntohl(nin6.in6_u.u6_addr32[3]); \ |
| ipaddrt[1] = ntohl(nin6.in6_u.u6_addr32[2]); \ |
| ipaddrt[2] = ntohl(nin6.in6_u.u6_addr32[1]); \ |
| ipaddrt[3] = ntohl(nin6.in6_u.u6_addr32[0]); \ |
| } |
| |
| /* |
| * This macro converts from ECM ip_addr_t to Linux IPv6 address (network order) |
| */ |
| #define ECM_IP_ADDR_TO_NIN6_ADDR(nin6, ipaddrt) \ |
| { \ |
| ecm_type_check_linux_ipv6(nin6); \ |
| ecm_type_check_ecm_ip_addr(ipaddrt); \ |
| nin6.in6_u.u6_addr32[3] = htonl(ipaddrt[0]); \ |
| nin6.in6_u.u6_addr32[2] = htonl(ipaddrt[1]); \ |
| nin6.in6_u.u6_addr32[1] = htonl(ipaddrt[2]); \ |
| nin6.in6_u.u6_addr32[0] = htonl(ipaddrt[3]); \ |
| } |
| |
| /* |
| * This macro converts from Linux IPv6 address (host order) to ECM ip_addr_t |
| */ |
| #define ECM_HIN6_ADDR_TO_IP_ADDR(ipaddrt, hin6) \ |
| { \ |
| ecm_type_check_ecm_ip_addr(ipaddrt); \ |
| ecm_type_check_linux_ipv6(hin6); \ |
| ipaddrt[0] = hin6.in6_u.u6_addr32[3]; \ |
| ipaddrt[1] = hin6.in6_u.u6_addr32[2]; \ |
| ipaddrt[2] = hin6.in6_u.u6_addr32[1]; \ |
| ipaddrt[3] = hin6.in6_u.u6_addr32[0]; \ |
| } |
| |
| /* |
| * This macro converts from ECM ip_addr_t to Linux IPv6 address (host order) |
| */ |
| #define ECM_IP_ADDR_TO_HIN6_ADDR(hin6, ipaddrt) \ |
| { \ |
| ecm_type_check_linux_ipv6(hin6); \ |
| ecm_type_check_ecm_ip_addr(ipaddrt); \ |
| hin6.in6_u.u6_addr32[0] = ipaddrt[3]; \ |
| hin6.in6_u.u6_addr32[1] = ipaddrt[2]; \ |
| hin6.in6_u.u6_addr32[2] = ipaddrt[1]; \ |
| hin6.in6_u.u6_addr32[3] = ipaddrt[0]; \ |
| } |
| |
| /* |
| * ecm_mac_addr_equal() |
| * Compares two MAC addresses. |
| */ |
| static inline bool ecm_mac_addr_equal(const u8 *addr1, const u8 *addr2) |
| { |
| return !ether_addr_equal(addr1, addr2); |
| } |
| |
| /* |
| * ecm_ip_addr_is_non_unicast() |
| * Returns true if the IP address is not unicast |
| */ |
| static inline bool ecm_ip_addr_is_non_unicast(ip_addr_t addr) |
| { |
| uint32_t v4_addr = addr[0]; |
| |
| #ifdef ECM_IPV6_ENABLE |
| if (!ECM_IP_ADDR_IS_V4(addr)) { |
| /* |
| * IPv6 |
| * ff00::0/8 are multicast |
| */ |
| if (ECM_IP_ADDR_IS_NULL(addr) || ((addr[3] & 0xff000000) == 0xff000000)) { |
| return true; |
| } |
| return false; |
| } |
| #endif |
| |
| if (ECM_IP_ADDR_IS_NULL(addr) || ((v4_addr & 0xe0000000) == 0xe0000000)) { |
| return true; |
| } |
| return false; |
| } |
| |
| /* |
| * ecm_ip_addr_is_multicast() |
| * Returns true if the IP address is multicast |
| */ |
| static inline bool ecm_ip_addr_is_multicast(ip_addr_t addr) |
| { |
| uint32_t v4_addr = addr[0]; |
| |
| #ifdef ECM_IPV6_ENABLE |
| if (!ECM_IP_ADDR_IS_V4(addr)) { |
| /* |
| * IPv6 |
| * ff00::0/8 are multicast |
| */ |
| if (((addr[3] & 0xff000000) == 0xff000000)) { |
| return true; |
| } |
| return false; |
| } |
| #endif |
| |
| if (((v4_addr & 0xf0000000) == 0xe0000000)) { |
| return true; |
| } |
| return false; |
| } |
| |
| #ifdef ECM_MULTICAST_ENABLE |
| /* |
| * ecm_translate_multicast_mac() |
| * Create the multicast MAC address given a multicast IP address |
| */ |
| static inline void ecm_translate_multicast_mac(ip_addr_t addr, unsigned char multicast_mac[]) |
| { |
| uint32_t ip_addr = addr[0]; |
| |
| if (ECM_IP_ADDR_IS_V4(addr)) { |
| multicast_mac[0] = 0x01; |
| multicast_mac[1] = 0x00; |
| multicast_mac[2] = 0x5e; |
| multicast_mac[3] = (ip_addr & 0x7f0000) >> 16; |
| multicast_mac[4] = (ip_addr & 0xff00) >> 8; |
| multicast_mac[5] = (ip_addr & 0xff); |
| return; |
| } |
| |
| multicast_mac[0] = 0x33; |
| multicast_mac[1] = 0x33; |
| multicast_mac[2] = (ip_addr & 0xff000000) >> 24; |
| multicast_mac[3] = (ip_addr & 0xff0000) >> 16; |
| multicast_mac[4] = (ip_addr & 0xff00) >> 8; |
| multicast_mac[5] = (ip_addr & 0xff); |
| } |
| #endif |
| |
| /* |
| * ecm_ip_addr_in_range() |
| * Tests if a is >= s && <= e |
| */ |
| static inline bool ecm_ip_addr_in_range(ip_addr_t a, ip_addr_t s, ip_addr_t e) |
| { |
| if (a[3] > s[3]) goto test_end; |
| if (a[3] < s[3]) return false; |
| /* a[3] == s[3] */ |
| if (a[2] > s[2]) goto test_end; |
| if (a[2] < s[2]) return false; |
| /* a[2] == s[2] */ |
| if (a[1] > s[1]) goto test_end; |
| if (a[1] < s[1]) return false; |
| /* a[1] == s[1] */ |
| if (a[0] > s[0]) goto test_end; |
| if (a[0] < s[0]) return false; |
| /* a[0] == s[0] */ |
| |
| test_end: |
| ; |
| if (a[3] > e[3]) return false; |
| if (a[3] < e[3]) return true; |
| /* a[3] == e[3] */ |
| if (a[2] > e[2]) return false; |
| if (a[2] < e[2]) return true; |
| /* a[2] == e[2] */ |
| if (a[1] > e[1]) return false; |
| if (a[1] < e[1]) return true; |
| /* a[1] == e[1] */ |
| if (a[0] > e[0]) return false; |
| /* a[0] <= e[0] */ |
| return true; |
| } |
| |
| /* |
| * ecm_ip_addr_to_string() |
| * Converts the given address to a string either as octal or dotted format |
| * |
| * Supplied buffer should be at least 40 bytes long. |
| */ |
| static inline void ecm_ip_addr_to_string(char *str, ip_addr_t a) |
| { |
| #ifdef ECM_IPV6_ENABLE |
| if (!ECM_IP_ADDR_IS_V4(a)) { |
| snprintf(str, ECM_IP_ADDR_STR_BUFF_SIZE, ECM_IP_ADDR_OCTAL_FMT, ECM_IP_ADDR_TO_OCTAL(a)); |
| return; |
| } |
| #endif |
| |
| snprintf(str, ECM_IP_ADDR_DOT_FMT_STR_BUFF_SIZE, ECM_IP_ADDR_DOT_FMT, ECM_IP_ADDR_TO_DOT(a)); |
| } |
| |
| /* |
| * ecm_string_to_ip_addr() |
| * Convert a string IP address to its ECM ip_addr_t representation |
| */ |
| static inline bool ecm_string_to_ip_addr(ip_addr_t addr, char *ip_str) |
| { |
| struct in6_addr dbuf; |
| uint8_t *dptr = dbuf.s6_addr; |
| if (in4_pton(ip_str, -1, dptr, '\0', NULL) > 0) { |
| /* |
| * IPv4 |
| */ |
| ECM_NIN4_ADDR_TO_IP_ADDR(addr, dbuf.s6_addr[0]); |
| return true; |
| } |
| #ifdef ECM_IPV6_ENABLE |
| if (in6_pton(ip_str, -1, dptr, '\0', NULL) > 0) { |
| /* |
| * IPv6 |
| */ |
| ECM_NIN6_ADDR_TO_IP_ADDR(addr, dbuf); |
| return true; |
| } |
| #endif |
| return false; |
| } |
| |
| /* |
| * The following are debug macros used throughout the ECM. |
| * Each file that #includes this file MUST have a: |
| * |
| * #define DEBUG_LEVEL X |
| * |
| * before the inclusion of this file. |
| * X is: |
| * 0 = OFF |
| * 1 = ASSERTS / ERRORS |
| * 2 = 1 + WARN |
| * 3 = 2 + INFO |
| * 4 = 3 + TRACE |
| * NOTE: But X is usually provided by a -D preprocessor defined in the Makefile |
| */ |
| #if (DEBUG_LEVEL < 1) |
| #define DEBUG_ASSERT(s, ...) |
| #define DEBUG_ERROR(s, ...) |
| #define DEBUG_ERROR_RATELIMITED(s, ...) |
| #define DEBUG_CHECK_MAGIC(i, m, s, ...) |
| #define DEBUG_SET_MAGIC(i, m) |
| #define DEBUG_CLEAR_MAGIC(i) |
| #define DEBUG_ECM_IP_ADDR_TO_STRING(addr_str, addr) |
| #else |
| #define DEBUG_ASSERT(c, s, ...) if (!(c)) { pr_emerg("ASSERT: %s:%d:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__); BUG(); } |
| #define DEBUG_ERROR(s, ...) pr_err("%s:%d:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__) |
| #define DEBUG_ERROR_RATELIMITED(s, ...) pr_err_ratelimited("%s:%d:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__) |
| #define DEBUG_CHECK_MAGIC(i, m, s, ...) if (i->magic != m) { DEBUG_ASSERT(false, s, ##__VA_ARGS__); } |
| #define DEBUG_SET_MAGIC(i, m) i->magic = m |
| #define DEBUG_CLEAR_MAGIC(i) i->magic = 0 |
| #define DEBUG_ECM_IP_ADDR_TO_STRING(addr_str, addr) ecm_ip_addr_to_string(addr_str, addr); |
| #endif |
| |
| #if defined(CONFIG_DYNAMIC_DEBUG) |
| /* |
| * Compile messages for dynamic enable/disable |
| */ |
| #define DEBUG_WARN(s, ...) pr_debug("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__) |
| #define DEBUG_INFO(s, ...) pr_debug("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__) |
| #define DEBUG_TRACE(s, ...) pr_debug("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__) |
| #else |
| |
| /* |
| * Statically compile messages at different levels |
| */ |
| #if (DEBUG_LEVEL < 2) |
| #define DEBUG_WARN(s, ...) |
| #else |
| #define DEBUG_WARN(s, ...) pr_warn("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__) |
| #endif |
| |
| #if (DEBUG_LEVEL < 3) |
| #define DEBUG_INFO(s, ...) |
| #else |
| #define DEBUG_INFO(s, ...) pr_notice("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__) |
| #endif |
| |
| #if (DEBUG_LEVEL < 4) |
| #define DEBUG_TRACE(s, ...) |
| #else |
| #define DEBUG_TRACE(s, ...) pr_info("%s[%d]:" s, __FUNCTION__, __LINE__, ##__VA_ARGS__) |
| #endif |
| #endif |
| #endif |