From 8a3b3eb04991795009c0a577e440da586a7a34f4 Mon Sep 17 00:00:00 2001 From: luwenpeng Date: Sun, 15 Jan 2023 18:07:18 +0800 Subject: [PATCH] =?UTF-8?q?TSG-13383=20=E8=A7=A3=E6=9E=90RAW=5FPACKET=20?= =?UTF-8?q?=20=20=20=20*=20=E5=A2=9E=E5=8A=A0=20ETH=5FVLAN=5FVLAN=5FIP4=5F?= =?UTF-8?q?IP4=5FUDP=20=E7=9A=84=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B=20=20?= =?UTF-8?q?=20=20=20*=20=E5=A2=9E=E5=8A=A0=20ETH=5FIP6=5FIP4=5FTCP=5FSSH?= =?UTF-8?q?=20=E7=9A=84=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B=20=20=20=20=20?= =?UTF-8?q?*=20=E5=A2=9E=E5=8A=A0=20ETH=5FIP4=5FIP6=5FTCP=20=E7=9A=84?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B=20=20=20=20=20*=20?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20ETH=5FIP6=5FIP6=5FUDP=20=E7=9A=84=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E7=94=A8=E4=BE=8B=20=20=20=20=20*=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=20ETH=5FMPLS=5FIP4=5FTCP=20=E7=9A=84=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E7=94=A8=E4=BE=8B=20=20=20=20=20*=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=20ETH=5FMPLS=5FMPLS=5FIP4=5FTCP=20=E7=9A=84=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E7=94=A8=E4=BE=8B=20=20=20=20=20*=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=20ETH=5FVLAN=5FPPPOE=5FIP4=5FTCP=20=E7=9A=84=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E7=94=A8=E4=BE=8B=20=20=20=20=20*=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=20ETH=5FIP6=5FUDP=5FGTP=5FIP6=5FTCP=5FTLS=20=E7=9A=84?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B=20=20=20=20=20*=20?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20ETH=5FIP6=5FUDP=5FGTP=5FIP4=5FTCP=5FTLS=20?= =?UTF-8?q?=E7=9A=84=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B=20=20=20=20=20*?= =?UTF-8?q?=20=E5=A2=9E=E5=8A=A0=20ETH=5FIP4=5FUDP=5FVXLAN=5FETH=5FIP4=5FU?= =?UTF-8?q?DP=5FDNS=20=E7=9A=84=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B=20=20?= =?UTF-8?q?=20=20=20*=20=E5=A2=9E=E5=8A=A0=20ETH=5FMPLS=5FMPLS=5FPWETHCW?= =?UTF-8?q?=5FETH=5FARP=20=E7=9A=84=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B=20?= =?UTF-8?q?=20=20=20=20*=20=E5=A2=9E=E5=8A=A0=20ETH=5FMPLS=5FMPLS=5FPWETHC?= =?UTF-8?q?W=5FETH=5FARP=20=E7=9A=84=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/CMakeLists.txt | 2 +- common/include/addr_tuple4.h | 68 +- common/include/log.h | 4 +- common/include/raw_packet.h | 80 + common/src/addr_tuple4.cpp | 16 +- common/src/raw_packet.cpp | 959 ++++++++++++ common/test/CMakeLists.txt | 11 +- common/test/gtest_raw_packet.cpp | 1378 +++++++++++++++++ .../pcap/1-ETH_VLAN_VLAN_IP4_IP4_UDP.pcap | Bin 0 -> 954 bytes .../pcap/10-ETH_IP6_UDP_GTP_IP4_TCP_TLS.pcap | Bin 0 -> 1510 bytes .../11-ETH_IP4_UDP_VXLAN_ETH_IP4_UDP_DNS.pcap | Bin 0 -> 776 bytes .../12-ETH_MPLS_MPLS_PWETHCW_ETH_ARP.pcap | Bin 0 -> 1256 bytes common/test/pcap/2-ETH_IP6_IP4_TCP_SSH.pcap | Bin 0 -> 1356 bytes ...-ETH_VLAN_IP6_IP4_GRE_PPP_IP4_UDP_DNS.pcap | Bin 0 -> 312 bytes common/test/pcap/4-ETH_IP4_IP6_TCP.pcap.pcap | Bin 0 -> 1424 bytes common/test/pcap/5-ETH_IP6_IP6_UDP.pcap | Bin 0 -> 268 bytes common/test/pcap/6-ETH_MPLS_IP4_TCP.pcap | Bin 0 -> 3726 bytes common/test/pcap/7-ETH_MPLS_MPLS_IP4_TCP.pcap | Bin 0 -> 832 bytes .../test/pcap/8-ETH_VLAN_PPPOE_IP4_TCP.pcap | Bin 0 -> 79857 bytes .../pcap/9-ETH_IP6_UDP_GTP_IP6_TCP_TLS.pcap | Bin 0 -> 1482 bytes 20 files changed, 2471 insertions(+), 47 deletions(-) create mode 100644 common/include/raw_packet.h create mode 100644 common/src/raw_packet.cpp create mode 100644 common/test/gtest_raw_packet.cpp create mode 100644 common/test/pcap/1-ETH_VLAN_VLAN_IP4_IP4_UDP.pcap create mode 100644 common/test/pcap/10-ETH_IP6_UDP_GTP_IP4_TCP_TLS.pcap create mode 100644 common/test/pcap/11-ETH_IP4_UDP_VXLAN_ETH_IP4_UDP_DNS.pcap create mode 100644 common/test/pcap/12-ETH_MPLS_MPLS_PWETHCW_ETH_ARP.pcap create mode 100644 common/test/pcap/2-ETH_IP6_IP4_TCP_SSH.pcap create mode 100644 common/test/pcap/3-ETH_VLAN_IP6_IP4_GRE_PPP_IP4_UDP_DNS.pcap create mode 100644 common/test/pcap/4-ETH_IP4_IP6_TCP.pcap.pcap create mode 100644 common/test/pcap/5-ETH_IP6_IP6_UDP.pcap create mode 100644 common/test/pcap/6-ETH_MPLS_IP4_TCP.pcap create mode 100644 common/test/pcap/7-ETH_MPLS_MPLS_IP4_TCP.pcap create mode 100644 common/test/pcap/8-ETH_VLAN_PPPOE_IP4_TCP.pcap create mode 100644 common/test/pcap/9-ETH_IP6_UDP_GTP_IP6_TCP_TLS.pcap diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 0fe6e27..feee134 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -1,4 +1,4 @@ -add_library(common src/addr_tuple4.cpp src/session_table.cpp src/bfd.cpp) +add_library(common src/addr_tuple4.cpp src/session_table.cpp src/raw_packet.cpp src/bfd.cpp) target_include_directories(common PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include) diff --git a/common/include/addr_tuple4.h b/common/include/addr_tuple4.h index 2a7aae9..5efcce1 100644 --- a/common/include/addr_tuple4.h +++ b/common/include/addr_tuple4.h @@ -8,44 +8,42 @@ extern "C" #include -enum addr_tuple4_type -{ - ADDR_TUPLE4_TYPE_V4, - ADDR_TUPLE4_TYPE_V6, -}; - -struct addr_tuple4_v4 -{ - struct in_addr src_addr; /* network order */ - struct in_addr dst_addr; /* network order */ - in_port_t src_port; /* network order */ - in_port_t dst_port; /* network order */ -}; - -struct addr_tuple4_v6 -{ - struct in6_addr src_addr; /* network order */ - struct in6_addr dst_addr; /* network order */ - in_port_t src_port; /* network order */ - in_port_t dst_port; /* network order */ -}; - -struct addr_tuple4 -{ - enum addr_tuple4_type addr_type; - union + enum addr_tuple4_type { - struct addr_tuple4_v4 addr_v4; - struct addr_tuple4_v6 addr_v6; + ADDR_TUPLE4_TYPE_V4, + ADDR_TUPLE4_TYPE_V6, + }; + + struct addr_v4 + { + struct in_addr src_addr; /* network order */ + struct in_addr dst_addr; /* network order */ + }; + + struct addr_v6 + { + struct in6_addr src_addr; /* network order */ + struct in6_addr dst_addr; /* network order */ + }; + + struct addr_tuple4 + { + enum addr_tuple4_type addr_type; + in_port_t src_port; /* network order */ + in_port_t dst_port; /* network order */ + union + { + struct addr_v4 addr_v4; + struct addr_v6 addr_v6; + }; }; -}; #define INIT_ADDR_V4(name, src_addr_str, src_port_num, dst_addr_str, dst_port_num) \ struct addr_tuple4 name; \ memset(&name, 0, sizeof(name)); \ (name).addr_type = ADDR_TUPLE4_TYPE_V4; \ - (name).addr_v4.src_port = htons((src_port_num)); \ - (name).addr_v4.dst_port = htons((dst_port_num)); \ + (name).src_port = htons((src_port_num)); \ + (name).dst_port = htons((dst_port_num)); \ inet_pton(AF_INET, (src_addr_str), &(name).addr_v4.src_addr); \ inet_pton(AF_INET, (dst_addr_str), &(name).addr_v4.dst_addr); @@ -53,13 +51,13 @@ struct addr_tuple4 struct addr_tuple4 name; \ memset(&name, 0, sizeof(name)); \ (name).addr_type = ADDR_TUPLE4_TYPE_V6; \ - (name).addr_v6.src_port = htons((src_port_num)); \ - (name).addr_v6.dst_port = htons((dst_port_num)); \ + (name).src_port = htons((src_port_num)); \ + (name).dst_port = htons((dst_port_num)); \ inet_pton(AF_INET6, (src_addr_str), &(name).addr_v6.src_addr); \ inet_pton(AF_INET6, (dst_addr_str), &(name).addr_v6.dst_addr); -char *addr_tuple4_to_str(const struct addr_tuple4 *addr); -void addr_tuple4_reverse(const struct addr_tuple4 *orin, struct addr_tuple4 *out); + char *addr_tuple4_to_str(const struct addr_tuple4 *addr); + void addr_tuple4_reverse(const struct addr_tuple4 *orin, struct addr_tuple4 *out); #ifdef __cpluscplus } diff --git a/common/include/log.h b/common/include/log.h index 7553bd8..97d3a6e 100644 --- a/common/include/log.h +++ b/common/include/log.h @@ -10,13 +10,13 @@ extern "C" #define LOG_DEBUG(format, ...) \ { \ - fprintf(stdout, format "\n", ##__VA_ARGS__); \ + fprintf(stdout, "DEBUG " format "\n", ##__VA_ARGS__); \ fflush(stdout); \ } #define LOG_ERROR(format, ...) \ { \ - fprintf(stderr, format "\n", ##__VA_ARGS__); \ + fprintf(stderr, "ERROR " format "\n", ##__VA_ARGS__); \ fflush(stderr); \ } diff --git a/common/include/raw_packet.h b/common/include/raw_packet.h new file mode 100644 index 0000000..01bb4ba --- /dev/null +++ b/common/include/raw_packet.h @@ -0,0 +1,80 @@ +#ifndef _RAW_PACKET_H +#define _RAW_PACKET_H + +#ifdef __cpluscplus +extern "C" +{ +#endif + +enum layer_type +{ + // 数据链路层 + LAYER_TYPE_ETHER = 1 << 0, + LAYER_TYPE_PPP = 1 << 1, + LAYER_TYPE_HDLC = 1 << 2, + LAYER_TYPE_L2 = (LAYER_TYPE_ETHER | LAYER_TYPE_PPP | LAYER_TYPE_HDLC), + + // 数据链路层 -- 隧道 + LAYER_TYPE_VLAN = 1 << 3, + LAYER_TYPE_PPPOE = 1 << 4, + LAYER_TYPE_MPLS = 1 << 5, + LAYER_TYPE_L2_TUN = (LAYER_TYPE_VLAN | LAYER_TYPE_PPPOE | LAYER_TYPE_MPLS), + + // 网络层 + LAYER_TYPE_IPV4 = 1 << 6, + LAYER_TYPE_IPV6 = 1 << 7, + LAYER_TYPE_L3 = (LAYER_TYPE_IPV4 | LAYER_TYPE_IPV6), + + // 网络层 -- 隧道 + + // 传输层 + LAYER_TYPE_UDP = 1 << 8, + LAYER_TYPE_TCP = 1 << 9, + LAYER_TYPE_L4 = (LAYER_TYPE_UDP | LAYER_TYPE_TCP), + + // 传输层 -- 隧道 + LAYER_TYPE_G_VXLAN = 1 << 10, + LAYER_TYPE_GTPV1_U = 1 << 11, + + // ALL + LAYER_TYPE_ALL = (LAYER_TYPE_L2 | LAYER_TYPE_L2_TUN | LAYER_TYPE_L3 | LAYER_TYPE_L4 | LAYER_TYPE_G_VXLAN | LAYER_TYPE_GTPV1_U), + + // UNKNOWN + LAYER_TYPE_UNKNOWN, +}; + +enum parse_status +{ + PARSE_STATUS_CONTINUE, + PARSE_STATUS_STOP +}; + +struct raw_pkt_parser; + +struct raw_pkt_parser *raw_packet_parser_create(enum layer_type expect_type, uint16_t expect_results_num); +void raw_packet_parser_destory(struct raw_pkt_parser *handler); + +// return 0 : success +// return -ENOMEM : error +int raw_packet_parser_push(struct raw_pkt_parser *handler, enum layer_type type, uint16_t offset); +// return PARSE_STATUS_CONTINUE +// return PARSE_STATUS_STOP +enum parse_status raw_packet_parser_status(struct raw_pkt_parser *handler, const void *data, enum layer_type this_type); +// return most inner payload +const void *raw_packet_parser_parse(struct raw_pkt_parser *handler, const void *data, size_t length); + +// return 0 : success +// return -1 : error +int raw_packet_parser_get_most_inner_tuple4(struct raw_pkt_parser *handler, struct addr_tuple4 *addr); +int raw_packet_parser_get_most_outer_tuple4(struct raw_pkt_parser *handler, struct addr_tuple4 *addr); + +// return 0 : success +// return -1 : error +int raw_packet_parser_get_most_inner_address(struct raw_pkt_parser *handler, struct addr_tuple4 *addr); +int raw_packet_parser_get_most_outer_address(struct raw_pkt_parser *handler, struct addr_tuple4 *addr); + +#ifdef __cpluscplus +} +#endif + +#endif \ No newline at end of file diff --git a/common/src/addr_tuple4.cpp b/common/src/addr_tuple4.cpp index b9a67ad..fd60f0d 100644 --- a/common/src/addr_tuple4.cpp +++ b/common/src/addr_tuple4.cpp @@ -12,8 +12,8 @@ char *addr_tuple4_to_str(const struct addr_tuple4 *addr) { char src_addr[INET_ADDRSTRLEN] = {0}; char dst_addr[INET_ADDRSTRLEN] = {0}; - uint16_t src_port = ntohs((uint16_t)addr->addr_v4.src_port); - uint16_t dst_port = ntohs((uint16_t)addr->addr_v4.dst_port); + uint16_t src_port = ntohs((uint16_t)addr->src_port); + uint16_t dst_port = ntohs((uint16_t)addr->dst_port); inet_ntop(AF_INET, &addr->addr_v4.src_addr, src_addr, sizeof(src_addr)); inet_ntop(AF_INET, &addr->addr_v4.dst_addr, dst_addr, sizeof(dst_addr)); asprintf(&str_ret, "%s %u %s %u", src_addr, src_port, dst_addr, dst_port); @@ -23,8 +23,8 @@ char *addr_tuple4_to_str(const struct addr_tuple4 *addr) { char src_addr[INET6_ADDRSTRLEN] = {0}; char dst_addr[INET6_ADDRSTRLEN] = {0}; - uint16_t src_port = ntohs((uint16_t)addr->addr_v6.src_port); - uint16_t dst_port = ntohs((uint16_t)addr->addr_v6.dst_port); + uint16_t src_port = ntohs((uint16_t)addr->src_port); + uint16_t dst_port = ntohs((uint16_t)addr->dst_port); inet_ntop(AF_INET6, &addr->addr_v6.src_addr, src_addr, sizeof(src_addr)); inet_ntop(AF_INET6, &addr->addr_v6.dst_addr, dst_addr, sizeof(dst_addr)); asprintf(&str_ret, "%s %u %s %u", src_addr, src_port, dst_addr, dst_port); @@ -42,8 +42,8 @@ void addr_tuple4_reverse(const struct addr_tuple4 *orin, struct addr_tuple4 *out out->addr_type = ADDR_TUPLE4_TYPE_V4; out->addr_v4.src_addr = orin->addr_v4.dst_addr; out->addr_v4.dst_addr = orin->addr_v4.src_addr; - out->addr_v4.src_port = orin->addr_v4.dst_port; - out->addr_v4.dst_port = orin->addr_v4.src_port; + out->src_port = orin->dst_port; + out->dst_port = orin->src_port; } if (orin->addr_type == ADDR_TUPLE4_TYPE_V6) @@ -51,7 +51,7 @@ void addr_tuple4_reverse(const struct addr_tuple4 *orin, struct addr_tuple4 *out out->addr_type = ADDR_TUPLE4_TYPE_V6; out->addr_v6.src_addr = orin->addr_v6.dst_addr; out->addr_v6.dst_addr = orin->addr_v6.src_addr; - out->addr_v6.src_port = orin->addr_v6.dst_port; - out->addr_v6.dst_port = orin->addr_v6.src_port; + out->src_port = orin->dst_port; + out->dst_port = orin->src_port; } } \ No newline at end of file diff --git a/common/src/raw_packet.cpp b/common/src/raw_packet.cpp new file mode 100644 index 0000000..63c4c6b --- /dev/null +++ b/common/src/raw_packet.cpp @@ -0,0 +1,959 @@ +#include +#include +#include +#include + +#include +#include +#define __FAVOR_BSD 1 +#include +#include +#include +#include + +#include "log.h" +#include "addr_tuple4.h" +#include "raw_packet.h" + +#define LOG_TAG "RAW_PACKET_PARSER" + +/****************************************************************************** + * Protocol Struct + ******************************************************************************/ + +struct vlan_hdr +{ + uint16_t vlan_cfi; + uint16_t protocol; +} __attribute__((__packed__)); + +struct vxlan_hdr +{ + uint8_t flags[2]; + uint16_t gdp; // group policy id + uint8_t vni[3]; + uint8_t reserved; +} __attribute__((__packed__)); + +struct gtp_hdr +{ +#if __BYTE_ORDER == __LITTLE_ENDIAN + unsigned char flags; + unsigned char msg_type; + unsigned short len; + unsigned int teid; +#elif __BYTE_ORDER == __BIG_ENDIAN + unsigned int teid; + unsigned short len; + unsigned char msg_type; + unsigned char flags; +#else +#error "Please check " +#endif +} __attribute__((__packed__)); + +#define GTP_HDR_VER_MASK (0xE0) +#define GTP_HDR_FLAG_N_PDU (0x01) +#define GTP_HDR_FLAG_SEQ_NUM (0x02) +#define GTP_HDR_FLAG_NEXT_EXT_HDR (0x04) + +/****************************************************************************** + * Parser Struct + ******************************************************************************/ + +static uint64_t packet_trace_id = 0; + +struct layer_result +{ + uint16_t offset; + enum layer_type type; +}; + +struct layer_result_array +{ + struct layer_result *layers; + uint16_t layers_used; + uint16_t layers_size; +}; + +struct raw_pkt_parser +{ + enum layer_type expect_type; + struct layer_result_array results; + + const void *ptr_pkt_start; + uint64_t pkt_trace_id; +}; + +/****************************************************************************** + * Static API + ******************************************************************************/ + +// parser utils +static void set_addr_tuple4(const void *data, enum layer_type layer_type, struct addr_tuple4 *addr); +static const char *layer_type2str(enum layer_type this_type); +static uint16_t parse_gtphdr_len(const struct gtp_hdr *gtph); + +// parser protocol +static const void *parse_ether(struct raw_pkt_parser *handler, const void *data, size_t length, enum layer_type this_type); +static const void *parse_ipv4(struct raw_pkt_parser *handler, const void *data, size_t length, enum layer_type this_type); +static const void *parse_ipv6(struct raw_pkt_parser *handler, const void *data, size_t length, enum layer_type this_type); +static const void *parse_tcp(struct raw_pkt_parser *handler, const void *data, size_t length, enum layer_type this_type); +static const void *parse_udp(struct raw_pkt_parser *handler, const void *data, size_t length, enum layer_type this_type); +static const void *parse_ppp(struct raw_pkt_parser *handler, const void *data, size_t length, enum layer_type this_type); +static const void *parse_pppoe_ses(struct raw_pkt_parser *handler, const void *data, size_t length, enum layer_type this_type); +// static const void *parse_hdlc(struct raw_pkt_parser *handler, const void *data, size_t length, enum layer_type this_type); +static const void *parse_vxlan(struct raw_pkt_parser *handler, const void *data, size_t length, enum layer_type this_type); +static const void *parse_vlan8021q(struct raw_pkt_parser *handler, const void *data, size_t length, enum layer_type this_type); +static const void *parse_gtpv1_u(struct raw_pkt_parser *handler, const void *data, size_t length, enum layer_type this_type); +static const void *parse_mpls(struct raw_pkt_parser *handler, const void *data, size_t length, enum layer_type this_type); + +/****************************************************************************** + * Public API + ******************************************************************************/ + +struct raw_pkt_parser *raw_packet_parser_create(enum layer_type expect_type, uint16_t expect_results_num) +{ + struct raw_pkt_parser *handler = (struct raw_pkt_parser *)calloc(1, sizeof(struct raw_pkt_parser)); + assert(handler); + + handler->expect_type = expect_type; + handler->results.layers = (struct layer_result *)calloc(expect_results_num, sizeof(struct layer_result)); + assert(handler->results.layers); + handler->results.layers_used = 0; + handler->results.layers_size = expect_results_num; + + return handler; +} + +void raw_packet_parser_destory(struct raw_pkt_parser *handler) +{ + if (handler) + { + if (handler->results.layers) + { + free(handler->results.layers); + handler->results.layers = NULL; + } + + free(handler); + handler = NULL; + } +} + +// return 0 : success +// return -ENOMEM : error +int raw_packet_parser_push(struct raw_pkt_parser *handler, enum layer_type type, uint16_t offset) +{ + struct layer_result_array *result = &handler->results; + + if (result->layers_used >= result->layers_size) + { + return -ENOMEM; + } + + result->layers[result->layers_used].offset = offset; + result->layers[result->layers_used].type = type; + result->layers_used++; + + return 0; +} + +// return PARSE_STATUS_CONTINUE +// return PARSE_STATUS_STOP +enum parse_status raw_packet_parser_status(struct raw_pkt_parser *handler, const void *data, enum layer_type this_type) +{ + /* + * only when this_type & handler->expect_type is true, + * the information of the current layer will be recorded in results. + */ + if (!(this_type & handler->expect_type)) + { + return PARSE_STATUS_CONTINUE; + } + + uint16_t offset = (uintptr_t)data - (uintptr_t)(handler->ptr_pkt_start); + if (raw_packet_parser_push(handler, this_type, offset) < 0) + { + return PARSE_STATUS_STOP; + } + else + { + return PARSE_STATUS_CONTINUE; + } +} + +// return most inner payload +const void *raw_packet_parser_parse(struct raw_pkt_parser *handler, const void *data, size_t length) +{ + handler->ptr_pkt_start = data; + handler->pkt_trace_id = __atomic_fetch_add(&packet_trace_id, 1, __ATOMIC_RELAXED); + + // TESTED + return parse_ether(handler, data, length, LAYER_TYPE_ETHER); +} + +// return 0 : success +// return -1 : error +int raw_packet_parser_get_most_inner_tuple4(struct raw_pkt_parser *handler, struct addr_tuple4 *addr) +{ + const char *l3_layer_data = NULL; + const char *l4_layer_data = NULL; + const struct layer_result *l3_layer_result = NULL; + const struct layer_result *l4_layer_result = NULL; + struct layer_result_array *results = &handler->results; + + // search L4 layer and L3 layer in reverse order + for (int8_t i = results->layers_used - 1; i >= 0; i--) + { + const struct layer_result *layer = &results->layers[i]; + enum layer_type type = layer->type; + + LOG_DEBUG("%s: find most inner tuple4, pkt_trace_id: %lu, layer[%d/%d]: %s", LOG_TAG, handler->pkt_trace_id, i, results->layers_size, layer_type2str(type)); + + // first get L4 layer + if (type & LAYER_TYPE_L4) + { + l4_layer_result = layer; + continue; + } + + // second get L3 layer + if (type & LAYER_TYPE_L3) + { + l3_layer_result = layer; + break; + } + } + + if (l3_layer_result) + { + l3_layer_data = (const char *)handler->ptr_pkt_start + l3_layer_result->offset; + set_addr_tuple4(l3_layer_data, l3_layer_result->type, addr); + } + + if (l4_layer_result) + { + l4_layer_data = (const char *)handler->ptr_pkt_start + l4_layer_result->offset; + set_addr_tuple4(l4_layer_data, l4_layer_result->type, addr); + } + + if (l3_layer_result && l4_layer_result) + { + return 0; + } + else + { + return -1; + } +} + +// return 0 : success +// return -1 : error +int raw_packet_parser_get_most_outer_tuple4(struct raw_pkt_parser *handler, struct addr_tuple4 *addr) +{ + const char *l3_layer_data = NULL; + const char *l4_layer_data = NULL; + const struct layer_result *l3_layer_result = NULL; + const struct layer_result *l4_layer_result = NULL; + struct layer_result_array *results = &handler->results; + + // search L3 layer and L4 layer in order + for (int8_t i = 0; i <= results->layers_used - 1; i++) + { + const struct layer_result *layer = &results->layers[i]; + enum layer_type type = layer->type; + + LOG_DEBUG("%s: find most outer tuple4, pkt_trace_id: %lu, layer[%d/%d]: %s", LOG_TAG, handler->pkt_trace_id, i, results->layers_size, layer_type2str(type)); + + // first get L3 layer + if (type & LAYER_TYPE_L3) + { + l3_layer_result = layer; + continue; + } + + // second get L4 layer + if (type & LAYER_TYPE_L4) + { + l4_layer_result = layer; + break; + } + } + + if (l3_layer_result) + { + l3_layer_data = (const char *)handler->ptr_pkt_start + l3_layer_result->offset; + set_addr_tuple4(l3_layer_data, l3_layer_result->type, addr); + } + + if (l4_layer_result) + { + l4_layer_data = (const char *)handler->ptr_pkt_start + l4_layer_result->offset; + set_addr_tuple4(l4_layer_data, l4_layer_result->type, addr); + } + + if (l3_layer_result && l4_layer_result) + { + return 0; + } + else + { + return -1; + } +} + +// return 0 : success +// return -1 : error +int raw_packet_parser_get_most_inner_address(struct raw_pkt_parser *handler, struct addr_tuple4 *addr) +{ + const char *l3_layer_data = NULL; + struct layer_result_array *results = &handler->results; + + // search L3 layer in reverse order + for (int8_t i = results->layers_used - 1; i >= 0; i--) + { + const struct layer_result *layer = &results->layers[i]; + enum layer_type type = layer->type; + + LOG_DEBUG("%s: find most inner address, pkt_trace_id: %lu, layer[%d/%d]: %s", LOG_TAG, handler->pkt_trace_id, i, results->layers_size, layer_type2str(type)); + if (type & LAYER_TYPE_L3) + { + l3_layer_data = (const char *)handler->ptr_pkt_start + layer->offset; + set_addr_tuple4(l3_layer_data, type, addr); + return 0; + } + } + + return -1; +} + +// return 0 : success +// return -1 : error +int raw_packet_parser_get_most_outer_address(struct raw_pkt_parser *handler, struct addr_tuple4 *addr) +{ + const char *l3_layer_data = NULL; + struct layer_result_array *results = &handler->results; + + // search L3 layer in order + for (int8_t i = 0; i <= results->layers_used - 1; i++) + { + const struct layer_result *layer = &results->layers[i]; + enum layer_type type = layer->type; + + LOG_DEBUG("%s: find most outer address, pkt_trace_id: %lu, layer[%d/%d]: %s", LOG_TAG, handler->pkt_trace_id, i, results->layers_size, layer_type2str(type)); + if (type & LAYER_TYPE_L3) + { + l3_layer_data = (const char *)handler->ptr_pkt_start + layer->offset; + set_addr_tuple4(l3_layer_data, type, addr); + return 0; + } + } + + return -1; +} + +/****************************************************************************** + * Private API + ******************************************************************************/ + +static void set_addr_tuple4(const void *data, enum layer_type layer_type, struct addr_tuple4 *addr) +{ + const struct tcphdr *tcp_hdr = NULL; + const struct udphdr *udp_hdr = NULL; + const struct ip *ipv4_hdr = NULL; + const struct ip6_hdr *ipv6_hdr = NULL; + + switch (layer_type) + { + case LAYER_TYPE_TCP: + tcp_hdr = (const struct tcphdr *)data; + addr->src_port = tcp_hdr->th_sport; + addr->dst_port = tcp_hdr->th_dport; + break; + case LAYER_TYPE_UDP: + udp_hdr = (const struct udphdr *)data; + addr->src_port = udp_hdr->uh_sport; + addr->dst_port = udp_hdr->uh_dport; + break; + case LAYER_TYPE_IPV4: + ipv4_hdr = (const struct ip *)data; + addr->addr_type = ADDR_TUPLE4_TYPE_V4; + addr->addr_v4.src_addr = ipv4_hdr->ip_src; + addr->addr_v4.dst_addr = ipv4_hdr->ip_dst; + break; + case LAYER_TYPE_IPV6: + ipv6_hdr = (const struct ip6_hdr *)data; + addr->addr_type = ADDR_TUPLE4_TYPE_V6; + memcpy(&addr->addr_v6.src_addr, &ipv6_hdr->ip6_src, sizeof(addr->addr_v6.src_addr)); + memcpy(&addr->addr_v6.dst_addr, &ipv6_hdr->ip6_dst, sizeof(addr->addr_v6.dst_addr)); + break; + default: + break; + } +} + +static const char *layer_type2str(enum layer_type this_type) +{ + switch (this_type) + { + case LAYER_TYPE_ETHER: + return "ETHER"; + case LAYER_TYPE_PPP: + return "PPP"; + case LAYER_TYPE_HDLC: + return "HDLC"; + case LAYER_TYPE_VLAN: + return "VLAN"; + case LAYER_TYPE_PPPOE: + return "PPPOE"; + case LAYER_TYPE_MPLS: + return "MPLS"; + case LAYER_TYPE_IPV4: + return "IPV4"; + case LAYER_TYPE_IPV6: + return "IPV6"; + case LAYER_TYPE_UDP: + return "UDP"; + case LAYER_TYPE_TCP: + return "TCP"; + case LAYER_TYPE_G_VXLAN: + return "G_VXLAN"; + case LAYER_TYPE_GTPV1_U: + return "GTPV1_U"; + default: + return "UNKNOWN"; + } +} + +// FROM SAPP +static uint16_t parse_gtphdr_len(const struct gtp_hdr *gtph) +{ + const unsigned char *p_ext_hdr = (unsigned char *)gtph + sizeof(struct gtp_hdr); + unsigned char next_hdr_type; + unsigned char this_ext_field_cont_len; + + // v0 太古老已废弃,目前仅支持 GTPv1 版本 + if (((gtph->flags & GTP_HDR_VER_MASK) >> 5) != 1) + { + return -1; + } + + if (gtph->flags & (GTP_HDR_FLAG_SEQ_NUM | GTP_HDR_FLAG_N_PDU | GTP_HDR_FLAG_NEXT_EXT_HDR)) + { + // skip seq field (2 bytes) + p_ext_hdr += 2; + + // skip N-PDU field (1 byte) + p_ext_hdr++; + + // 解析 GTP 扩展头部字段,参考 wireshark 源码 packet-gtp.c->dissect_gtp_common() + next_hdr_type = *p_ext_hdr; + if (gtph->flags & GTP_HDR_FLAG_NEXT_EXT_HDR) + { + while (next_hdr_type != 0) + { + // 指向长度字段, 以4个字节为单位 + p_ext_hdr++; + this_ext_field_cont_len = *p_ext_hdr * 4 - 2; + + // 指向数据部分第一个字节 + p_ext_hdr++; + p_ext_hdr += this_ext_field_cont_len; + + // 指向下一个头部字段 + next_hdr_type = *p_ext_hdr; + p_ext_hdr++; + } + } + else + { + p_ext_hdr++; + } + } + + return (char *)p_ext_hdr - (char *)gtph; +} + +static const void *parse_ether(struct raw_pkt_parser *handler, const void *data, size_t length, enum layer_type this_type) +{ + if (length < sizeof(struct ethhdr)) + { + LOG_ERROR("%s: pkt_trace_id: %lu, this_layer: %s, err: data not enough", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type)); + return data; + } + + if (raw_packet_parser_status(handler, data, this_type) == PARSE_STATUS_STOP) + { + return data; + } + + struct ethhdr *hdr = (struct ethhdr *)data; + uint16_t next_proto = ntohs(hdr->h_proto); + uint16_t hdr_len = sizeof(struct ethhdr); + const void *data_next_layer = (const char *)data + hdr_len; + size_t data_next_length = length - hdr_len; + + LOG_DEBUG("%s: pkt_trace_id: %lu, this_layer: %s, payload_len: [%lu/%lu]", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type), data_next_length, length); + switch (next_proto) + { + case ETH_P_8021Q: + // TESTED + return parse_vlan8021q(handler, data_next_layer, data_next_length, LAYER_TYPE_VLAN); + case ETH_P_8021AD: + // TODO + return parse_ether(handler, data_next_layer, data_next_length, LAYER_TYPE_ETHER); + case ETH_P_IP: + // TESTED + return parse_ipv4(handler, data_next_layer, data_next_length, LAYER_TYPE_IPV4); + case ETH_P_IPV6: + // TESTED + return parse_ipv6(handler, data_next_layer, data_next_length, LAYER_TYPE_IPV6); + case ETH_P_PPP_SES: + // TODO + return parse_pppoe_ses(handler, data_next_layer, data_next_length, LAYER_TYPE_PPPOE); + case ETH_P_MPLS_UC: + // TESTED + return parse_mpls(handler, data_next_layer, data_next_length, LAYER_TYPE_MPLS); + default: + LOG_ERROR("%s: pkt_trace_id: %lu, this_layer: %s, stop parse next protocol %d", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type), next_proto); + return data_next_layer; + } +} + +static const void *parse_ipv4(struct raw_pkt_parser *handler, const void *data, size_t length, enum layer_type this_type) +{ + if (length < sizeof(struct ip)) + { + LOG_ERROR("%s: pkt_trace_id: %lu, this_layer: %s, err: data not enough", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type)); + return data; + } + + if (raw_packet_parser_status(handler, data, this_type) == PARSE_STATUS_STOP) + { + return data; + } + + struct ip *hdr = (struct ip *)data; + uint16_t next_proto = hdr->ip_p; + uint16_t hdr_len = (hdr->ip_hl & 0xf) * 4u; + const void *data_next_layer = (const char *)data + hdr_len; + size_t data_next_length = length - hdr_len; + + LOG_DEBUG("%s: pkt_trace_id: %lu, this_layer: %s, payload_len: [%lu/%lu]", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type), data_next_length, length); + switch (next_proto) + { + case IPPROTO_TCP: + // TESTED + return parse_tcp(handler, data_next_layer, data_next_length, LAYER_TYPE_TCP); + case IPPROTO_UDP: + // TESTED + return parse_udp(handler, data_next_layer, data_next_length, LAYER_TYPE_UDP); + case IPPROTO_IPIP: + // TESTED + return parse_ipv4(handler, data_next_layer, data_next_length, LAYER_TYPE_IPV4); + case IPPROTO_IPV6: + // TESTED + return parse_ipv6(handler, data_next_layer, data_next_length, LAYER_TYPE_IPV6); + default: + LOG_ERROR("%s: pkt_trace_id: %lu, this_layer: %s, stop parse next protocol %d", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type), next_proto); + return data_next_layer; + } +} + +static const void *parse_ipv6(struct raw_pkt_parser *handler, const void *data, size_t length, enum layer_type this_type) +{ + if (length < sizeof(struct ip6_hdr)) + { + LOG_ERROR("%s: pkt_trace_id: %lu, this_layer: %s, err: data not enough", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type)); + return data; + } + + if (raw_packet_parser_status(handler, data, this_type) == PARSE_STATUS_STOP) + { + return data; + } + + struct ip6_hdr *hdr = (struct ip6_hdr *)data; + uint16_t next_proto = hdr->ip6_nxt; + uint16_t hdr_len = sizeof(struct ip6_hdr); + const void *data_next_layer = (const char *)data + hdr_len; + size_t data_next_length = length - hdr_len; + + LOG_DEBUG("%s: pkt_trace_id: %lu, this_layer: %s, payload_len: [%lu/%lu]", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type), data_next_length, length); + switch (next_proto) + { + case IPPROTO_TCP: + // TESTED + return parse_tcp(handler, data_next_layer, data_next_length, LAYER_TYPE_TCP); + case IPPROTO_UDP: + // TESTED + return parse_udp(handler, data_next_layer, data_next_length, LAYER_TYPE_UDP); + case IPPROTO_IPIP: + // TESTED + return parse_ipv4(handler, data_next_layer, data_next_length, LAYER_TYPE_IPV4); + case IPPROTO_IPV6: + // TESTED + return parse_ipv6(handler, data_next_layer, data_next_length, LAYER_TYPE_IPV6); + default: + LOG_ERROR("%s: pkt_trace_id: %lu, this_layer: %s, stop parse next protocol %d", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type), next_proto); + return data_next_layer; + } +} + +static const void *parse_tcp(struct raw_pkt_parser *handler, const void *data, size_t length, enum layer_type this_type) +{ + if (length < sizeof(struct tcphdr)) + { + LOG_ERROR("%s: pkt_trace_id: %lu, this_layer: %s, err: data not enough", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type)); + return data; + } + + if (raw_packet_parser_status(handler, data, this_type) == PARSE_STATUS_STOP) + { + return data; + } + + struct tcphdr *hdr = (struct tcphdr *)data; + uint16_t hdr_len = hdr->th_off << 2; + const void *data_next_layer = (const char *)data + hdr_len; + size_t data_next_length = length - hdr_len; + + LOG_DEBUG("%s: pkt_trace_id: %lu, this_layer: %s, payload_len: [%lu/%lu]", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type), data_next_length, length); + + return data_next_layer; +} + +static const void *parse_udp(struct raw_pkt_parser *handler, const void *data, size_t length, enum layer_type this_type) +{ + if (length < sizeof(struct udphdr)) + { + LOG_ERROR("%s: pkt_trace_id: %lu, this_layer: %s, err: data not enough", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type)); + return data; + } + + if (raw_packet_parser_status(handler, data, this_type) == PARSE_STATUS_STOP) + { + return data; + } + + struct udphdr *hdr = (struct udphdr *)data; + uint16_t hdr_len = sizeof(struct udphdr); + const void *data_next_layer = (const char *)data + hdr_len; + size_t data_next_length = length - hdr_len; + + LOG_DEBUG("%s: pkt_trace_id: %lu, this_layer: %s, payload_len: [%lu/%lu]", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type), data_next_length, length); + switch (ntohs(hdr->uh_dport)) + { + // VXLAN_DPORT + case 4789: + // TESTED + return parse_vxlan(handler, data_next_layer, data_next_length, LAYER_TYPE_G_VXLAN); + // GTP1U_PORT + case 2152: + // TESTED + return parse_gtpv1_u(handler, data_next_layer, data_next_length, LAYER_TYPE_GTPV1_U); + default: + return data_next_layer; + } +} + +static const void *parse_ppp(struct raw_pkt_parser *handler, const void *data, size_t length, enum layer_type this_type) +{ + if (length < PPP_HDRLEN) + { + LOG_ERROR("%s: pkt_trace_id: %lu, this_layer: %s, err: data not enough", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type)); + return data; + } + + if (raw_packet_parser_status(handler, data, this_type) == PARSE_STATUS_STOP) + { + return data; + } + + uint16_t next_proto = PPP_PROTOCOL(data); + uint16_t hdr_len = PPP_HDRLEN; + const void *data_next_layer = (const char *)data + hdr_len; + size_t data_next_length = length - hdr_len; + + LOG_DEBUG("%s: pkt_trace_id: %lu, this_layer: %s, payload_len: [%lu/%lu]", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type), data_next_length, length); + switch (next_proto) + { + case PPP_IP: + // TODO + return parse_ipv4(handler, data_next_layer, data_next_length, LAYER_TYPE_IPV4); + case PPP_IPV6: + // TODO + return parse_ipv6(handler, data_next_layer, data_next_length, LAYER_TYPE_IPV6); + default: + LOG_ERROR("%s: pkt_trace_id: %lu, this_layer: %s, stop parse next protocol %d", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type), next_proto); + return data_next_layer; + } +} + +static const void *parse_pppoe_ses(struct raw_pkt_parser *handler, const void *data, size_t length, enum layer_type this_type) +{ + if (length < 8) + { + LOG_ERROR("%s: pkt_trace_id: %lu, this_layer: %s, err: data not enough", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type)); + return data; + } + + if (raw_packet_parser_status(handler, data, this_type) == PARSE_STATUS_STOP) + { + return data; + } + + uint16_t next_proto = *((uint16_t *)data + 3); + uint16_t hdr_len = 8; + const void *data_next_layer = (const char *)data + hdr_len; + size_t data_next_length = length - hdr_len; + + LOG_DEBUG("%s: pkt_trace_id: %lu, this_layer: %s, payload_len: [%lu/%lu]", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type), data_next_length, length); + switch (next_proto) + { + // PPPOE_TYPE_IPV4 + case 0x2100: + // TESTED + return parse_ipv4(handler, data_next_layer, data_next_length, LAYER_TYPE_IPV4); + // PPPOE_TYPE_IPV6 + case 0x5700: + // TODO + return parse_ipv6(handler, data_next_layer, data_next_length, LAYER_TYPE_IPV6); + default: + LOG_ERROR("%s: pkt_trace_id: %lu, this_layer: %s, stop parse next protocol %d", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type), next_proto); + return data_next_layer; + } +} + +static const void *parse_hdlc(struct raw_pkt_parser *handler, const void *data, size_t length, enum layer_type this_type) +{ + if (length < 4) + { + LOG_ERROR("%s: pkt_trace_id: %lu, this_layer: %s, err: data not enough", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type)); + return data; + } + + if (raw_packet_parser_status(handler, data, this_type) == PARSE_STATUS_STOP) + { + return data; + } + + uint16_t next_proto = ntohs(*(const uint16_t *)((const char *)data + 2)); + uint16_t hdr_len = 4; + const void *data_next_layer = (const char *)data + hdr_len; + size_t data_next_length = length - hdr_len; + + LOG_DEBUG("%s: pkt_trace_id: %lu, this_layer: %s, payload_len: [%lu/%lu]", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type), data_next_length, length); + // HDLC的协议字段与以太网的类似,对于IPv4,为0x0800 + switch (next_proto) + { + // ETHER_TYPE_IPV4 0x0800 + case 0x0800: + // TODO + return parse_ipv4(handler, data_next_layer, data_next_length, LAYER_TYPE_IPV4); + // ETHER_TYPE_IPV6 0x86DD + case 0x86DD: + // TODO + return parse_ipv6(handler, data_next_layer, data_next_length, LAYER_TYPE_IPV6); + default: + LOG_ERROR("%s: pkt_trace_id: %lu, this_layer: %s, stop parse next protocol %d", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type), next_proto); + return data_next_layer; + } +} + +static const void *parse_vxlan(struct raw_pkt_parser *handler, const void *data, size_t length, enum layer_type this_type) +{ + if (length < sizeof(struct vxlan_hdr)) + { + LOG_ERROR("%s: pkt_trace_id: %lu, this_layer: %s, err: data not enough", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type)); + return NULL; + } + + if (raw_packet_parser_status(handler, data, this_type) == PARSE_STATUS_STOP) + { + return data; + } + + struct vxlan_hdr *vxlan_hdr = (struct vxlan_hdr *)data; + uint16_t hdr_len = sizeof(struct vxlan_hdr); + const void *data_next_layer = (const char *)data + hdr_len; + size_t data_next_length = length - hdr_len; + + LOG_DEBUG("%s: pkt_trace_id: %lu, this_layer: %s, payload_len: [%lu/%lu]", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type), data_next_length, length); + // TESTED + return parse_ether(handler, data_next_layer, data_next_length, LAYER_TYPE_ETHER); + + // TODO HDLC + // TODO PPP +} + +static const void *parse_vlan8021q(struct raw_pkt_parser *handler, const void *data, size_t length, enum layer_type this_type) +{ + if (length < sizeof(struct vlan_hdr)) + { + LOG_ERROR("%s: pkt_trace_id: %lu, this_layer: %s, err: data not enough", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type)); + return NULL; + } + + if (raw_packet_parser_status(handler, data, this_type) == PARSE_STATUS_STOP) + { + return data; + } + + struct vlan_hdr *hdr = (struct vlan_hdr *)data; + uint16_t next_proto = ntohs(hdr->protocol); + uint16_t hdr_len = sizeof(struct vlan_hdr); + const void *data_next_layer = (const char *)data + hdr_len; + size_t data_next_length = length - hdr_len; + + LOG_DEBUG("%s: pkt_trace_id: %lu, this_layer: %s, payload_len: [%lu/%lu]", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type), data_next_length, length); + switch (next_proto) + { + case ETH_P_8021Q: + // TESTED + return parse_vlan8021q(handler, data_next_layer, data_next_length, LAYER_TYPE_VLAN); + case ETH_P_IP: + // TESTED + return parse_ipv4(handler, data_next_layer, data_next_length, LAYER_TYPE_IPV4); + case ETH_P_IPV6: + // TODO + return parse_ipv6(handler, data_next_layer, data_next_length, LAYER_TYPE_IPV6); + case ETH_P_PPP_SES: + // TESTED + return parse_pppoe_ses(handler, data_next_layer, data_next_length, LAYER_TYPE_PPPOE); + case ETH_P_MPLS_UC: + // TODO + return parse_mpls(handler, data_next_layer, data_next_length, LAYER_TYPE_MPLS); + default: + LOG_ERROR("%s: pkt_trace_id: %lu, this_layer: %s, stop parse next protocol %d", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type), next_proto); + return data_next_layer; + } +} + +static const void *parse_gtpv1_u(struct raw_pkt_parser *handler, const void *data, size_t length, enum layer_type this_type) +{ + if (length < sizeof(struct gtp_hdr)) + { + LOG_ERROR("%s: pkt_trace_id: %lu, this_layer: %s, err: data not enough", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type)); + return NULL; + } + + uint16_t hdr_len = parse_gtphdr_len((const struct gtp_hdr *)data); + if (hdr_len < 0) + { + return data; + } + + if (raw_packet_parser_status(handler, data, this_type) == PARSE_STATUS_STOP) + { + return data; + } + + uint8_t next_proto = (((const uint8_t *)((const char *)data + hdr_len))[0]) >> 4; + const void *data_next_layer = (const char *)data + hdr_len; + size_t data_next_length = length - hdr_len; + + LOG_DEBUG("%s: pkt_trace_id: %lu, this_layer: %s, payload_len: [%lu/%lu]", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type), data_next_length, length); + switch (next_proto) + { + case 4: + // TESTED + return parse_ipv4(handler, data_next_layer, data_next_length, LAYER_TYPE_IPV4); + case 6: + // TESTED + return parse_ipv6(handler, data_next_layer, data_next_length, LAYER_TYPE_IPV6); + default: + LOG_ERROR("%s: pkt_trace_id: %lu, this_layer: %s, stop parse next protocol %d", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type), next_proto); + return data_next_layer; + } +} + +static const void *parse_mpls(struct raw_pkt_parser *handler, const void *data, size_t length, enum layer_type this_type) +{ + if (length < 4) + { + LOG_ERROR("%s: pkt_trace_id: %lu, this_layer: %s, err: data not enough", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type)); + return data; + } + + if (raw_packet_parser_status(handler, data, this_type) == PARSE_STATUS_STOP) + { + return data; + } + +#define MPLS_LABEL_MASK (0xFFFFF000) +#define MPLS_EXP_MASK (0x00000E00) +#define MPLS_BLS_MASK (0x00000100) +#define MPLS_TTL_MASK (0x000000FF) + + /* + * MPLS Format + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Label | Exp |S| TTL | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * Label : Label Value 20 bits + * Exp : Experimental Use 3 bits + * S : Bottom of Stack 1 bit + * TTL : Time to Live 8 bits + */ + + uint32_t *hdr = (uint32_t *)data; + // unsigned int mpls_label = (ntohl(*hdr) & MPLS_LABEL_MASK) >> 12; + // unsigned int mpls_exp = (ntohl(*hdr) & MPLS_EXP_MASK) >> 9; + unsigned int mpls_bls = (ntohl(*hdr) & MPLS_BLS_MASK) >> 8; + // unsigned int mpls_ttl = (ntohl(*hdr) & MPLS_TTL_MASK); + + uint16_t hdr_len = 4; + const void *data_next_layer = (const char *)data + hdr_len; + size_t data_next_length = length - hdr_len; + + if (mpls_bls == 1) + { + uint8_t ip_version = (((uint8_t *)data_next_layer)[0]) >> 4; + if (ip_version == 0) + { + /* + * PW Ethernet Control Word + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |0 0 0 0| Reserved | Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * Reference: https://tools.ietf.org/html/rfc4448 + */ + data_next_layer = (const char *)data_next_layer + 4; + data_next_length = data_next_length - 4; + + LOG_DEBUG("%s: pkt_trace_id: %lu, this_layer: %s, payload_len: [%lu/%lu]", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type), data_next_length, length); + // TESTED + return parse_ether(handler, data_next_layer, data_next_length, LAYER_TYPE_ETHER); + } + else if (ip_version == 4) + { + LOG_DEBUG("%s: pkt_trace_id: %lu, this_layer: %s, payload_len: [%lu/%lu]", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type), data_next_length, length); + // TESTED + return parse_ipv4(handler, data_next_layer, data_next_length, LAYER_TYPE_IPV4); + } + else if (ip_version == 6) + { + LOG_DEBUG("%s: pkt_trace_id: %lu, this_layer: %s, payload_len: [%lu/%lu]", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type), data_next_length, length); + // TODO + return parse_ipv6(handler, data_next_layer, data_next_length, LAYER_TYPE_IPV6); + } + else + { + LOG_DEBUG("%s: pkt_trace_id: %lu, this_layer: %s, payload_len: [%lu/%lu]", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type), data_next_length, length); + // TODO + return parse_ether(handler, data_next_layer, data_next_length, LAYER_TYPE_ETHER); + } + } + else + { + LOG_DEBUG("%s: pkt_trace_id: %lu, this_layer: %s, payload_len: [%lu/%lu]", LOG_TAG, handler->pkt_trace_id, layer_type2str(this_type), data_next_length, length); + // TESTED + return parse_mpls(handler, data_next_layer, data_next_length, LAYER_TYPE_MPLS); + } +} \ No newline at end of file diff --git a/common/test/CMakeLists.txt b/common/test/CMakeLists.txt index bbbefd9..b76ed3e 100644 --- a/common/test/CMakeLists.txt +++ b/common/test/CMakeLists.txt @@ -14,10 +14,19 @@ add_executable(gtest_session_table gtest_session_table.cpp) target_include_directories(gtest_session_table PUBLIC ${CMAKE_SOURCE_DIR}/common/include) target_link_libraries(gtest_session_table common gtest) +############################################################################### +# gtest_raw_packet +############################################################################### + +add_executable(gtest_raw_packet gtest_raw_packet.cpp) +target_include_directories(gtest_raw_packet PUBLIC ${CMAKE_SOURCE_DIR}/common/include) +target_link_libraries(gtest_raw_packet common gtest) + ############################################################################### # gtest_discover_tests ############################################################################### include(GoogleTest) gtest_discover_tests(gtest_addr_tuple4) -gtest_discover_tests(gtest_session_table) \ No newline at end of file +gtest_discover_tests(gtest_session_table) +gtest_discover_tests(gtest_raw_packet) \ No newline at end of file diff --git a/common/test/gtest_raw_packet.cpp b/common/test/gtest_raw_packet.cpp new file mode 100644 index 0000000..17ec4a4 --- /dev/null +++ b/common/test/gtest_raw_packet.cpp @@ -0,0 +1,1378 @@ +#include +#include + +#include "raw_packet.h" +#include "addr_tuple4.h" + +/****************************************************************************** + * Pcap file: 1-ETH_VLAN_VLAN_IP4_IP4_UDP.pcap + * [Protocols in frame: eth:ethertype:vlan:ethertype:vlan:ethertype:ip:ip:udp:data] + ****************************************************************************** + * + * Frame 1: 170 bytes on wire (1360 bits), 170 bytes captured (1360 bits) + * Ethernet II, Src: HuaweiTe_3b:b3:9a (a4:c6:4f:3b:b3:9a), Dst: 00:00:00_00:00:04 (00:00:00:00:00:04) + * Destination: 00:00:00_00:00:04 (00:00:00:00:00:04) + * Source: HuaweiTe_3b:b3:9a (a4:c6:4f:3b:b3:9a) + * Type: 802.1Q Virtual LAN (0x8100) + * 802.1Q Virtual LAN, PRI: 3, DEI: 0, ID: 1624 + * 011. .... .... .... = Priority: Critical Applications (3) + * ...0 .... .... .... = DEI: Ineligible + * .... 0110 0101 1000 = ID: 1624 + * Type: 802.1Q Virtual LAN (0x8100) + * 802.1Q Virtual LAN, PRI: 3, DEI: 0, ID: 505 + * 011. .... .... .... = Priority: Critical Applications (3) + * ...0 .... .... .... = DEI: Ineligible + * .... 0001 1111 1001 = ID: 505 + * Type: IPv4 (0x0800) + * Internet Protocol Version 4, Src: 69.67.35.146, Dst: 41.202.46.110 + * 0100 .... = Version: 4 + * .... 0101 = Header Length: 20 bytes (5) + * Differentiated Services Field: 0xb8 (DSCP: EF PHB, ECN: Not-ECT) + * Total Length: 148 + * Identification: 0xe858 (59480) + * 000. .... = Flags: 0x0 + * ...0 0000 0000 0000 = Fragment Offset: 0 + * Time to Live: 255 + * Protocol: IPIP (4) + * Header Checksum: 0x1148 [validation disabled] + * [Header checksum status: Unverified] + * Source Address: 69.67.35.146 + * Destination Address: 41.202.46.110 + * Internet Protocol Version 4, Src: 10.10.100.25, Dst: 10.10.101.2 + * 0100 .... = Version: 4 + * .... 0101 = Header Length: 20 bytes (5) + * Differentiated Services Field: 0xb8 (DSCP: EF PHB, ECN: Not-ECT) + * Total Length: 128 + * Identification: 0x0001 (1) + * 000. .... = Flags: 0x0 + * ...0 0000 0000 0000 = Fragment Offset: 0 + * Time to Live: 254 + * Protocol: UDP (17) + * Header Checksum: 0xde84 [validation disabled] + * [Header checksum status: Unverified] + * Source Address: 10.10.100.25 + * Destination Address: 10.10.101.2 + * User Datagram Protocol, Src Port: 62367, Dst Port: 17000 + * Source Port: 62367 + * Destination Port: 17000 + * Length: 108 + * Checksum: 0x4b9a [unverified] + * [Checksum Status: Unverified] + * [Stream index: 0] + * [Timestamps] + * [Time since first frame: 0.000000000 seconds] + * [Time since previous frame: 0.000000000 seconds] + * UDP payload (100 bytes) + * Data (100 bytes) + */ + +unsigned char data1[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xa4, 0xc6, 0x4f, 0x3b, 0xb3, 0x9a, 0x81, 0x00, 0x66, 0x58, 0x81, 0x00, 0x61, 0xf9, 0x08, 0x00, 0x45, 0xb8, 0x00, 0x94, + 0xe8, 0x58, 0x00, 0x00, 0xff, 0x04, 0x11, 0x48, 0x45, 0x43, 0x23, 0x92, 0x29, 0xca, 0x2e, 0x6e, 0x45, 0xb8, 0x00, 0x80, 0x00, 0x01, 0x00, 0x00, 0xfe, 0x11, + 0xde, 0x84, 0x0a, 0x0a, 0x64, 0x19, 0x0a, 0x0a, 0x65, 0x02, 0xf3, 0x9f, 0x42, 0x68, 0x00, 0x6c, 0x4b, 0x9a, 0x00, 0x02, 0x00, 0x00, 0x04, 0x73, 0x6c, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, + 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd, 0xab, 0xcd}; + +TEST(RAW_PACKET, ETH_VLAN_VLAN_IP4_IP4_UDP) +{ + struct raw_pkt_parser *handler = raw_packet_parser_create(LAYER_TYPE_ALL, 8); + EXPECT_TRUE(handler != nullptr); + + const void *payload = raw_packet_parser_parse(handler, (const void *)data1, sizeof(data1)); + EXPECT_TRUE(payload != nullptr); + EXPECT_TRUE((char *)payload - (char *)&data1 == 70); + + struct addr_tuple4 inner_addr; + struct addr_tuple4 outer_addr; + char *inner_addr_str = NULL; + char *outer_addr_str = NULL; + + memset(&inner_addr, 0, sizeof(inner_addr)); + memset(&outer_addr, 0, sizeof(outer_addr)); + EXPECT_TRUE(raw_packet_parser_get_most_inner_tuple4(handler, &inner_addr) == 0); + EXPECT_TRUE(raw_packet_parser_get_most_outer_tuple4(handler, &outer_addr) == 0); + inner_addr_str = addr_tuple4_to_str(&inner_addr); + outer_addr_str = addr_tuple4_to_str(&outer_addr); + EXPECT_STREQ(inner_addr_str, "10.10.100.25 62367 10.10.101.2 17000"); + EXPECT_STREQ(outer_addr_str, "10.10.100.25 62367 10.10.101.2 17000"); + free(inner_addr_str); + free(outer_addr_str); + + memset(&inner_addr, 0, sizeof(inner_addr)); + memset(&outer_addr, 0, sizeof(outer_addr)); + EXPECT_TRUE(raw_packet_parser_get_most_inner_address(handler, &inner_addr) == 0); + EXPECT_TRUE(raw_packet_parser_get_most_outer_address(handler, &outer_addr) == 0); + inner_addr_str = addr_tuple4_to_str(&inner_addr); + outer_addr_str = addr_tuple4_to_str(&outer_addr); + EXPECT_STREQ(inner_addr_str, "10.10.100.25 0 10.10.101.2 0"); + EXPECT_STREQ(outer_addr_str, "69.67.35.146 0 41.202.46.110 0"); + free(inner_addr_str); + free(outer_addr_str); + + raw_packet_parser_destory(handler); +} + +/****************************************************************************** + * Pcap file: 2-ETH_IP6_IP4_TCP_SSH.pcap + * [Protocols in frame: eth:ethertype:ipv6:ip:tcp:ssh] + ****************************************************************************** + * + * Frame 1: 726 bytes on wire (5808 bits), 726 bytes captured (5808 bits) + * Ethernet II, Src: EvocInte_36:51:3c (00:22:46:36:51:3c), Dst: EvocInte_36:51:38 (00:22:46:36:51:38) + * Destination: EvocInte_36:51:38 (00:22:46:36:51:38) + * Source: EvocInte_36:51:3c (00:22:46:36:51:3c) + * Type: IPv6 (0x86dd) + * Internet Protocol Version 6, Src: 2001::192:168:40:134, Dst: 2001::192:168:40:133 + * 0110 .... = Version: 6 + * .... 0000 0000 .... .... .... .... .... = Traffic Class: 0x00 (DSCP: CS0, ECN: Not-ECT) + * .... 0000 0000 0000 0000 0000 = Flow Label: 0x00000 + * Payload Length: 672 + * Next Header: IPIP (4) + * Hop Limit: 64 + * Source Address: 2001::192:168:40:134 + * Destination Address: 2001::192:168:40:133 + * [Source Teredo Server IPv4: 0.0.0.0] + * [Source Teredo Port: 65175] + * [Source Teredo Client IPv4: 255.191.254.203] + * [Destination Teredo Server IPv4: 0.0.0.0] + * [Destination Teredo Port: 65175] + * [Destination Teredo Client IPv4: 255.191.254.204] + * Internet Protocol Version 4, Src: 1.1.1.1, Dst: 2.2.2.2 + * 0100 .... = Version: 4 + * .... 0101 = Header Length: 20 bytes (5) + * Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT) + * Total Length: 672 + * Identification: 0x0968 (2408) + * 000. .... = Flags: 0x0 + * ...0 0000 0000 0000 = Fragment Offset: 0 + * Time to Live: 212 + * Protocol: TCP (6) + * Header Checksum: 0xd4ea [validation disabled] + * [Header checksum status: Unverified] + * Source Address: 1.1.1.1 + * Destination Address: 2.2.2.2 + * Transmission Control Protocol, Src Port: 57639, Dst Port: 22, Seq: 1, Ack: 1, Len: 632 + * Source Port: 57639 + * Destination Port: 22 + * [Stream index: 0] + * [Conversation completeness: Incomplete (8)] + * [TCP Segment Len: 632] + * Sequence Number: 1 (relative sequence number) + * Sequence Number (raw): 1508621024 + * [Next Sequence Number: 633 (relative sequence number)] + * Acknowledgment Number: 1 (relative ack number) + * Acknowledgment number (raw): 2828957019 + * 0101 .... = Header Length: 20 bytes (5) + * Flags: 0x018 (PSH, ACK) + * Window: 28584 + * [Calculated window size: 28584] + * [Window size scaling factor: -1 (unknown)] + * Checksum: 0xc51f [unverified] + * [Checksum Status: Unverified] + * Urgent Pointer: 0 + * [Timestamps] + * [SEQ/ACK analysis] + * TCP payload (632 bytes) + * SSH Protocol + */ + +unsigned char data2[] = { + 0x00, 0x22, 0x46, 0x36, 0x51, 0x38, 0x00, 0x22, 0x46, 0x36, 0x51, 0x3c, 0x86, 0xdd, 0x60, 0x00, 0x00, 0x00, 0x02, 0xa0, 0x04, 0x40, 0x20, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x92, 0x01, 0x68, 0x00, 0x40, 0x01, 0x34, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x92, 0x01, 0x68, 0x00, 0x40, + 0x01, 0x33, 0x45, 0x00, 0x02, 0xa0, 0x09, 0x68, 0x00, 0x00, 0xd4, 0x06, 0xd4, 0xea, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0xe1, 0x27, 0x00, 0x16, + 0x59, 0xeb, 0xba, 0xe0, 0xa8, 0x9e, 0x75, 0x5b, 0x50, 0x18, 0x6f, 0xa8, 0xc5, 0x1f, 0x00, 0x00, 0x4f, 0xe3, 0xa9, 0x48, 0x9b, 0xbe, 0xa8, 0x07, 0x0e, 0xbb, + 0x5b, 0xf1, 0x15, 0x1d, 0xc9, 0xbe, 0xdf, 0x78, 0x89, 0xa2, 0x8f, 0x12, 0x5f, 0xad, 0x51, 0xd5, 0xfa, 0xa7, 0x0b, 0xf2, 0x34, 0x00, 0x5b, 0x77, 0xae, 0xab, + 0xe4, 0x49, 0xa7, 0xa5, 0xa7, 0x1f, 0xda, 0x90, 0xcc, 0xe1, 0x8e, 0x9f, 0xe9, 0xee, 0x53, 0x59, 0xa4, 0x17, 0xf8, 0x0d, 0x40, 0xe5, 0x75, 0x97, 0xf0, 0x29, + 0xfa, 0x7c, 0xb8, 0x12, 0x7e, 0x93, 0xbc, 0x7e, 0x0a, 0x69, 0x8f, 0x1d, 0x7b, 0x1a, 0x2e, 0xf6, 0xa6, 0x78, 0x67, 0x26, 0xfe, 0x8f, 0xcf, 0x5a, 0x02, 0x7d, + 0xbb, 0x1b, 0xdb, 0xc7, 0x71, 0xee, 0xe9, 0xd9, 0xc1, 0x48, 0xbf, 0xc7, 0xcc, 0x00, 0x82, 0x7f, 0x69, 0x52, 0xa7, 0xe1, 0x12, 0xec, 0xf1, 0x93, 0xa8, 0x55, + 0x5b, 0x33, 0xd3, 0x35, 0x11, 0x5d, 0xf8, 0x3d, 0x5b, 0x94, 0xc9, 0x67, 0xae, 0xba, 0xc0, 0x4a, 0x8b, 0x25, 0x8d, 0xbf, 0xd4, 0xcc, 0x24, 0xb7, 0x3d, 0x0f, + 0x1a, 0x57, 0x20, 0x5c, 0x64, 0x62, 0xf7, 0x3c, 0xff, 0xaf, 0x6b, 0xf2, 0xf3, 0xca, 0xd1, 0xcb, 0x7b, 0x9f, 0xc1, 0x31, 0x25, 0x01, 0xd1, 0x18, 0x78, 0x81, + 0xf8, 0xae, 0x61, 0x4b, 0x59, 0xa1, 0xbe, 0x4a, 0x94, 0x12, 0xa3, 0x05, 0x4a, 0x26, 0x85, 0xbd, 0x5e, 0x59, 0xb2, 0xc2, 0x24, 0xec, 0xd6, 0x94, 0x6e, 0xc5, + 0x7a, 0xdf, 0x21, 0x21, 0xe4, 0x06, 0x67, 0x89, 0xe0, 0x76, 0x85, 0xa9, 0x00, 0x43, 0xfe, 0x72, 0x8c, 0x10, 0xe4, 0x96, 0x63, 0x1a, 0xe8, 0x84, 0xe1, 0x86, + 0xa2, 0xa5, 0x67, 0x31, 0x67, 0x44, 0xca, 0xec, 0xe8, 0xa1, 0x3e, 0x5f, 0x4e, 0x71, 0x5d, 0xd4, 0x34, 0xa9, 0x3d, 0xfa, 0x6a, 0xdb, 0xfb, 0x28, 0x2b, 0x70, + 0xcc, 0xf1, 0x3c, 0x7c, 0xf5, 0x39, 0xb5, 0xd0, 0xa2, 0x56, 0x22, 0x96, 0x7e, 0xc5, 0x0e, 0x66, 0x2d, 0xcd, 0x5c, 0x33, 0x43, 0x1c, 0xca, 0x17, 0x77, 0x46, + 0xb2, 0x41, 0x06, 0x8a, 0x7c, 0x7c, 0x66, 0x06, 0x18, 0x33, 0x21, 0x16, 0x8f, 0x5a, 0xb7, 0xdd, 0x10, 0xa1, 0xab, 0xe9, 0x66, 0xf7, 0x90, 0x22, 0x2c, 0xbe, + 0xdd, 0xad, 0xe1, 0x40, 0xe9, 0x21, 0x53, 0x97, 0x07, 0x97, 0x6b, 0xd6, 0x91, 0x11, 0x44, 0x4e, 0x9d, 0x1f, 0x57, 0x07, 0xed, 0xa2, 0xac, 0x77, 0xc0, 0x84, + 0xb7, 0xc5, 0x2b, 0xaa, 0x17, 0xd2, 0xdb, 0x2a, 0x15, 0x47, 0x2b, 0x69, 0xf1, 0xb4, 0xb5, 0x8f, 0x98, 0xcf, 0x26, 0x03, 0xf0, 0x4b, 0x1a, 0xba, 0x94, 0xc4, + 0x12, 0xe3, 0xd1, 0x38, 0x0c, 0x2e, 0x87, 0x33, 0x0f, 0xe1, 0xa6, 0xba, 0x75, 0xd0, 0xa4, 0x94, 0x80, 0x49, 0x67, 0xa8, 0x90, 0x31, 0x19, 0xaa, 0xf9, 0x78, + 0x0d, 0xdd, 0x64, 0xe3, 0xc7, 0x0e, 0x81, 0xa7, 0x6b, 0x44, 0x0c, 0xb5, 0xa0, 0x25, 0x8a, 0xa2, 0xdc, 0x5e, 0xbc, 0xcd, 0xb4, 0x87, 0x1b, 0x6c, 0x08, 0x38, + 0x63, 0xa8, 0xc1, 0xde, 0xe2, 0xa1, 0xa4, 0x19, 0x1e, 0x3c, 0x67, 0x3b, 0xf7, 0x7f, 0x67, 0xfb, 0x50, 0x9a, 0x06, 0x5c, 0xdd, 0xf2, 0x26, 0x2c, 0xb9, 0xd2, + 0xbd, 0x80, 0xd5, 0xfc, 0xc5, 0x54, 0x6c, 0xc1, 0xea, 0x76, 0x3e, 0xd4, 0xbb, 0x57, 0x65, 0x6a, 0xf8, 0x8e, 0x3e, 0x93, 0xe5, 0x03, 0xfc, 0xce, 0xf1, 0x1c, + 0xf3, 0x10, 0xae, 0x87, 0x78, 0x46, 0x02, 0x63, 0xc5, 0xc0, 0x41, 0xbd, 0xae, 0x46, 0x68, 0x0c, 0x92, 0x22, 0xa4, 0xc0, 0xce, 0xf3, 0xc4, 0xf7, 0x83, 0xa9, + 0x22, 0x78, 0x74, 0x7f, 0x2e, 0xc1, 0xc6, 0x3b, 0x72, 0x26, 0x4b, 0x45, 0xbd, 0x1b, 0x9f, 0x66, 0x61, 0x46, 0xbb, 0x0f, 0xf3, 0xc5, 0x65, 0x95, 0xbc, 0xae, + 0x8f, 0x37, 0xfd, 0xa3, 0x20, 0xb6, 0xe4, 0xa8, 0xff, 0x45, 0xa1, 0x01, 0xa1, 0x76, 0xb3, 0xad, 0x16, 0x07, 0x39, 0x58, 0x3b, 0x34, 0xe9, 0xe6, 0xc0, 0xee, + 0x7f, 0x65, 0x6f, 0x68, 0xf4, 0x45, 0xa4, 0x85, 0xa7, 0x50, 0x63, 0xce, 0x0b, 0x0d, 0xbd, 0xd1, 0x20, 0xc8, 0x41, 0x37, 0x05, 0x1f, 0x81, 0xf3, 0x7c, 0xe7, + 0x67, 0x15, 0xce, 0xad, 0x76, 0x95, 0x1a, 0x93, 0x4a, 0xab, 0xc4, 0xea, 0x30, 0x44, 0x13, 0x47, 0xec, 0x79, 0xa2, 0x41, 0x0c, 0xdd, 0x42, 0xdf, 0xbf, 0x02, + 0xef, 0x9e, 0x67, 0x7e, 0x1e, 0xb0, 0x2a, 0x7f, 0x97, 0xf3, 0x5a, 0xbc, 0x21, 0x8d, 0xf9, 0xc3, 0x30, 0x45, 0xfe, 0x72, 0x74, 0x04, 0x53, 0x99, 0xe7, 0xd1, + 0x2b, 0xb6, 0x3a, 0x9c, 0x84, 0x0e, 0x15, 0x5e, 0x75, 0x3b, 0xc9, 0x0e, 0x94, 0xe6, 0x48, 0x0e, 0x37, 0x07, 0xf8, 0xd9, 0x59, 0x4b, 0x04, 0x50}; + +TEST(RAW_PACKET, ETH_IP6_IP4_TCP_SSH) +{ + struct raw_pkt_parser *handler = raw_packet_parser_create(LAYER_TYPE_ALL, 8); + EXPECT_TRUE(handler != nullptr); + + const void *payload = raw_packet_parser_parse(handler, (const void *)data2, sizeof(data2)); + EXPECT_TRUE(payload != nullptr); + EXPECT_TRUE((char *)payload - (char *)&data2 == 94); + + struct addr_tuple4 inner_addr; + struct addr_tuple4 outer_addr; + char *inner_addr_str = NULL; + char *outer_addr_str = NULL; + + memset(&inner_addr, 0, sizeof(inner_addr)); + memset(&outer_addr, 0, sizeof(outer_addr)); + EXPECT_TRUE(raw_packet_parser_get_most_inner_tuple4(handler, &inner_addr) == 0); + EXPECT_TRUE(raw_packet_parser_get_most_outer_tuple4(handler, &outer_addr) == 0); + inner_addr_str = addr_tuple4_to_str(&inner_addr); + outer_addr_str = addr_tuple4_to_str(&outer_addr); + EXPECT_STREQ(inner_addr_str, "1.1.1.1 57639 2.2.2.2 22"); + EXPECT_STREQ(outer_addr_str, "1.1.1.1 57639 2.2.2.2 22"); + free(inner_addr_str); + free(outer_addr_str); + + memset(&inner_addr, 0, sizeof(inner_addr)); + memset(&outer_addr, 0, sizeof(outer_addr)); + EXPECT_TRUE(raw_packet_parser_get_most_inner_address(handler, &inner_addr) == 0); + EXPECT_TRUE(raw_packet_parser_get_most_outer_address(handler, &outer_addr) == 0); + inner_addr_str = addr_tuple4_to_str(&inner_addr); + outer_addr_str = addr_tuple4_to_str(&outer_addr); + EXPECT_STREQ(inner_addr_str, "1.1.1.1 0 2.2.2.2 0"); + EXPECT_STREQ(outer_addr_str, "2001::192:168:40:134 0 2001::192:168:40:133 0"); + free(inner_addr_str); + free(outer_addr_str); + + raw_packet_parser_destory(handler); +} + +/****************************************************************************** + * Pcap file: 3-ETH_VLAN_IP6_IP4_GRE_PPP_IP4_UDP_DNS.pcap + * [Protocols in frame: eth:ethertype:vlan:ethertype:ipv6:ip:gre:ppp:ip:udp:dns] + ****************************************************************************** + * + * Frame 1: 272 bytes on wire (2176 bits), 272 bytes captured (2176 bits) + * Ethernet II, Src: Cisco_e6:82:c4 (00:19:06:e6:82:c4), Dst: 10:01:00:00:61:3d (10:01:00:00:61:3d) + * Destination: 10:01:00:00:61:3d (10:01:00:00:61:3d) + * Source: Cisco_e6:82:c4 (00:19:06:e6:82:c4) + * Type: 802.1Q Virtual LAN (0x8100) + * 802.1Q Virtual LAN, PRI: 0, DEI: 0, ID: 100 + * 000. .... .... .... = Priority: Best Effort (default) (0) + * ...0 .... .... .... = DEI: Ineligible + * .... 0000 0110 0100 = ID: 100 + * Type: IPv6 (0x86dd) + * Internet Protocol Version 6, Src: 2607:fcd0:100:2300::b108:2a6b, Dst: 2402:f000:1:8e01::5555 + * 0110 .... = Version: 6 + * .... 0000 0000 .... .... .... .... .... = Traffic Class: 0x00 (DSCP: CS0, ECN: Not-ECT) + * .... 0000 0000 0000 0000 0000 = Flow Label: 0x00000 + * Payload Length: 214 + * Next Header: IPIP (4) + * Hop Limit: 57 + * Source Address: 2607:fcd0:100:2300::b108:2a6b + * Destination Address: 2402:f000:1:8e01::5555 + * Internet Protocol Version 4, Src: 192.52.166.154, Dst: 16.0.0.200 + * 0100 .... = Version: 4 + * .... 0101 = Header Length: 20 bytes (5) + * Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT) + * Total Length: 214 + * Identification: 0x842f (33839) + * 010. .... = Flags: 0x2, Don't fragment + * ...0 0000 0000 0000 = Fragment Offset: 0 + * Time to Live: 64 + * Protocol: Generic Routing Encapsulation (47) + * Header Checksum: 0x3e33 [validation disabled] + * [Header checksum status: Unverified] + * Source Address: 192.52.166.154 + * Destination Address: 16.0.0.200 + * Generic Routing Encapsulation (PPP) + * Flags and Version: 0x3081 + * Protocol Type: PPP (0x880b) + * Payload Length: 178 + * Call ID: 17 + * Sequence Number: 538640 + * Acknowledgment Number: 429725 + * Point-to-Point Protocol + * Address: 0xff + * Control: 0x03 + * Protocol: Internet Protocol version 4 (0x0021) + * Internet Protocol Version 4, Src: 8.8.8.8, Dst: 172.16.44.3 + * 0100 .... = Version: 4 + * .... 0101 = Header Length: 20 bytes (5) + * Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT) + * Total Length: 174 + * Identification: 0x2f9c (12188) + * 000. .... = Flags: 0x0 + * ...0 0000 0000 0000 = Fragment Offset: 0 + * Time to Live: 50 + * Protocol: UDP (17) + * Header Checksum: 0x7080 [validation disabled] + * [Header checksum status: Unverified] + * Source Address: 8.8.8.8 + * Destination Address: 172.16.44.3 + * User Datagram Protocol, Src Port: 53, Dst Port: 9879 + * Source Port: 53 + * Destination Port: 9879 + * Length: 154 + * Checksum: 0x45d9 [unverified] + * [Checksum Status: Unverified] + * [Stream index: 0] + * [Timestamps] + * UDP payload (146 bytes) + * Domain Name System (response) + */ + +unsigned char data3[] = { + 0x10, 0x01, 0x00, 0x00, 0x61, 0x3d, 0x00, 0x19, 0x06, 0xe6, 0x82, 0xc4, 0x81, 0x00, 0x00, 0x64, 0x86, 0xdd, 0x60, 0x00, 0x00, 0x00, 0x00, 0xd6, 0x04, 0x39, + 0x26, 0x07, 0xfc, 0xd0, 0x01, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x08, 0x2a, 0x6b, 0x24, 0x02, 0xf0, 0x00, 0x00, 0x01, 0x8e, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x55, 0x55, 0x45, 0x00, 0x00, 0xd6, 0x84, 0x2f, 0x40, 0x00, 0x40, 0x2f, 0x3e, 0x33, 0xc0, 0x34, 0xa6, 0x9a, 0x10, 0x00, 0x00, 0xc8, + 0x30, 0x81, 0x88, 0x0b, 0x00, 0xb2, 0x00, 0x11, 0x00, 0x08, 0x38, 0x10, 0x00, 0x06, 0x8e, 0x9d, 0xff, 0x03, 0x00, 0x21, 0x45, 0x00, 0x00, 0xae, 0x2f, 0x9c, + 0x00, 0x00, 0x32, 0x11, 0x70, 0x80, 0x08, 0x08, 0x08, 0x08, 0xac, 0x10, 0x2c, 0x03, 0x00, 0x35, 0x26, 0x97, 0x00, 0x9a, 0x45, 0xd9, 0xb4, 0xe2, 0x81, 0x83, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x35, 0x78, 0x71, 0x74, 0x2d, 0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x2d, 0x6d, 0x6f, 0x64, 0x65, 0x32, 0x2d, + 0x37, 0x38, 0x63, 0x30, 0x36, 0x64, 0x63, 0x37, 0x2d, 0x30, 0x34, 0x61, 0x37, 0x2d, 0x34, 0x38, 0x35, 0x33, 0x2d, 0x38, 0x34, 0x38, 0x33, 0x2d, 0x61, 0x35, + 0x36, 0x32, 0x38, 0x39, 0x37, 0x36, 0x65, 0x32, 0x33, 0x33, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x02, 0xf0, 0x00, 0x40, + 0x01, 0x61, 0x0c, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x03, 0x6e, 0x65, 0x74, 0x00, 0x05, 0x6e, 0x73, 0x74, 0x6c, 0x64, + 0x0c, 0x76, 0x65, 0x72, 0x69, 0x73, 0x69, 0x67, 0x6e, 0x2d, 0x67, 0x72, 0x73, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x78, 0x0d, 0x09, 0x09, 0x00, 0x00, 0x07, 0x08, + 0x00, 0x00, 0x03, 0x84, 0x00, 0x09, 0x3a, 0x80, 0x00, 0x01, 0x51, 0x80}; + +#if 0 +TEST(RAW_PACKET, ETH_VLAN_IP6_IP4_GRE_PPP_IP4_UDP_DNS) +{ + struct raw_pkt_parser *handler = raw_packet_parser_create(LAYER_TYPE_ALL, 8); + EXPECT_TRUE(handler != nullptr); + + const void *payload = raw_packet_parser_parse(handler, (const void *)data3, sizeof(data3)); + EXPECT_TRUE(payload != nullptr); + EXPECT_TRUE((char *)payload - (char *)&data3 == 126); + + struct addr_tuple4 inner_addr; + struct addr_tuple4 outer_addr; + char *inner_addr_str = NULL; + char *outer_addr_str = NULL; + + memset(&inner_addr, 0, sizeof(inner_addr)); + memset(&outer_addr, 0, sizeof(outer_addr)); + EXPECT_TRUE(raw_packet_parser_get_most_inner_tuple4(handler, &inner_addr) == 0); + EXPECT_TRUE(raw_packet_parser_get_most_outer_tuple4(handler, &outer_addr) == 0); + inner_addr_str = addr_tuple4_to_str(&inner_addr); + outer_addr_str = addr_tuple4_to_str(&outer_addr); + EXPECT_STREQ(inner_addr_str, "8.8.8.8 53 172.16.44.3 9879"); + EXPECT_STREQ(outer_addr_str, "8.8.8.8 53 172.16.44.3 9879"); + free(inner_addr_str); + free(outer_addr_str); + + memset(&inner_addr, 0, sizeof(inner_addr)); + memset(&outer_addr, 0, sizeof(outer_addr)); + EXPECT_TRUE(raw_packet_parser_get_most_inner_address(handler, &inner_addr) == 0); + EXPECT_TRUE(raw_packet_parser_get_most_outer_address(handler, &outer_addr) == 0); + inner_addr_str = addr_tuple4_to_str(&inner_addr); + outer_addr_str = addr_tuple4_to_str(&outer_addr); + EXPECT_STREQ(inner_addr_str, "8.8.8.8 0 172.16.44.3 0"); + EXPECT_STREQ(outer_addr_str, "2607:fcd0:100:2300::b108:2a6b 0 2402:f000:1:8e01::5555 0"); + free(inner_addr_str); + free(outer_addr_str); + + raw_packet_parser_destory(handler); +} +#endif + +/****************************************************************************** + * Pcap file: 4-ETH_IP4_IP6_TCP.pcap + * [Protocols in frame: eth:ethertype:ip:ipv6:tcp] + ****************************************************************************** + * + * Frame 1: 106 bytes on wire (848 bits), 106 bytes captured (848 bits) + * Ethernet II, Src: JuniperN_45:88:29 (2c:6b:f5:45:88:29), Dst: JuniperN_2a:a2:00 (5c:5e:ab:2a:a2:00) + * Destination: JuniperN_2a:a2:00 (5c:5e:ab:2a:a2:00) + * Source: JuniperN_45:88:29 (2c:6b:f5:45:88:29) + * Type: IPv4 (0x0800) + * Internet Protocol Version 4, Src: 210.77.88.163, Dst: 59.66.4.50 + * 0100 .... = Version: 4 + * .... 0101 = Header Length: 20 bytes (5) + * Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT) + * Total Length: 92 + * Identification: 0x0b4d (2893) + * 000. .... = Flags: 0x0 + * ...0 0000 0000 0000 = Fragment Offset: 0 + * Time to Live: 59 + * Protocol: IPv6 (41) + * Header Checksum: 0x09c8 [validation disabled] + * [Header checksum status: Unverified] + * Source Address: 210.77.88.163 + * Destination Address: 59.66.4.50 + * Internet Protocol Version 6, Src: 2001:da8:200:900e:200:5efe:d24d:58a3, Dst: 2600:140e:6::1702:1058 + * 0110 .... = Version: 6 + * .... 0000 0000 .... .... .... .... .... = Traffic Class: 0x00 (DSCP: CS0, ECN: Not-ECT) + * .... 0000 0000 0000 0000 0000 = Flow Label: 0x00000 + * Payload Length: 32 + * Next Header: TCP (6) + * Hop Limit: 64 + * Source Address: 2001:da8:200:900e:200:5efe:d24d:58a3 + * Destination Address: 2600:140e:6::1702:1058 + * [Source ISATAP IPv4: 210.77.88.163] + * Transmission Control Protocol, Src Port: 52556, Dst Port: 80, Seq: 0, Len: 0 + * Source Port: 52556 + * Destination Port: 80 + * [Stream index: 0] + * [Conversation completeness: Complete, WITH_DATA (31)] + * [TCP Segment Len: 0] + * Sequence Number: 0 (relative sequence number) + * Sequence Number (raw): 2172673142 + * [Next Sequence Number: 1 (relative sequence number)] + * Acknowledgment Number: 0 + * Acknowledgment number (raw): 0 + * 1000 .... = Header Length: 32 bytes (8) + * Flags: 0x002 (SYN) + * Window: 8192 + * [Calculated window size: 8192] + * Checksum: 0xf757 [unverified] + * [Checksum Status: Unverified] + * Urgent Pointer: 0 + * Options: (12 bytes), Maximum segment size, No-Operation (NOP), Window scale, No-Operation (NOP), No-Operation (NOP), SACK permitted + * [Timestamps] + */ + +unsigned char data4[] = { + 0x5c, 0x5e, 0xab, 0x2a, 0xa2, 0x00, 0x2c, 0x6b, 0xf5, 0x45, 0x88, 0x29, 0x08, 0x00, 0x45, 0x00, 0x00, 0x5c, 0x0b, 0x4d, 0x00, 0x00, 0x3b, 0x29, 0x09, 0xc8, + 0xd2, 0x4d, 0x58, 0xa3, 0x3b, 0x42, 0x04, 0x32, 0x60, 0x00, 0x00, 0x00, 0x00, 0x20, 0x06, 0x40, 0x20, 0x01, 0x0d, 0xa8, 0x02, 0x00, 0x90, 0x0e, 0x02, 0x00, + 0x5e, 0xfe, 0xd2, 0x4d, 0x58, 0xa3, 0x26, 0x00, 0x14, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x02, 0x10, 0x58, 0xcd, 0x4c, 0x00, 0x50, + 0x81, 0x80, 0x5c, 0x76, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, 0x20, 0x00, 0xf7, 0x57, 0x00, 0x00, 0x02, 0x04, 0x04, 0xc4, 0x01, 0x03, 0x03, 0x08, 0x01, 0x01, + 0x04, 0x02}; + +TEST(RAW_PACKET, ETH_IP4_IP6_TCP) +{ + struct raw_pkt_parser *handler = raw_packet_parser_create(LAYER_TYPE_ALL, 8); + EXPECT_TRUE(handler != nullptr); + + const void *payload = raw_packet_parser_parse(handler, (const void *)data4, sizeof(data4)); + EXPECT_TRUE(payload != nullptr); + EXPECT_TRUE((char *)payload - (char *)&data4 == 106); + + struct addr_tuple4 inner_addr; + struct addr_tuple4 outer_addr; + char *inner_addr_str = NULL; + char *outer_addr_str = NULL; + + memset(&inner_addr, 0, sizeof(inner_addr)); + memset(&outer_addr, 0, sizeof(outer_addr)); + EXPECT_TRUE(raw_packet_parser_get_most_inner_tuple4(handler, &inner_addr) == 0); + EXPECT_TRUE(raw_packet_parser_get_most_outer_tuple4(handler, &outer_addr) == 0); + inner_addr_str = addr_tuple4_to_str(&inner_addr); + outer_addr_str = addr_tuple4_to_str(&outer_addr); + EXPECT_STREQ(inner_addr_str, "2001:da8:200:900e:200:5efe:d24d:58a3 52556 2600:140e:6::1702:1058 80"); + EXPECT_STREQ(outer_addr_str, "2001:da8:200:900e:200:5efe:d24d:58a3 52556 2600:140e:6::1702:1058 80"); + free(inner_addr_str); + free(outer_addr_str); + + memset(&inner_addr, 0, sizeof(inner_addr)); + memset(&outer_addr, 0, sizeof(outer_addr)); + EXPECT_TRUE(raw_packet_parser_get_most_inner_address(handler, &inner_addr) == 0); + EXPECT_TRUE(raw_packet_parser_get_most_outer_address(handler, &outer_addr) == 0); + inner_addr_str = addr_tuple4_to_str(&inner_addr); + outer_addr_str = addr_tuple4_to_str(&outer_addr); + EXPECT_STREQ(inner_addr_str, "2001:da8:200:900e:200:5efe:d24d:58a3 0 2600:140e:6::1702:1058 0"); + EXPECT_STREQ(outer_addr_str, "210.77.88.163 0 59.66.4.50 0"); + free(inner_addr_str); + free(outer_addr_str); + + raw_packet_parser_destory(handler); +} + +/****************************************************************************** + * Pcap file: 5-ETH_IP6_IP6_UDP.pcap + * [Protocols in frame: eth:ethertype:ipv6:ipv6:udp:data] + ****************************************************************************** + * + * Frame 1: 106 bytes on wire (848 bits), 106 bytes captured (848 bits) + * Ethernet II, Src: 00:00:00_00:00:00 (00:00:00:00:00:00), Dst: Broadcast (ff:ff:ff:ff:ff:ff) + * Destination: Broadcast (ff:ff:ff:ff:ff:ff) + * Source: 00:00:00_00:00:00 (00:00:00:00:00:00) + * Type: IPv6 (0x86dd) + * Internet Protocol Version 6, Src: 2001:4f8:4:7:2e0:81ff:fe52:ffff, Dst: 2001:4f8:4:7:2e0:81ff:fe52:9a6b + * 0110 .... = Version: 6 + * .... 0000 0000 .... .... .... .... .... = Traffic Class: 0x00 (DSCP: CS0, ECN: Not-ECT) + * .... 0000 0000 0000 0000 0000 = Flow Label: 0x00000 + * Payload Length: 52 + * Next Header: IPv6 (41) + * Hop Limit: 64 + * Source Address: 2001:4f8:4:7:2e0:81ff:fe52:ffff + * Destination Address: 2001:4f8:4:7:2e0:81ff:fe52:9a6b + * [Source SLAAC MAC: TyanComp_52:ff:ff (00:e0:81:52:ff:ff)] + * [Destination SLAAC MAC: TyanComp_52:9a:6b (00:e0:81:52:9a:6b)] + * Internet Protocol Version 6, Src: dead::beef, Dst: cafe::babe + * 0110 .... = Version: 6 + * .... 0000 0000 .... .... .... .... .... = Traffic Class: 0x00 (DSCP: CS0, ECN: Not-ECT) + * .... 0000 0000 0000 0000 0000 = Flow Label: 0x00000 + * Payload Length: 12 + * Next Header: UDP (17) + * Hop Limit: 64 + * Source Address: dead::beef + * Destination Address: cafe::babe + * User Datagram Protocol, Src Port: 30000, Dst Port: 13000 + * Source Port: 30000 + * Destination Port: 13000 + * Length: 12 + * Checksum: 0x83d2 [unverified] + * [Checksum Status: Unverified] + * [Stream index: 0] + * [Timestamps] + * UDP payload (4 bytes) + * Data (4 bytes) + */ + +unsigned char data5[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0xdd, 0x60, 0x00, 0x00, 0x00, 0x00, 0x34, 0x29, 0x40, 0x20, 0x01, 0x04, 0xf8, + 0x00, 0x04, 0x00, 0x07, 0x02, 0xe0, 0x81, 0xff, 0xfe, 0x52, 0xff, 0xff, 0x20, 0x01, 0x04, 0xf8, 0x00, 0x04, 0x00, 0x07, 0x02, 0xe0, 0x81, 0xff, 0xfe, 0x52, + 0x9a, 0x6b, 0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11, 0x40, 0xde, 0xad, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0xef, + 0xca, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0xbe, 0x75, 0x30, 0x32, 0xc8, 0x00, 0x0c, 0x83, 0xd2, 0x58, 0x58, + 0x58, 0x58}; + +TEST(RAW_PACKET, ETH_IP6_IP6_UDP) +{ + struct raw_pkt_parser *handler = raw_packet_parser_create(LAYER_TYPE_ALL, 8); + EXPECT_TRUE(handler != nullptr); + + const void *payload = raw_packet_parser_parse(handler, (const void *)data5, sizeof(data5)); + EXPECT_TRUE(payload != nullptr); + EXPECT_TRUE((char *)payload - (char *)&data5 == 102); + + struct addr_tuple4 inner_addr; + struct addr_tuple4 outer_addr; + char *inner_addr_str = NULL; + char *outer_addr_str = NULL; + + memset(&inner_addr, 0, sizeof(inner_addr)); + memset(&outer_addr, 0, sizeof(outer_addr)); + EXPECT_TRUE(raw_packet_parser_get_most_inner_tuple4(handler, &inner_addr) == 0); + EXPECT_TRUE(raw_packet_parser_get_most_outer_tuple4(handler, &outer_addr) == 0); + inner_addr_str = addr_tuple4_to_str(&inner_addr); + outer_addr_str = addr_tuple4_to_str(&outer_addr); + EXPECT_STREQ(inner_addr_str, "dead::beef 30000 cafe::babe 13000"); + EXPECT_STREQ(outer_addr_str, "dead::beef 30000 cafe::babe 13000"); + free(inner_addr_str); + free(outer_addr_str); + + memset(&inner_addr, 0, sizeof(inner_addr)); + memset(&outer_addr, 0, sizeof(outer_addr)); + EXPECT_TRUE(raw_packet_parser_get_most_inner_address(handler, &inner_addr) == 0); + EXPECT_TRUE(raw_packet_parser_get_most_outer_address(handler, &outer_addr) == 0); + inner_addr_str = addr_tuple4_to_str(&inner_addr); + outer_addr_str = addr_tuple4_to_str(&outer_addr); + EXPECT_STREQ(inner_addr_str, "dead::beef 0 cafe::babe 0"); + EXPECT_STREQ(outer_addr_str, "2001:4f8:4:7:2e0:81ff:fe52:ffff 0 2001:4f8:4:7:2e0:81ff:fe52:9a6b 0"); + free(inner_addr_str); + free(outer_addr_str); + + raw_packet_parser_destory(handler); +} + +/****************************************************************************** + * Pcap file: 6-ETH_MPLS_IP4_TCP.pcap + * [Protocols in frame: eth:ethertype:mpls:ip:tcp] + ****************************************************************************** + * + * Frame 1: 70 bytes on wire (560 bits), 70 bytes captured (560 bits) + * Ethernet II, Src: Hangzhou_d9:28:cc (00:23:89:d9:28:cc), Dst: HuaweiTe_7f:eb:f7 (d4:6a:a8:7f:eb:f7) + * Destination: HuaweiTe_7f:eb:f7 (d4:6a:a8:7f:eb:f7) + * Source: Hangzhou_d9:28:cc (00:23:89:d9:28:cc) + * Type: MPLS label switched packet (0x8847) + * MultiProtocol Label Switching Header, Label: 18, Exp: 6, S: 1, TTL: 254 + * 0000 0000 0000 0001 0010 .... .... .... = MPLS Label: 18 (0x00012) + * .... .... .... .... .... 110. .... .... = MPLS Experimental Bits: 6 + * .... .... .... .... .... ...1 .... .... = MPLS Bottom Of Label Stack: 1 + * .... .... .... .... .... .... 1111 1110 = MPLS TTL: 254 + * Internet Protocol Version 4, Src: 119.40.37.65, Dst: 123.125.29.250 + * 0100 .... = Version: 4 + * .... 0101 = Header Length: 20 bytes (5) + * Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT) + * Total Length: 52 + * Identification: 0x02a1 (673) + * 010. .... = Flags: 0x2, Don't fragment + * ...0 0000 0000 0000 = Fragment Offset: 0 + * Time to Live: 126 + * Protocol: TCP (6) + * Header Checksum: 0xc442 [validation disabled] + * [Header checksum status: Unverified] + * Source Address: 119.40.37.65 + * Destination Address: 123.125.29.250 + * Transmission Control Protocol, Src Port: 61853, Dst Port: 80, Seq: 0, Len: 0 + * Source Port: 61853 + * Destination Port: 80 + * [Stream index: 0] + * [Conversation completeness: Complete, WITH_DATA (31)] + * [TCP Segment Len: 0] + * Sequence Number: 0 (relative sequence number) + * Sequence Number (raw): 1710561749 + * [Next Sequence Number: 1 (relative sequence number)] + * Acknowledgment Number: 0 + * Acknowledgment number (raw): 0 + * 1000 .... = Header Length: 32 bytes (8) + * Flags: 0x002 (SYN) + * Window: 8192 + * [Calculated window size: 8192] + * Checksum: 0xa777 [unverified] + * [Checksum Status: Unverified] + * Urgent Pointer: 0 + * Options: (12 bytes), Maximum segment size, No-Operation (NOP), Window scale, No-Operation (NOP), No-Operation (NOP), SACK permitted + * [Timestamps] + */ + +unsigned char data6[] = { + 0xd4, 0x6a, 0xa8, 0x7f, 0xeb, 0xf7, 0x00, 0x23, 0x89, 0xd9, 0x28, 0xcc, 0x88, 0x47, 0x00, 0x01, 0x2d, 0xfe, 0x45, 0x00, 0x00, 0x34, 0x02, 0xa1, 0x40, 0x00, + 0x7e, 0x06, 0xc4, 0x42, 0x77, 0x28, 0x25, 0x41, 0x7b, 0x7d, 0x1d, 0xfa, 0xf1, 0x9d, 0x00, 0x50, 0x65, 0xf5, 0x19, 0xd5, 0x00, 0x00, 0x00, 0x00, 0x80, 0x02, + 0x20, 0x00, 0xa7, 0x77, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4, 0x01, 0x03, 0x03, 0x08, 0x01, 0x01, 0x04, 0x02}; + +TEST(RAW_PACKET, ETH_MPLS_IP4_TCP) +{ + struct raw_pkt_parser *handler = raw_packet_parser_create(LAYER_TYPE_ALL, 8); + EXPECT_TRUE(handler != nullptr); + + const void *payload = raw_packet_parser_parse(handler, (const void *)data6, sizeof(data6)); + EXPECT_TRUE(payload != nullptr); + EXPECT_TRUE((char *)payload - (char *)&data6 == 70); + + struct addr_tuple4 inner_addr; + struct addr_tuple4 outer_addr; + char *inner_addr_str = NULL; + char *outer_addr_str = NULL; + + memset(&inner_addr, 0, sizeof(inner_addr)); + memset(&outer_addr, 0, sizeof(outer_addr)); + EXPECT_TRUE(raw_packet_parser_get_most_inner_tuple4(handler, &inner_addr) == 0); + EXPECT_TRUE(raw_packet_parser_get_most_outer_tuple4(handler, &outer_addr) == 0); + inner_addr_str = addr_tuple4_to_str(&inner_addr); + outer_addr_str = addr_tuple4_to_str(&outer_addr); + EXPECT_STREQ(inner_addr_str, "119.40.37.65 61853 123.125.29.250 80"); + EXPECT_STREQ(outer_addr_str, "119.40.37.65 61853 123.125.29.250 80"); + free(inner_addr_str); + free(outer_addr_str); + + memset(&inner_addr, 0, sizeof(inner_addr)); + memset(&outer_addr, 0, sizeof(outer_addr)); + EXPECT_TRUE(raw_packet_parser_get_most_inner_address(handler, &inner_addr) == 0); + EXPECT_TRUE(raw_packet_parser_get_most_outer_address(handler, &outer_addr) == 0); + inner_addr_str = addr_tuple4_to_str(&inner_addr); + outer_addr_str = addr_tuple4_to_str(&outer_addr); + EXPECT_STREQ(inner_addr_str, "119.40.37.65 0 123.125.29.250 0"); + EXPECT_STREQ(outer_addr_str, "119.40.37.65 0 123.125.29.250 0"); + free(inner_addr_str); + free(outer_addr_str); + + raw_packet_parser_destory(handler); +} + +/****************************************************************************** + * Pcap file: 7-ETH_MPLS_MPLS_IP4_TCP.pcap + * [Protocols in frame: eth:ethertype:mpls:ip:tcp] + ****************************************************************************** + * + * Frame 1: 66 bytes on wire (528 bits), 66 bytes captured (528 bits) + * Ethernet II, Src: Cisco_05:28:38 (00:30:96:05:28:38), Dst: Cisco_e6:fc:39 (00:30:96:e6:fc:39) + * Destination: Cisco_e6:fc:39 (00:30:96:e6:fc:39) + * Source: Cisco_05:28:38 (00:30:96:05:28:38) + * Type: MPLS label switched packet (0x8847) + * MultiProtocol Label Switching Header, Label: 18, Exp: 5, S: 0, TTL: 255 + * 0000 0000 0000 0001 0010 .... .... .... = MPLS Label: 18 (0x00012) + * .... .... .... .... .... 101. .... .... = MPLS Experimental Bits: 5 + * .... .... .... .... .... ...0 .... .... = MPLS Bottom Of Label Stack: 0 + * .... .... .... .... .... .... 1111 1111 = MPLS TTL: 255 + * MultiProtocol Label Switching Header, Label: 16, Exp: 5, S: 1, TTL: 255 + * 0000 0000 0000 0001 0000 .... .... .... = MPLS Label: 16 (0x00010) + * .... .... .... .... .... 101. .... .... = MPLS Experimental Bits: 5 + * .... .... .... .... .... ...1 .... .... = MPLS Bottom Of Label Stack: 1 + * .... .... .... .... .... .... 1111 1111 = MPLS TTL: 255 + * Internet Protocol Version 4, Src: 10.31.0.1, Dst: 10.34.0.1 + * 0100 .... = Version: 4 + * .... 0101 = Header Length: 20 bytes (5) + * Differentiated Services Field: 0xb0 (DSCP: Unknown, ECN: Not-ECT) + * Total Length: 44 + * Identification: 0x0000 (0) + * 000. .... = Flags: 0x0 + * ...0 0000 0000 0000 = Fragment Offset: 0 + * Time to Live: 255 + * Protocol: TCP (6) + * Header Checksum: 0xa6d9 [validation disabled] + * [Header checksum status: Unverified] + * Source Address: 10.31.0.1 + * Destination Address: 10.34.0.1 + * Transmission Control Protocol, Src Port: 11001, Dst Port: 23, Seq: 0, Len: 0 + * Source Port: 11001 + * Destination Port: 23 + * [Stream index: 0] + * [Conversation completeness: Incomplete (29)] + * [TCP Segment Len: 0] + * Sequence Number: 0 (relative sequence number) + * Sequence Number (raw): 3481568569 + * [Next Sequence Number: 1 (relative sequence number)] + * Acknowledgment Number: 0 + * Acknowledgment number (raw): 0 + * 0110 .... = Header Length: 24 bytes (6) + * Flags: 0x002 (SYN) + * Window: 4128 + * [Calculated window size: 4128] + * Checksum: 0xf791 [unverified] + * [Checksum Status: Unverified] + * Urgent Pointer: 0 + * Options: (4 bytes), Maximum segment size + * [Timestamps] + */ + +unsigned char data7[] = { + 0x00, 0x30, 0x96, 0xe6, 0xfc, 0x39, 0x00, 0x30, 0x96, 0x05, 0x28, 0x38, 0x88, 0x47, 0x00, 0x01, 0x2a, 0xff, 0x00, 0x01, 0x0b, 0xff, 0x45, 0xb0, 0x00, 0x2c, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x06, 0xa6, 0xd9, 0x0a, 0x1f, 0x00, 0x01, 0x0a, 0x22, 0x00, 0x01, 0x2a, 0xf9, 0x00, 0x17, 0xcf, 0x84, 0x85, 0x39, 0x00, 0x00, + 0x00, 0x00, 0x60, 0x02, 0x10, 0x20, 0xf7, 0x91, 0x00, 0x00, 0x02, 0x04, 0x02, 0x18}; + +TEST(RAW_PACKET, ETH_MPLS_MPLS_IP4_TCP) +{ + struct raw_pkt_parser *handler = raw_packet_parser_create(LAYER_TYPE_ALL, 8); + EXPECT_TRUE(handler != nullptr); + + const void *payload = raw_packet_parser_parse(handler, (const void *)data7, sizeof(data7)); + EXPECT_TRUE(payload != nullptr); + EXPECT_TRUE((char *)payload - (char *)&data7 == 66); + + struct addr_tuple4 inner_addr; + struct addr_tuple4 outer_addr; + char *inner_addr_str = NULL; + char *outer_addr_str = NULL; + + memset(&inner_addr, 0, sizeof(inner_addr)); + memset(&outer_addr, 0, sizeof(outer_addr)); + EXPECT_TRUE(raw_packet_parser_get_most_inner_tuple4(handler, &inner_addr) == 0); + EXPECT_TRUE(raw_packet_parser_get_most_outer_tuple4(handler, &outer_addr) == 0); + inner_addr_str = addr_tuple4_to_str(&inner_addr); + outer_addr_str = addr_tuple4_to_str(&outer_addr); + EXPECT_STREQ(inner_addr_str, "10.31.0.1 11001 10.34.0.1 23"); + EXPECT_STREQ(outer_addr_str, "10.31.0.1 11001 10.34.0.1 23"); + free(inner_addr_str); + free(outer_addr_str); + + memset(&inner_addr, 0, sizeof(inner_addr)); + memset(&outer_addr, 0, sizeof(outer_addr)); + EXPECT_TRUE(raw_packet_parser_get_most_inner_address(handler, &inner_addr) == 0); + EXPECT_TRUE(raw_packet_parser_get_most_outer_address(handler, &outer_addr) == 0); + inner_addr_str = addr_tuple4_to_str(&inner_addr); + outer_addr_str = addr_tuple4_to_str(&outer_addr); + EXPECT_STREQ(inner_addr_str, "10.31.0.1 0 10.34.0.1 0"); + EXPECT_STREQ(outer_addr_str, "10.31.0.1 0 10.34.0.1 0"); + free(inner_addr_str); + free(outer_addr_str); + + raw_packet_parser_destory(handler); +} + +/****************************************************************************** + * Pcap file: 8-ETH_VLAN_PPPOE_IP4_TCP.pcap + * [Protocols in frame: eth:ethertype:vlan:ethertype:pppoes:ppp:ip:tcp] + ****************************************************************************** + * + * Frame 55: 78 bytes on wire (624 bits), 78 bytes captured (624 bits) + * Ethernet II, Src: 00:00:00_00:04:46 (00:00:00:00:04:46), Dst: 18:10:04:00:02:27 (18:10:04:00:02:27) + * Destination: 18:10:04:00:02:27 (18:10:04:00:02:27) + * Source: 00:00:00_00:04:46 (00:00:00:00:04:46) + * Type: 802.1Q Virtual LAN (0x8100) + * 802.1Q Virtual LAN, PRI: 3, DEI: 0, ID: 1476 + * 011. .... .... .... = Priority: Critical Applications (3) + * ...0 .... .... .... = DEI: Ineligible + * .... 0101 1100 0100 = ID: 1476 + * Type: PPPoE Session (0x8864) + * PPP-over-Ethernet Session + * 0001 .... = Version: 1 + * .... 0001 = Type: 1 + * Code: Session Data (0x00) + * Session ID: 0xb4bc + * Payload Length: 54 + * Point-to-Point Protocol + * Protocol: Internet Protocol version 4 (0x0021) + * Internet Protocol Version 4, Src: 100.65.55.0, Dst: 91.185.14.33 + * 0100 .... = Version: 4 + * .... 0101 = Header Length: 20 bytes (5) + * Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT) + * Total Length: 52 + * Identification: 0x4ba7 (19367) + * 010. .... = Flags: 0x2, Don't fragment + * ...0 0000 0000 0000 = Fragment Offset: 0 + * Time to Live: 63 + * Protocol: TCP (6) + * Header Checksum: 0xeb01 [validation disabled] + * [Header checksum status: Unverified] + * Source Address: 100.65.55.0 + * Destination Address: 91.185.14.33 + * Transmission Control Protocol, Src Port: 34532, Dst Port: 443, Seq: 491, Ack: 54523, Len: 0 + * Source Port: 34532 + * Destination Port: 443 + * [Stream index: 0] + * [Conversation completeness: Incomplete (12)] + * [TCP Segment Len: 0] + * Sequence Number: 491 (relative sequence number) + * Sequence Number (raw): 3064322674 + * [Next Sequence Number: 491 (relative sequence number)] + * Acknowledgment Number: 54523 (relative ack number) + * Acknowledgment number (raw): 2083649568 + * 1000 .... = Header Length: 32 bytes (8) + * Flags: 0x010 (ACK) + * Window: 4032 + * [Calculated window size: 4032] + * [Window size scaling factor: -1 (unknown)] + * Checksum: 0xc361 [unverified] + * [Checksum Status: Unverified] + * Urgent Pointer: 0 + * Options: (12 bytes), No-Operation (NOP), No-Operation (NOP), Timestamps + * [Timestamps] + * [SEQ/ACK analysis] + */ + +unsigned char data8[] = { + 0x18, 0x10, 0x04, 0x00, 0x02, 0x27, 0x00, 0x00, 0x00, 0x00, 0x04, 0x46, 0x81, 0x00, 0x65, 0xc4, 0x88, 0x64, 0x11, 0x00, 0xb4, 0xbc, 0x00, 0x36, 0x00, 0x21, + 0x45, 0x00, 0x00, 0x34, 0x4b, 0xa7, 0x40, 0x00, 0x3f, 0x06, 0xeb, 0x01, 0x64, 0x41, 0x37, 0x00, 0x5b, 0xb9, 0x0e, 0x21, 0x86, 0xe4, 0x01, 0xbb, 0xb6, 0xa5, + 0xda, 0x72, 0x7c, 0x31, 0xf8, 0x20, 0x80, 0x10, 0x0f, 0xc0, 0xc3, 0x61, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0x00, 0x6f, 0xab, 0xdf, 0x9c, 0x61, 0xc7, 0xc5}; + +TEST(RAW_PACKET, ETH_VLAN_PPPOE_IP4_TCP) +{ + struct raw_pkt_parser *handler = raw_packet_parser_create(LAYER_TYPE_ALL, 8); + EXPECT_TRUE(handler != nullptr); + + const void *payload = raw_packet_parser_parse(handler, (const void *)data8, sizeof(data8)); + EXPECT_TRUE(payload != nullptr); + EXPECT_TRUE((char *)payload - (char *)&data8 == 78); + + struct addr_tuple4 inner_addr; + struct addr_tuple4 outer_addr; + char *inner_addr_str = NULL; + char *outer_addr_str = NULL; + + memset(&inner_addr, 0, sizeof(inner_addr)); + memset(&outer_addr, 0, sizeof(outer_addr)); + EXPECT_TRUE(raw_packet_parser_get_most_inner_tuple4(handler, &inner_addr) == 0); + EXPECT_TRUE(raw_packet_parser_get_most_outer_tuple4(handler, &outer_addr) == 0); + inner_addr_str = addr_tuple4_to_str(&inner_addr); + outer_addr_str = addr_tuple4_to_str(&outer_addr); + EXPECT_STREQ(inner_addr_str, "100.65.55.0 34532 91.185.14.33 443"); + EXPECT_STREQ(outer_addr_str, "100.65.55.0 34532 91.185.14.33 443"); + free(inner_addr_str); + free(outer_addr_str); + + memset(&inner_addr, 0, sizeof(inner_addr)); + memset(&outer_addr, 0, sizeof(outer_addr)); + EXPECT_TRUE(raw_packet_parser_get_most_inner_address(handler, &inner_addr) == 0); + EXPECT_TRUE(raw_packet_parser_get_most_outer_address(handler, &outer_addr) == 0); + inner_addr_str = addr_tuple4_to_str(&inner_addr); + outer_addr_str = addr_tuple4_to_str(&outer_addr); + EXPECT_STREQ(inner_addr_str, "100.65.55.0 0 91.185.14.33 0"); + EXPECT_STREQ(outer_addr_str, "100.65.55.0 0 91.185.14.33 0"); + free(inner_addr_str); + free(outer_addr_str); + + raw_packet_parser_destory(handler); +} + +/****************************************************************************** + * Pcap file: 9-ETH_IP6_UDP_GTP_IP6_TCP_TLS.pcap + * [Protocols in frame: eth:ethertype:ipv6:udp:gtp:ipv6:tcp:ja3:tls] + ****************************************************************************** + * + * Frame 1: 1442 bytes on wire (11536 bits), 1442 bytes captured (11536 bits) + * Ethernet II, Src: zte_0e:f5:40 (74:4a:a4:0e:f5:40), Dst: HuaweiTe_40:e9:c2 (ac:b3:b5:40:e9:c2) + * Destination: HuaweiTe_40:e9:c2 (ac:b3:b5:40:e9:c2) + * Source: zte_0e:f5:40 (74:4a:a4:0e:f5:40) + * Type: IPv6 (0x86dd) + * Internet Protocol Version 6, Src: 2409:8034:4040:5300::105, Dst: 2409:8034:4025::60:61 + * 0110 .... = Version: 6 + * .... 0000 0000 .... .... .... .... .... = Traffic Class: 0x00 (DSCP: CS0, ECN: Not-ECT) + * .... 0000 0000 0000 0000 0000 = Flow Label: 0x00000 + * Payload Length: 1388 + * Next Header: UDP (17) + * Hop Limit: 127 + * Source Address: 2409:8034:4040:5300::105 + * Destination Address: 2409:8034:4025::60:61 + * User Datagram Protocol, Src Port: 2152, Dst Port: 2152 + * Source Port: 2152 + * Destination Port: 2152 + * Length: 1388 + * Checksum: 0xeb00 [unverified] + * [Checksum Status: Unverified] + * [Stream index: 0] + * [Timestamps] + * UDP payload (1380 bytes) + * GPRS Tunneling Protocol + * Flags: 0x30 + * Message Type: T-PDU (0xff) + * Length: 1372 + * TEID: 0x024c3cbd (38550717) + * Internet Protocol Version 6, Src: 2409:8c34:4400:700:0:4:0:3, Dst: 2409:8934:5082:2100:ecad:e0e4:530a:c269 + * 0110 .... = Version: 6 + * .... 0000 0000 .... .... .... .... .... = Traffic Class: 0x00 (DSCP: CS0, ECN: Not-ECT) + * .... 0000 0000 0000 0000 0000 = Flow Label: 0x00000 + * Payload Length: 1332 + * Next Header: TCP (6) + * Hop Limit: 56 + * Source Address: 2409:8c34:4400:700:0:4:0:3 + * Destination Address: 2409:8934:5082:2100:ecad:e0e4:530a:c269 + * Transmission Control Protocol, Src Port: 443, Dst Port: 46582, Seq: 1, Ack: 1, Len: 1312 + * Source Port: 443 + * Destination Port: 46582 + * [Stream index: 0] + * [Conversation completeness: Incomplete (8)] + * [TCP Segment Len: 1312] + * Sequence Number: 1 (relative sequence number) + * Sequence Number (raw): 2198097831 + * [Next Sequence Number: 1313 (relative sequence number)] + * Acknowledgment Number: 1 (relative ack number) + * Acknowledgment number (raw): 2264498872 + * 0101 .... = Header Length: 20 bytes (5) + * Flags: 0x010 (ACK) + * Window: 529 + * [Calculated window size: 529] + * [Window size scaling factor: -1 (unknown)] + * Checksum: 0x2c4b [unverified] + * [Checksum Status: Unverified] + * Urgent Pointer: 0 + * [Timestamps] + * [SEQ/ACK analysis] + * TCP payload (1312 bytes) + * Transport Layer Security + */ + +unsigned char data9[] = { + 0xac, 0xb3, 0xb5, 0x40, 0xe9, 0xc2, 0x74, 0x4a, 0xa4, 0x0e, 0xf5, 0x40, 0x86, 0xdd, 0x60, 0x00, 0x00, 0x00, 0x05, 0x6c, 0x11, 0x7f, 0x24, 0x09, 0x80, 0x34, + 0x40, 0x40, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x05, 0x24, 0x09, 0x80, 0x34, 0x40, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, + 0x00, 0x61, 0x08, 0x68, 0x08, 0x68, 0x05, 0x6c, 0xeb, 0x00, 0x30, 0xff, 0x05, 0x5c, 0x02, 0x4c, 0x3c, 0xbd, 0x60, 0x00, 0x00, 0x00, 0x05, 0x34, 0x06, 0x38, + 0x24, 0x09, 0x8c, 0x34, 0x44, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x24, 0x09, 0x89, 0x34, 0x50, 0x82, 0x21, 0x00, 0xec, 0xad, + 0xe0, 0xe4, 0x53, 0x0a, 0xc2, 0x69, 0x01, 0xbb, 0xb5, 0xf6, 0x83, 0x04, 0x4f, 0xa7, 0x86, 0xf9, 0x82, 0xb8, 0x50, 0x10, 0x02, 0x11, 0x2c, 0x4b, 0x00, 0x00, + 0x17, 0x03, 0x03, 0x3c, 0x8c, 0x87, 0xa0, 0x99, 0x23, 0x5b, 0x53, 0x4a, 0x12, 0x1b, 0xf8, 0xba, 0xe8, 0x83, 0xc2, 0x95, 0xda, 0xb8, 0xea, 0x5b, 0xdc, 0x84, + 0x61, 0xa9, 0x86, 0x7e, 0x43, 0xc7, 0x31, 0x44, 0x6e, 0x11, 0xc1, 0x30, 0x21, 0x03, 0xb4, 0x21, 0x4a, 0xee, 0xc9, 0x2e, 0x14, 0xd2, 0x98, 0x63, 0x12, 0xfe, + 0x79, 0x58, 0xb3, 0x18, 0xa6, 0x8d, 0x0c, 0x62, 0x67, 0x51, 0xef, 0x02, 0x5a, 0xa8, 0xb3, 0x82, 0x1f, 0xe4, 0x51, 0xba, 0xde, 0xee, 0x83, 0x9c, 0x4e, 0xac, + 0x4d, 0xa2, 0xb7, 0x6a, 0x82, 0xe7, 0xbb, 0x00, 0xf7, 0x5a, 0xe7, 0x02, 0x71, 0x7e, 0x7d, 0x6f, 0xf2, 0xe5, 0x47, 0xd0, 0xba, 0x3c, 0x51, 0x09, 0x95, 0xcd, + 0xf6, 0xc9, 0x8b, 0x6f, 0xb0, 0x39, 0x11, 0x0d, 0xe9, 0x0d, 0x4d, 0x29, 0xd4, 0xcb, 0x87, 0xba, 0x11, 0xfa, 0x0d, 0x0b, 0x82, 0x95, 0xa5, 0x84, 0x94, 0x48, + 0xa2, 0xee, 0xa4, 0xb7, 0xb6, 0x76, 0x13, 0x4d, 0x18, 0x42, 0x91, 0x77, 0xad, 0x82, 0x38, 0xee, 0x34, 0x1c, 0xb7, 0xf6, 0x39, 0xdc, 0xa4, 0x23, 0xa1, 0x7c, + 0xa5, 0x0b, 0x7e, 0x4c, 0x8b, 0x81, 0x31, 0x48, 0xea, 0xf4, 0x18, 0x37, 0x09, 0x0a, 0x53, 0x13, 0x05, 0x90, 0x26, 0x10, 0x69, 0xb2, 0xa3, 0x36, 0xbc, 0xa5, + 0x83, 0xd8, 0x16, 0x77, 0x98, 0xc8, 0x21, 0x38, 0xd9, 0x88, 0x0c, 0xa7, 0x16, 0x97, 0x4e, 0x20, 0x6d, 0x68, 0xda, 0x1b, 0x3b, 0x4a, 0x62, 0xe0, 0x36, 0x0d, + 0xbf, 0x30, 0x71, 0xb1, 0xe9, 0xbe, 0x47, 0x77, 0x99, 0xb9, 0xe6, 0x26, 0xab, 0x81, 0x2e, 0x46, 0xf1, 0x1b, 0x1e, 0xfb, 0xd7, 0x81, 0x60, 0x21, 0x4a, 0x71, + 0x85, 0xf7, 0x9c, 0x9c, 0xd4, 0x1c, 0x52, 0xc4, 0x3d, 0x8d, 0x72, 0xf6, 0x7c, 0xd3, 0x58, 0x79, 0x0d, 0x78, 0xd7, 0x7c, 0x29, 0x2b, 0xc3, 0x96, 0x1d, 0xc7, + 0x96, 0x50, 0x42, 0xd7, 0xda, 0xeb, 0x29, 0x8e, 0x2a, 0x72, 0x23, 0x57, 0x0f, 0x6f, 0x37, 0x35, 0xb2, 0x42, 0x76, 0x78, 0xbf, 0xbf, 0x8c, 0x3f, 0x31, 0xa2, + 0x51, 0xec, 0x9e, 0x0d, 0xfd, 0xf2, 0xaf, 0x71, 0xa0, 0x4f, 0xa9, 0xf6, 0x19, 0xcf, 0x3e, 0x4b, 0xc8, 0xaa, 0x38, 0x06, 0xa1, 0x15, 0xde, 0xde, 0xef, 0x9b, + 0x25, 0xa3, 0xcc, 0x47, 0xca, 0x29, 0x30, 0x65, 0x5f, 0xc1, 0x8b, 0x12, 0x63, 0x79, 0xcd, 0x57, 0x4d, 0x99, 0xc0, 0xcd, 0xbe, 0x62, 0xcb, 0xc3, 0xf2, 0x6b, + 0x0b, 0x40, 0xc5, 0xee, 0x79, 0x0a, 0xa4, 0x75, 0x56, 0xe7, 0xe7, 0xf2, 0xfd, 0xe0, 0x72, 0x78, 0x04, 0xa2, 0x50, 0x31, 0x09, 0x8b, 0x57, 0xc3, 0x85, 0x4e, + 0xc4, 0xae, 0xde, 0x8a, 0xfa, 0xf6, 0x31, 0x06, 0xd2, 0x07, 0x25, 0x40, 0xce, 0x0d, 0xfd, 0x26, 0x98, 0x41, 0xa3, 0xa9, 0xa2, 0x8d, 0x8b, 0x7f, 0x6d, 0x63, + 0x87, 0x7e, 0x75, 0x2f, 0x78, 0xc9, 0xd5, 0x04, 0xb2, 0x4f, 0xc9, 0x94, 0xa7, 0x7f, 0xbc, 0x75, 0x7b, 0xb6, 0xfb, 0x2c, 0x46, 0xf6, 0xde, 0x36, 0x31, 0x2a, + 0x32, 0x1d, 0x7f, 0x30, 0x9e, 0x4a, 0x84, 0x69, 0x66, 0xac, 0xef, 0xbe, 0xb3, 0x83, 0x8c, 0xb8, 0x30, 0xd2, 0x3f, 0xcf, 0xb5, 0xbb, 0x65, 0xaa, 0xe7, 0x6b, + 0x74, 0x48, 0x2c, 0xb2, 0x72, 0x2b, 0x78, 0xaf, 0xd0, 0x71, 0x04, 0xa9, 0xb4, 0x65, 0xd9, 0xfc, 0x74, 0x23, 0xff, 0x89, 0xc1, 0x16, 0x23, 0xac, 0x59, 0x16, + 0x89, 0x41, 0xc3, 0xdb, 0xdb, 0x5b, 0x9a, 0x3d, 0x08, 0xc4, 0x12, 0x28, 0xf8, 0x10, 0xa5, 0xad, 0xc6, 0x81, 0xc0, 0x61, 0x48, 0xba, 0x9d, 0xef, 0xc7, 0xf8, + 0xad, 0x9a, 0xbd, 0x87, 0xfa, 0x7f, 0xa2, 0x4e, 0x4d, 0xe0, 0x19, 0xd5, 0x47, 0xc7, 0xd0, 0xfb, 0x00, 0x7b, 0xbf, 0x17, 0x80, 0xfe, 0xf5, 0x27, 0xec, 0x94, + 0x44, 0x3d, 0x4a, 0x34, 0x49, 0x60, 0xb4, 0x8d, 0x71, 0x6d, 0x9c, 0xf4, 0x4c, 0x33, 0xa9, 0x49, 0x58, 0x58, 0x6f, 0xe1, 0xd1, 0x7d, 0x36, 0x51, 0xf4, 0xd8, + 0x0d, 0x0b, 0xfc, 0xeb, 0xae, 0x58, 0x06, 0x08, 0xbf, 0x67, 0x07, 0x28, 0x7e, 0x68, 0x65, 0x79, 0x86, 0xfb, 0x43, 0x0f, 0x0a, 0xef, 0xd0, 0x97, 0x33, 0x10, + 0x7a, 0x20, 0xe8, 0x22, 0xe5, 0xdc, 0x0c, 0xa2, 0xa5, 0x50, 0x1b, 0x08, 0x15, 0xc2, 0xec, 0xd2, 0x06, 0x25, 0xd0, 0x3b, 0xfd, 0xe3, 0xa2, 0x6f, 0x41, 0x15, + 0x6d, 0x9f, 0x5f, 0xc4, 0x07, 0x5c, 0x99, 0x63, 0xd9, 0xd7, 0xdc, 0x90, 0xc9, 0x8f, 0x3a, 0x4b, 0x6a, 0x84, 0xe8, 0x3c, 0xc7, 0x71, 0x50, 0x71, 0x86, 0x71, + 0x7d, 0x54, 0x84, 0x7b, 0xb7, 0xca, 0xd5, 0x42, 0xaf, 0x88, 0xa5, 0xae, 0xa4, 0x9c, 0xfd, 0x71, 0x71, 0x0f, 0x67, 0xaa, 0x1b, 0x61, 0xd7, 0xf4, 0x50, 0x21, + 0x9d, 0x80, 0x6e, 0x54, 0xcd, 0xb6, 0xb9, 0x02, 0x3e, 0x59, 0x50, 0xff, 0xf2, 0xda, 0x21, 0x5c, 0x50, 0x6d, 0x64, 0x8c, 0x33, 0x75, 0x2a, 0xa4, 0x56, 0xb3, + 0xa8, 0xdb, 0xba, 0xbe, 0x52, 0xd4, 0xe5, 0x29, 0x68, 0xe2, 0x6b, 0x94, 0x6b, 0xb3, 0x90, 0x63, 0x91, 0x1a, 0x95, 0xb5, 0xd7, 0x10, 0x1b, 0xd9, 0x93, 0x4f, + 0x33, 0xb6, 0x6a, 0x4e, 0xcd, 0x40, 0x9d, 0x47, 0x76, 0x3e, 0x4b, 0xc7, 0x2f, 0x16, 0x96, 0x64, 0x9d, 0x4e, 0x8c, 0xfb, 0x0f, 0xd2, 0xec, 0x6c, 0xba, 0xf2, + 0x9c, 0xca, 0xd2, 0x3e, 0x64, 0x37, 0x32, 0x20, 0xd7, 0x4c, 0xb0, 0xe7, 0xd3, 0x75, 0x51, 0x3a, 0x94, 0xc1, 0xdf, 0x1c, 0xb3, 0x10, 0xd5, 0x1e, 0xcf, 0x7c, + 0xb7, 0xab, 0x4a, 0x93, 0xf0, 0x78, 0x58, 0x28, 0x63, 0x10, 0xee, 0xb0, 0xd6, 0x14, 0x81, 0x47, 0xeb, 0x2e, 0xc8, 0x6e, 0x33, 0x7e, 0xf3, 0x2d, 0xc8, 0xdb, + 0x29, 0x0c, 0x80, 0xe4, 0x2f, 0x10, 0x07, 0x8e, 0x08, 0x86, 0x97, 0x1b, 0x39, 0x98, 0x39, 0x06, 0xb3, 0x85, 0x53, 0xb7, 0xbb, 0x65, 0x65, 0x85, 0x0e, 0x0a, + 0x7d, 0x29, 0x3d, 0x3f, 0x52, 0xc2, 0x7b, 0x2b, 0x30, 0x94, 0x99, 0x6a, 0x4b, 0xad, 0xe9, 0xec, 0xcb, 0xcd, 0xae, 0x97, 0x45, 0x54, 0xd5, 0x00, 0x5e, 0xd8, + 0xac, 0xeb, 0x99, 0xdc, 0x58, 0x0b, 0x01, 0xeb, 0x32, 0x22, 0xc4, 0xec, 0x4f, 0xd2, 0x15, 0x03, 0x30, 0x88, 0xc7, 0x28, 0xaf, 0x78, 0xf5, 0x38, 0x84, 0x3b, + 0x3b, 0xe9, 0x29, 0x71, 0x50, 0xa3, 0x07, 0x49, 0x3b, 0xc6, 0x97, 0xc6, 0xf9, 0x53, 0x95, 0x51, 0x65, 0x7e, 0xd7, 0xd4, 0xe8, 0x76, 0x6a, 0x6d, 0x37, 0x6b, + 0xa5, 0x59, 0xaa, 0x14, 0x18, 0x8c, 0x8d, 0x65, 0x78, 0x67, 0xfb, 0x60, 0x56, 0xab, 0x04, 0xa0, 0xc2, 0x93, 0x46, 0xf1, 0x2b, 0x0d, 0x3b, 0x38, 0x62, 0x62, + 0x5e, 0xc8, 0x30, 0xf9, 0x45, 0x28, 0x6f, 0xa1, 0xb1, 0x88, 0xf1, 0x2b, 0x3b, 0xf8, 0xae, 0x91, 0x52, 0xc3, 0x72, 0x86, 0xe4, 0xec, 0xc3, 0x54, 0x86, 0xbf, + 0x8f, 0x33, 0xb1, 0x0f, 0x42, 0xc5, 0x9c, 0xb8, 0xc2, 0x67, 0x8b, 0xac, 0x78, 0xd7, 0x63, 0xab, 0x05, 0xc6, 0x6c, 0x37, 0xa1, 0x28, 0xef, 0x95, 0xc9, 0xf5, + 0x12, 0x38, 0x54, 0x34, 0x2e, 0x03, 0x6a, 0xaa, 0xa9, 0x97, 0x72, 0x22, 0x9f, 0x20, 0xec, 0x9e, 0x29, 0x09, 0xd8, 0x38, 0xd1, 0x86, 0x82, 0x99, 0xbd, 0x2a, + 0x03, 0xe9, 0x3d, 0xbd, 0xea, 0xc5, 0x8b, 0xb0, 0x4c, 0x8b, 0x7e, 0x78, 0x08, 0xef, 0x39, 0xa8, 0xb4, 0x47, 0xce, 0x44, 0xc3, 0x3f, 0x52, 0xe4, 0xbd, 0x9e, + 0xf6, 0xed, 0x6f, 0x6c, 0x05, 0x19, 0xa6, 0x0a, 0x1e, 0x48, 0xe3, 0x9b, 0x91, 0x61, 0xef, 0xf5, 0x91, 0x39, 0x70, 0x44, 0x1c, 0x08, 0x2e, 0x2c, 0x6c, 0x27, + 0xb9, 0x0e, 0xcc, 0x74, 0x69, 0xa5, 0xf8, 0x19, 0xd6, 0xbf, 0x57, 0x6c, 0x9a, 0x91, 0x74, 0xfd, 0xc2, 0x31, 0x32, 0x12, 0x06, 0xa3, 0x69, 0x71, 0xda, 0x40, + 0xa1, 0xf3, 0xb5, 0x9a, 0x43, 0xcc, 0xb4, 0x3c, 0x16, 0x40, 0x65, 0x2b, 0x02, 0xac, 0x5c, 0xae, 0xd6, 0x34, 0x34, 0xe3, 0x69, 0x76, 0x2c, 0xa8, 0xdd, 0x04, + 0x92, 0xa6, 0x7a, 0xc0, 0x87, 0x70, 0x8b, 0x85, 0xba, 0x5d, 0xbb, 0x62, 0x70, 0xcc, 0x1f, 0x21, 0x2c, 0x7e, 0xc3, 0x77, 0xcf, 0x23, 0x22, 0xf4, 0x16, 0x8e, + 0xf1, 0x3d, 0xdc, 0x33, 0x99, 0x5e, 0xaa, 0xa2, 0x50, 0x68, 0xde, 0x03, 0x44, 0xbb, 0xc7, 0x16, 0x2a, 0xf2, 0x08, 0xeb, 0x3d, 0x12, 0x6d, 0xcb, 0x2a, 0xaf, + 0xb4, 0x79, 0xdb, 0x74, 0x5e, 0x54, 0x89, 0x73, 0x0c, 0x48, 0x9c, 0x03, 0x33, 0xd2, 0x92, 0x22, 0xdb, 0x3a, 0xa0, 0x8c, 0xe2, 0x30, 0x6f, 0x39, 0xe4, 0xa9, + 0x24, 0x04, 0xbb, 0x85, 0x7d, 0x62, 0xc5, 0xa9, 0x98, 0x92, 0xef, 0xc6, 0xc8, 0xd1, 0x81, 0xad, 0x95, 0x40, 0x27, 0x09, 0xc7, 0x43, 0xcd, 0xb6, 0x94, 0xfc, + 0x1c, 0x7d, 0x1c, 0xd3, 0x47, 0xfe, 0x62, 0x9c, 0xfa, 0xeb, 0xfc, 0x02, 0x2e, 0x48, 0x62, 0xcf, 0x63, 0xdb, 0x63, 0xd9, 0x21, 0x86, 0xe8, 0x96, 0x54, 0xeb, + 0x6a, 0xa8, 0x78, 0x3c, 0x5b, 0xb6, 0xde, 0xa9, 0x04, 0x48, 0x63, 0xb2, 0x10, 0x02, 0x6a, 0x7f, 0x6d, 0xc8, 0x04, 0xdd, 0x99, 0x25, 0x08, 0xff, 0x80, 0x11, + 0x53, 0xfb, 0x7a, 0x07, 0x39, 0xd9, 0x97, 0xca, 0xf0, 0xa7, 0x46, 0x9c, 0xc2, 0xae, 0x2e, 0x05, 0x62, 0xa0, 0xd5, 0x5d, 0x17, 0x0e, 0x5c, 0x7e, 0x9a, 0xb2, + 0xb7, 0x9d, 0xd4, 0x4f, 0xe3, 0xac, 0x64, 0xdb, 0x6f, 0x1d, 0xdf, 0xd8, 0x41, 0xd7, 0xd9, 0x50, 0x55, 0x30, 0xeb, 0x4b, 0x19, 0xce, 0x78, 0x1f, 0xa8, 0x1e, + 0x87, 0x9c, 0x8f, 0x93, 0x97, 0xd4, 0xa2, 0x28, 0x2c, 0x79, 0x22, 0xc8}; + +TEST(RAW_PACKET, ETH_IP6_UDP_GTP_IP6_TCP_TLS) +{ + struct raw_pkt_parser *handler = raw_packet_parser_create(LAYER_TYPE_ALL, 8); + EXPECT_TRUE(handler != nullptr); + + const void *payload = raw_packet_parser_parse(handler, (const void *)data9, sizeof(data9)); + EXPECT_TRUE(payload != nullptr); + EXPECT_TRUE((char *)payload - (char *)&data9 == 130); + + struct addr_tuple4 inner_addr; + struct addr_tuple4 outer_addr; + char *inner_addr_str = NULL; + char *outer_addr_str = NULL; + + memset(&inner_addr, 0, sizeof(inner_addr)); + memset(&outer_addr, 0, sizeof(outer_addr)); + EXPECT_TRUE(raw_packet_parser_get_most_inner_tuple4(handler, &inner_addr) == 0); + EXPECT_TRUE(raw_packet_parser_get_most_outer_tuple4(handler, &outer_addr) == 0); + inner_addr_str = addr_tuple4_to_str(&inner_addr); + outer_addr_str = addr_tuple4_to_str(&outer_addr); + EXPECT_STREQ(inner_addr_str, "2409:8c34:4400:700:0:4:0:3 443 2409:8934:5082:2100:ecad:e0e4:530a:c269 46582"); + EXPECT_STREQ(outer_addr_str, "2409:8034:4040:5300::105 2152 2409:8034:4025::60:61 2152"); + free(inner_addr_str); + free(outer_addr_str); + + memset(&inner_addr, 0, sizeof(inner_addr)); + memset(&outer_addr, 0, sizeof(outer_addr)); + EXPECT_TRUE(raw_packet_parser_get_most_inner_address(handler, &inner_addr) == 0); + EXPECT_TRUE(raw_packet_parser_get_most_outer_address(handler, &outer_addr) == 0); + inner_addr_str = addr_tuple4_to_str(&inner_addr); + outer_addr_str = addr_tuple4_to_str(&outer_addr); + EXPECT_STREQ(inner_addr_str, "2409:8c34:4400:700:0:4:0:3 0 2409:8934:5082:2100:ecad:e0e4:530a:c269 0"); + EXPECT_STREQ(outer_addr_str, "2409:8034:4040:5300::105 0 2409:8034:4025::60:61 0"); + free(inner_addr_str); + free(outer_addr_str); + + raw_packet_parser_destory(handler); +} + +/****************************************************************************** + * Pcap file: 10-ETH_IP6_UDP_GTP_IP4_TCP_TLS.pcap + * [Protocols in frame: eth:ethertype:ipv6:udp:gtp:ip:tcp:ja3:tls] + ****************************************************************************** + * + * Frame 1: 1470 bytes on wire (11760 bits), 1470 bytes captured (11760 bits) + * Ethernet II, Src: HuaweiTe_62:ee:70 (60:d7:55:62:ee:70), Dst: zte_0e:f5:1c (74:4a:a4:0e:f5:1c) + * Destination: zte_0e:f5:1c (74:4a:a4:0e:f5:1c) + * Source: HuaweiTe_62:ee:70 (60:d7:55:62:ee:70) + * Type: IPv6 (0x86dd) + * Internet Protocol Version 6, Src: 2409:8034:4025::50:a31, Dst: 2409:8034:4040:5301::204 + * 0110 .... = Version: 6 + * .... 0000 0000 .... .... .... .... .... = Traffic Class: 0x00 (DSCP: CS0, ECN: Not-ECT) + * .... 0000 0000 0000 0000 0000 = Flow Label: 0x00000 + * Payload Length: 1416 + * Next Header: UDP (17) + * Hop Limit: 252 + * Source Address: 2409:8034:4025::50:a31 + * Destination Address: 2409:8034:4040:5301::204 + * User Datagram Protocol, Src Port: 2152, Dst Port: 2152 + * Source Port: 2152 + * Destination Port: 2152 + * Length: 1416 + * Checksum: 0xc8df [unverified] + * [Checksum Status: Unverified] + * [Stream index: 0] + * [Timestamps] + * UDP payload (1408 bytes) + * GPRS Tunneling Protocol + * Flags: 0x30 + * Message Type: T-PDU (0xff) + * Length: 1400 + * TEID: 0x6c2a4753 (1814710099) + * Internet Protocol Version 4, Src: 10.49.115.138, Dst: 121.196.250.66 + * 0100 .... = Version: 4 + * .... 0101 = Header Length: 20 bytes (5) + * Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT) + * Total Length: 1400 + * Identification: 0x0003 (3) + * 010. .... = Flags: 0x2, Don't fragment + * ...0 0000 0000 0000 = Fragment Offset: 0 + * Time to Live: 64 + * Protocol: TCP (6) + * Header Checksum: 0x43bb [validation disabled] + * [Header checksum status: Unverified] + * Source Address: 10.49.115.138 + * Destination Address: 121.196.250.66 + * Transmission Control Protocol, Src Port: 50081, Dst Port: 443, Seq: 1, Ack: 1, Len: 1348 + * Source Port: 50081 + * Destination Port: 443 + * [Stream index: 0] + * [Conversation completeness: Incomplete (8)] + * [TCP Segment Len: 1348] + * Sequence Number: 1 (relative sequence number) + * Sequence Number (raw): 1522577104 + * [Next Sequence Number: 1349 (relative sequence number)] + * Acknowledgment Number: 1 (relative ack number) + * Acknowledgment number (raw): 3419365570 + * 1000 .... = Header Length: 32 bytes (8) + * Flags: 0x010 (ACK) + * Window: 2038 + * [Calculated window size: 2038] + * [Window size scaling factor: -1 (unknown)] + * Checksum: 0xd3c2 [unverified] + * [Checksum Status: Unverified] + * Urgent Pointer: 0 + * Options: (12 bytes), No-Operation (NOP), No-Operation (NOP), Timestamps + * [Timestamps] + * [SEQ/ACK analysis] + * TCP payload (1348 bytes) + * Transport Layer Security + */ + +unsigned char data10[] = { + 0x74, 0x4a, 0xa4, 0x0e, 0xf5, 0x1c, 0x60, 0xd7, 0x55, 0x62, 0xee, 0x70, 0x86, 0xdd, 0x60, 0x00, 0x00, 0x00, 0x05, 0x88, 0x11, 0xfc, 0x24, 0x09, 0x80, 0x34, + 0x40, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x0a, 0x31, 0x24, 0x09, 0x80, 0x34, 0x40, 0x40, 0x53, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x04, 0x08, 0x68, 0x08, 0x68, 0x05, 0x88, 0xc8, 0xdf, 0x30, 0xff, 0x05, 0x78, 0x6c, 0x2a, 0x47, 0x53, 0x45, 0x00, 0x05, 0x78, 0x00, 0x03, 0x40, 0x00, + 0x40, 0x06, 0x43, 0xbb, 0x0a, 0x31, 0x73, 0x8a, 0x79, 0xc4, 0xfa, 0x42, 0xc3, 0xa1, 0x01, 0xbb, 0x5a, 0xc0, 0xae, 0xd0, 0xcb, 0xcf, 0x60, 0xc2, 0x80, 0x10, + 0x07, 0xf6, 0xd3, 0xc2, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0x85, 0x14, 0x0e, 0xb0, 0xcc, 0x45, 0xf8, 0x5f, 0xef, 0x49, 0x45, 0xa0, 0xbe, 0x21, 0xd6, 0x46, + 0x9f, 0xb5, 0x17, 0xb2, 0xfe, 0x61, 0x2d, 0xed, 0x4f, 0x0c, 0x1e, 0xb5, 0xda, 0x91, 0x40, 0x87, 0xab, 0x02, 0x0d, 0x01, 0xc8, 0xf1, 0x24, 0x05, 0x8a, 0x9d, + 0x8d, 0xfc, 0xbb, 0x82, 0x24, 0xf5, 0x7d, 0x2d, 0x10, 0x66, 0x30, 0x2a, 0xaa, 0x4a, 0x51, 0x8d, 0xe9, 0x9a, 0x65, 0xcf, 0x89, 0x0c, 0x9e, 0x0d, 0x82, 0xda, + 0x5e, 0xd3, 0x98, 0xe3, 0x23, 0xf7, 0x5a, 0xd4, 0x88, 0x94, 0xd2, 0xdf, 0xbe, 0x44, 0x20, 0x2b, 0x21, 0x2d, 0x38, 0xca, 0x29, 0x5e, 0xa3, 0xb7, 0xbb, 0x34, + 0x20, 0x42, 0x02, 0x71, 0x04, 0xda, 0xd2, 0xeb, 0xb8, 0x81, 0xa3, 0x48, 0xc8, 0x54, 0xad, 0x42, 0x35, 0xc4, 0x4f, 0x6b, 0x15, 0x50, 0x22, 0x3e, 0x26, 0xb3, + 0xfc, 0x30, 0x49, 0x71, 0x6f, 0x41, 0x66, 0xa2, 0x2e, 0xe9, 0xd3, 0x1a, 0x69, 0xa8, 0x87, 0x71, 0x65, 0xa2, 0xc7, 0xc7, 0x2b, 0x25, 0x1d, 0x3f, 0xfb, 0xe6, + 0x05, 0xe1, 0x09, 0xb9, 0x76, 0x1d, 0xb9, 0xf9, 0xaf, 0xb4, 0x79, 0xa1, 0x35, 0x05, 0x59, 0x88, 0xa0, 0x07, 0xb5, 0x2d, 0x02, 0x11, 0x0a, 0x89, 0xf1, 0x67, + 0xdb, 0xe5, 0x5c, 0x5c, 0xaa, 0x0e, 0x21, 0xa6, 0xa4, 0x1a, 0x9f, 0x9e, 0xc8, 0x2a, 0x36, 0x6f, 0xcc, 0xa3, 0x13, 0x78, 0xf1, 0xbe, 0x34, 0xa0, 0x35, 0xef, + 0x1f, 0xf4, 0x79, 0xcb, 0x37, 0x3e, 0x77, 0x14, 0xfb, 0x2e, 0x21, 0x4f, 0x6b, 0xe5, 0xe9, 0x3a, 0x90, 0x76, 0xa8, 0x55, 0x09, 0xb6, 0x68, 0xbf, 0x66, 0xae, + 0xf1, 0x55, 0xc0, 0x76, 0x8f, 0x16, 0x86, 0x49, 0x9a, 0x88, 0x01, 0xdb, 0x78, 0x1f, 0xde, 0xc2, 0x33, 0x92, 0xe3, 0x22, 0xc6, 0x8c, 0x20, 0x17, 0xa0, 0xb2, + 0x79, 0xf4, 0x60, 0x8e, 0x98, 0x53, 0xcd, 0x8f, 0xb2, 0x8f, 0x80, 0xda, 0x9f, 0xf6, 0x00, 0x0c, 0xf8, 0x6b, 0xdf, 0x7d, 0x93, 0x48, 0x5a, 0x23, 0x35, 0x0e, + 0x1b, 0xf7, 0x50, 0x87, 0x93, 0x29, 0xaa, 0xa1, 0xb8, 0x98, 0x9f, 0x89, 0xb2, 0x0a, 0x02, 0x27, 0x95, 0x01, 0x84, 0x5a, 0x09, 0xb8, 0xff, 0x23, 0x02, 0x89, + 0xef, 0x1b, 0x64, 0xb2, 0x38, 0x81, 0xc4, 0x36, 0xe3, 0xda, 0xb5, 0x3b, 0x80, 0x45, 0x52, 0x96, 0xab, 0x0e, 0xdb, 0xb6, 0x9c, 0xcb, 0xc4, 0xe5, 0xb9, 0x72, + 0x67, 0x57, 0x4b, 0xb9, 0x55, 0xcb, 0x6b, 0xc4, 0xec, 0x46, 0x4d, 0xa3, 0xe0, 0xda, 0xba, 0x70, 0x3d, 0xa6, 0xa7, 0x3f, 0x58, 0xd2, 0x9f, 0xb0, 0x11, 0x66, + 0xaf, 0x73, 0x09, 0x60, 0x6e, 0xe0, 0x71, 0xa5, 0x65, 0x41, 0x28, 0x3e, 0x70, 0x1d, 0x25, 0x77, 0x6a, 0x4e, 0xed, 0xb9, 0x27, 0x6c, 0xf0, 0xba, 0x54, 0x8d, + 0x77, 0xfb, 0xb6, 0x4e, 0xe2, 0xab, 0x8f, 0xe3, 0xd4, 0x02, 0x65, 0x0a, 0x49, 0xf3, 0xf9, 0xc7, 0x09, 0x76, 0x81, 0xf4, 0xf8, 0x3e, 0x1f, 0x74, 0x30, 0xaf, + 0x3b, 0x9e, 0x97, 0x00, 0xde, 0xd8, 0x9a, 0xaf, 0xcc, 0x72, 0xeb, 0x0a, 0xe7, 0xab, 0xc1, 0x53, 0x62, 0x3f, 0x08, 0xba, 0x43, 0x06, 0x13, 0x0a, 0x3b, 0x5c, + 0xb4, 0xe0, 0xc8, 0xa6, 0x41, 0x45, 0xaa, 0x1a, 0xc9, 0x88, 0x86, 0x31, 0x25, 0x02, 0x4a, 0x76, 0x66, 0xb6, 0x6d, 0xff, 0x50, 0x1d, 0x3c, 0xf3, 0x2d, 0xfe, + 0x7b, 0xb2, 0x75, 0x5d, 0x9a, 0x9a, 0xe5, 0x39, 0x31, 0x4f, 0x7b, 0xa5, 0x6f, 0x94, 0xed, 0x31, 0xd4, 0x61, 0xc7, 0x44, 0x1d, 0x37, 0x19, 0x76, 0x04, 0x0e, + 0xbd, 0xc4, 0x9e, 0xe3, 0xdf, 0x94, 0x49, 0x32, 0x65, 0xd0, 0x37, 0x64, 0xb5, 0x2a, 0x61, 0x2d, 0x05, 0xc5, 0xe5, 0x79, 0x3e, 0xcf, 0x5f, 0x77, 0x0a, 0x7c, + 0x29, 0x34, 0x1a, 0x45, 0x7e, 0x11, 0x68, 0xb4, 0x3a, 0xf6, 0x5b, 0x23, 0xe4, 0x32, 0xa4, 0x11, 0x1a, 0xba, 0xd6, 0x4a, 0x45, 0x42, 0x29, 0xac, 0xb0, 0x17, + 0x05, 0x1b, 0xee, 0xf6, 0x52, 0x6d, 0x8b, 0xb4, 0x3b, 0x63, 0xe2, 0xca, 0xbf, 0x7e, 0xd3, 0xf7, 0x96, 0x75, 0x67, 0x9d, 0x27, 0x15, 0x39, 0xde, 0x5f, 0x66, + 0x74, 0x7c, 0x46, 0x01, 0x48, 0xf7, 0x99, 0x33, 0x7d, 0xc6, 0x81, 0xc4, 0x82, 0x09, 0x00, 0x20, 0x3f, 0x5c, 0xe4, 0x51, 0x88, 0x5b, 0xac, 0x31, 0x17, 0x04, + 0xa4, 0xac, 0xbf, 0x3d, 0xff, 0xad, 0x51, 0x07, 0x0b, 0xc7, 0x26, 0xa7, 0x9f, 0x83, 0x17, 0xd8, 0x2f, 0x6a, 0x47, 0x96, 0x14, 0x47, 0x68, 0xd4, 0xc0, 0xc0, + 0x3b, 0x87, 0x51, 0x30, 0xe9, 0xfa, 0x21, 0x46, 0x80, 0x1a, 0x5a, 0xef, 0x78, 0xd0, 0x3a, 0xac, 0x73, 0x1e, 0x39, 0xba, 0x82, 0x43, 0x5d, 0xef, 0x15, 0x2c, + 0x9a, 0xe5, 0xeb, 0x6a, 0xe7, 0x24, 0x12, 0xe6, 0x2a, 0xd2, 0x09, 0xc2, 0x85, 0x69, 0x9d, 0x73, 0x16, 0xb0, 0xad, 0x51, 0xf8, 0x3d, 0x94, 0x6b, 0xb7, 0xb3, + 0x7f, 0xb4, 0x9e, 0xc1, 0xdc, 0x31, 0x27, 0xa1, 0x2d, 0xfe, 0x30, 0x15, 0x04, 0x20, 0x82, 0xdc, 0xbd, 0x8b, 0xc5, 0xb4, 0xcf, 0x91, 0x85, 0xae, 0x21, 0x5e, + 0x00, 0x10, 0x04, 0x62, 0x8a, 0xe2, 0x66, 0x74, 0xf8, 0x8d, 0x8b, 0x52, 0x17, 0xd9, 0x1a, 0xbd, 0x06, 0x2d, 0x07, 0x6a, 0xf5, 0x8b, 0xdf, 0x85, 0x2e, 0x36, + 0xec, 0x15, 0x6f, 0x7e, 0xd2, 0x04, 0x43, 0x6a, 0xd7, 0x60, 0xf5, 0x53, 0x0d, 0x2e, 0x2d, 0xf5, 0x52, 0x4c, 0xcc, 0xe5, 0xf4, 0x47, 0xdd, 0x34, 0xda, 0xc1, + 0xfc, 0x60, 0x00, 0xaa, 0x68, 0x01, 0x5c, 0x82, 0x4b, 0xf9, 0x57, 0x54, 0x9d, 0xd5, 0x8b, 0xb6, 0x42, 0x77, 0xd4, 0x47, 0x70, 0x23, 0x4c, 0xad, 0xc5, 0x00, + 0x73, 0x9b, 0xbb, 0x65, 0xa7, 0x46, 0x74, 0xcd, 0x2e, 0x61, 0x0f, 0xac, 0xeb, 0x53, 0x5a, 0x87, 0x70, 0xfc, 0x5d, 0x2e, 0xa1, 0xe3, 0x9a, 0x87, 0x01, 0x0f, + 0x2e, 0xef, 0x10, 0xe2, 0x82, 0xd8, 0x12, 0xe7, 0xb8, 0x94, 0xa4, 0xdd, 0x5f, 0xea, 0x21, 0x63, 0x26, 0x43, 0xec, 0xc3, 0x54, 0x76, 0xb1, 0xb2, 0x1c, 0x03, + 0x4c, 0x5c, 0x22, 0xb5, 0x00, 0x7d, 0x77, 0x3a, 0xb6, 0xbf, 0x50, 0xbd, 0xfd, 0x0a, 0x31, 0x2c, 0xdc, 0xab, 0xe2, 0xc0, 0x0b, 0xb6, 0x66, 0xad, 0x9c, 0xca, + 0x94, 0xed, 0xd8, 0x77, 0x1b, 0xf1, 0x94, 0xdd, 0x65, 0x61, 0xda, 0x7b, 0x04, 0x3c, 0x93, 0xcf, 0x96, 0x74, 0x35, 0x8e, 0x41, 0xe1, 0xa4, 0xbc, 0xf2, 0x4f, + 0xe9, 0xb8, 0x16, 0x55, 0x05, 0x5a, 0xac, 0x10, 0xd3, 0xdf, 0xea, 0x6a, 0xf8, 0xe0, 0xf3, 0xdf, 0x66, 0x00, 0xab, 0x3d, 0xb9, 0x44, 0x65, 0x34, 0x49, 0x89, + 0xf2, 0x1d, 0x09, 0xc9, 0xfc, 0xa5, 0x84, 0xa1, 0x03, 0x5b, 0x7a, 0x5c, 0x7e, 0x21, 0xe9, 0xb4, 0x3a, 0x4c, 0x2b, 0x94, 0x64, 0x1d, 0x9b, 0xa5, 0xbf, 0x7e, + 0x1c, 0x97, 0x7e, 0x3d, 0xbe, 0x84, 0xfc, 0xab, 0x6d, 0x2a, 0x50, 0x23, 0x9e, 0x11, 0x3f, 0xe2, 0xa0, 0x68, 0xe7, 0xd5, 0xba, 0x5e, 0x24, 0x8c, 0x4c, 0x46, + 0xe6, 0x5b, 0x10, 0xc3, 0x82, 0x32, 0x17, 0x32, 0xdc, 0xec, 0xaa, 0x1e, 0x73, 0xe5, 0x7d, 0xb8, 0x1c, 0x6c, 0x4c, 0x9f, 0x60, 0x7b, 0x66, 0x4c, 0x90, 0x69, + 0xc4, 0x23, 0x66, 0x67, 0xce, 0x6d, 0x24, 0x1d, 0xcc, 0x8e, 0x78, 0xa1, 0xa7, 0xde, 0x87, 0x81, 0xac, 0x62, 0x54, 0xbc, 0x47, 0x82, 0x3c, 0xad, 0x92, 0x29, + 0xd9, 0xc0, 0xed, 0x0c, 0x11, 0x0e, 0xc5, 0x75, 0xa4, 0xbd, 0xbf, 0xcb, 0x3a, 0xaf, 0x2b, 0x9f, 0xbe, 0xbb, 0xbc, 0x31, 0x07, 0xa7, 0xbe, 0x6c, 0xa9, 0x4e, + 0xff, 0x35, 0x80, 0x2f, 0x09, 0x77, 0xe0, 0xc0, 0xdc, 0x9c, 0xc6, 0xa6, 0x63, 0xab, 0x47, 0x74, 0x5f, 0x5c, 0xae, 0x75, 0xbf, 0x42, 0x67, 0x55, 0x89, 0xcf, + 0xd3, 0x65, 0x8d, 0x5b, 0x6f, 0x5c, 0xf9, 0xd1, 0x78, 0xa2, 0xfd, 0x4f, 0x54, 0x6a, 0x71, 0x0c, 0x58, 0x13, 0xb0, 0x48, 0x0a, 0x7b, 0xcc, 0x84, 0x61, 0xa7, + 0x7d, 0x39, 0xa2, 0xd1, 0xc0, 0xdb, 0x8e, 0x97, 0x20, 0x86, 0x97, 0x20, 0xda, 0xca, 0x56, 0x78, 0x61, 0xc2, 0x2f, 0x36, 0xdb, 0x95, 0xae, 0x7e, 0x8d, 0x97, + 0xcb, 0x45, 0x6a, 0x6d, 0x27, 0xaa, 0xab, 0x4e, 0x88, 0x23, 0xb6, 0x6a, 0x8a, 0xca, 0x71, 0xca, 0x39, 0xa2, 0x98, 0x0d, 0x53, 0xa9, 0x38, 0xd5, 0x9c, 0x5d, + 0x0e, 0x5e, 0xc9, 0xeb, 0x21, 0xab, 0x00, 0xca, 0xff, 0x92, 0x20, 0x9d, 0x65, 0x9d, 0x8d, 0x49, 0x46, 0xbe, 0x51, 0x97, 0xc1, 0x61, 0x02, 0x9e, 0xa8, 0xb9, + 0x2c, 0x27, 0x7d, 0x73, 0xf9, 0x12, 0x16, 0x45, 0x25, 0xbb, 0xb0, 0x51, 0x14, 0x18, 0x07, 0xab, 0xc7, 0x06, 0xc0, 0xe9, 0x1c, 0xf8, 0x6d, 0xe1, 0x80, 0x21, + 0x21, 0x68, 0x24, 0xf7, 0x28, 0xb9, 0x07, 0xd4, 0xd7, 0xdf, 0x3e, 0xff, 0xbc, 0xe3, 0xbc, 0x6e, 0x42, 0x76, 0x63, 0xbc, 0x82, 0x0a, 0xf5, 0x99, 0x65, 0x17, + 0xd2, 0x38, 0xa9, 0xa8, 0x31, 0xce, 0x1f, 0xf7, 0xef, 0x8d, 0x94, 0xae, 0x99, 0x50, 0x30, 0x12, 0xbd, 0x4b, 0x65, 0x56, 0x59, 0xfb, 0x33, 0x7b, 0x99, 0xc7, + 0xe5, 0x80, 0xe6, 0x92, 0x0e, 0x44, 0x1d, 0x17, 0xc2, 0xd0, 0x78, 0x76, 0x9d, 0x5b, 0x7d, 0x3c, 0xb4, 0xf8, 0xcb, 0x2f, 0x83, 0x23, 0x35, 0x49, 0xc0, 0x78, + 0x2d, 0x44, 0x05, 0x64, 0x0f, 0xaa, 0x84, 0x9d, 0x3f, 0xac, 0xef, 0x5b, 0x46, 0x44, 0xb8, 0x15, 0xbe, 0x4f, 0xe7, 0x25, 0xb7, 0xa0, 0xc8, 0x0f, 0x70, 0x1a, + 0xca, 0x7f, 0xce, 0x79, 0x7b, 0xf5, 0x7e, 0x21, 0x35, 0xc7, 0x0e, 0x99, 0xdc, 0x76, 0xe0, 0x36, 0x09, 0x6e, 0x6d, 0x5f, 0x98, 0x5e, 0xb8, 0xa4, 0x88, 0xea, + 0x0b, 0x4b, 0x21, 0xa2, 0x52, 0x86, 0x95, 0x4e, 0x18, 0xac, 0xa2, 0xaf, 0x29, 0x5b, 0xe7, 0x05, 0xa1, 0xc8, 0xe1, 0x80, 0xfa, 0xb6, 0x5a, 0xed, 0x94, 0x32, + 0x4f, 0xe9, 0xf5, 0xf0, 0x61, 0x5d, 0x7f, 0xc4, 0xc4, 0xd1, 0x05, 0x54, 0x13, 0xdb}; + +TEST(RAW_PACKET, ETH_IP6_UDP_GTP_IP4_TCP_TLS) +{ + struct raw_pkt_parser *handler = raw_packet_parser_create(LAYER_TYPE_ALL, 8); + EXPECT_TRUE(handler != nullptr); + + const void *payload = raw_packet_parser_parse(handler, (const void *)data10, sizeof(data10)); + EXPECT_TRUE(payload != nullptr); + EXPECT_TRUE((char *)payload - (char *)&data10 == 122); + + struct addr_tuple4 inner_addr; + struct addr_tuple4 outer_addr; + char *inner_addr_str = NULL; + char *outer_addr_str = NULL; + + memset(&inner_addr, 0, sizeof(inner_addr)); + memset(&outer_addr, 0, sizeof(outer_addr)); + EXPECT_TRUE(raw_packet_parser_get_most_inner_tuple4(handler, &inner_addr) == 0); + EXPECT_TRUE(raw_packet_parser_get_most_outer_tuple4(handler, &outer_addr) == 0); + inner_addr_str = addr_tuple4_to_str(&inner_addr); + outer_addr_str = addr_tuple4_to_str(&outer_addr); + EXPECT_STREQ(inner_addr_str, "10.49.115.138 50081 121.196.250.66 443"); + EXPECT_STREQ(outer_addr_str, "2409:8034:4025::50:a31 2152 2409:8034:4040:5301::204 2152"); + free(inner_addr_str); + free(outer_addr_str); + + memset(&inner_addr, 0, sizeof(inner_addr)); + memset(&outer_addr, 0, sizeof(outer_addr)); + EXPECT_TRUE(raw_packet_parser_get_most_inner_address(handler, &inner_addr) == 0); + EXPECT_TRUE(raw_packet_parser_get_most_outer_address(handler, &outer_addr) == 0); + inner_addr_str = addr_tuple4_to_str(&inner_addr); + outer_addr_str = addr_tuple4_to_str(&outer_addr); + EXPECT_STREQ(inner_addr_str, "10.49.115.138 0 121.196.250.66 0"); + EXPECT_STREQ(outer_addr_str, "2409:8034:4025::50:a31 0 2409:8034:4040:5301::204 0"); + free(inner_addr_str); + free(outer_addr_str); + + raw_packet_parser_destory(handler); +} + +/****************************************************************************** + * Pcap file: 11-ETH_IP4_UDP_VXLAN_ETH_IP4_UDP_DNS.pcap + * [Protocols in frame: eth:ethertype:ip:udp:vxlan:eth:ethertype:ip:udp:dns] + ****************************************************************************** + * + * Frame 1: 124 bytes on wire (992 bits), 124 bytes captured (992 bits) + * Ethernet II, Src: zte_6c:fa:43 (00:1e:73:6c:fa:43), Dst: Shanghai_0d:0a (e4:95:6e:20:0d:0a) + * Destination: Shanghai_0d:0a (e4:95:6e:20:0d:0a) + * Source: zte_6c:fa:43 (00:1e:73:6c:fa:43) + * Type: IPv4 (0x0800) + * Internet Protocol Version 4, Src: 10.1.1.1, Dst: 192.168.1.10 + * 0100 .... = Version: 4 + * .... 0101 = Header Length: 20 bytes (5) + * Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT) + * Total Length: 110 + * Identification: 0x0000 (0) + * 000. .... = Flags: 0x0 + * ...0 0000 0000 0000 = Fragment Offset: 0 + * Time to Live: 254 + * Protocol: UDP (17) + * Header Checksum: 0xefca [validation disabled] + * [Header checksum status: Unverified] + * Source Address: 10.1.1.1 + * Destination Address: 192.168.1.10 + * User Datagram Protocol, Src Port: 50709, Dst Port: 4789 + * Source Port: 50709 + * Destination Port: 4789 + * Length: 90 + * Checksum: 0x0000 [zero-value ignored] + * [Stream index: 0] + * [Timestamps] + * UDP payload (82 bytes) + * Virtual eXtensible Local Area Network + * Flags: 0x0800, VXLAN Network ID (VNI) + * Group Policy ID: 0 + * VXLAN Network Identifier (VNI): 458755 + * Reserved: 0 + * Ethernet II, Src: WistronI_18:18:41 (3c:97:0e:18:18:41), Dst: DawningI_13:70:7a (e8:61:1f:13:70:7a) + * Destination: DawningI_13:70:7a (e8:61:1f:13:70:7a) + * Source: WistronI_18:18:41 (3c:97:0e:18:18:41) + * Type: IPv4 (0x0800) + * Internet Protocol Version 4, Src: 192.168.11.193, Dst: 114.114.114.114 + * 0100 .... = Version: 4 + * .... 0101 = Header Length: 20 bytes (5) + * Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT) + * Total Length: 60 + * Identification: 0x0cb6 (3254) + * 000. .... = Flags: 0x0 + * ...0 0000 0000 0000 = Fragment Offset: 0 + * Time to Live: 64 + * Protocol: UDP (17) + * Header Checksum: 0xbcad [validation disabled] + * [Header checksum status: Unverified] + * Source Address: 192.168.11.193 + * Destination Address: 114.114.114.114 + * User Datagram Protocol, Src Port: 65290, Dst Port: 53 + * Source Port: 65290 + * Destination Port: 53 + * Length: 40 + * Checksum: 0x39e4 [unverified] + * [Checksum Status: Unverified] + * [Stream index: 1] + * [Timestamps] + * UDP payload (32 bytes) + * Domain Name System (query) + */ + +unsigned char data11[] = { + 0xe4, 0x95, 0x6e, 0x20, 0x0d, 0x0a, 0x00, 0x1e, 0x73, 0x6c, 0xfa, 0x43, 0x08, 0x00, 0x45, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x11, 0xef, 0xca, + 0x0a, 0x01, 0x01, 0x01, 0xc0, 0xa8, 0x01, 0x0a, 0xc6, 0x15, 0x12, 0xb5, 0x00, 0x5a, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x03, 0x00, 0xe8, 0x61, + 0x1f, 0x13, 0x70, 0x7a, 0x3c, 0x97, 0x0e, 0x18, 0x18, 0x41, 0x08, 0x00, 0x45, 0x00, 0x00, 0x3c, 0x0c, 0xb6, 0x00, 0x00, 0x40, 0x11, 0xbc, 0xad, 0xc0, 0xa8, + 0x0b, 0xc1, 0x72, 0x72, 0x72, 0x72, 0xff, 0x0a, 0x00, 0x35, 0x00, 0x28, 0x39, 0xe4, 0x86, 0x84, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x03, 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01}; + +TEST(RAW_PACKET, ETH_IP4_UDP_VXLAN_ETH_IP4_UDP_DNS) +{ + struct raw_pkt_parser *handler = raw_packet_parser_create(LAYER_TYPE_ALL, 8); + EXPECT_TRUE(handler != nullptr); + + const void *payload = raw_packet_parser_parse(handler, (const void *)data11, sizeof(data11)); + EXPECT_TRUE(payload != nullptr); + EXPECT_TRUE((char *)payload - (char *)&data11 == 92); + + struct addr_tuple4 inner_addr; + struct addr_tuple4 outer_addr; + char *inner_addr_str = NULL; + char *outer_addr_str = NULL; + + memset(&inner_addr, 0, sizeof(inner_addr)); + memset(&outer_addr, 0, sizeof(outer_addr)); + EXPECT_TRUE(raw_packet_parser_get_most_inner_tuple4(handler, &inner_addr) == 0); + EXPECT_TRUE(raw_packet_parser_get_most_outer_tuple4(handler, &outer_addr) == 0); + inner_addr_str = addr_tuple4_to_str(&inner_addr); + outer_addr_str = addr_tuple4_to_str(&outer_addr); + EXPECT_STREQ(inner_addr_str, "192.168.11.193 65290 114.114.114.114 53"); + EXPECT_STREQ(outer_addr_str, "10.1.1.1 50709 192.168.1.10 4789"); + free(inner_addr_str); + free(outer_addr_str); + + memset(&inner_addr, 0, sizeof(inner_addr)); + memset(&outer_addr, 0, sizeof(outer_addr)); + EXPECT_TRUE(raw_packet_parser_get_most_inner_address(handler, &inner_addr) == 0); + EXPECT_TRUE(raw_packet_parser_get_most_outer_address(handler, &outer_addr) == 0); + inner_addr_str = addr_tuple4_to_str(&inner_addr); + outer_addr_str = addr_tuple4_to_str(&outer_addr); + EXPECT_STREQ(inner_addr_str, "192.168.11.193 0 114.114.114.114 0"); + EXPECT_STREQ(outer_addr_str, "10.1.1.1 0 192.168.1.10 0"); + free(inner_addr_str); + free(outer_addr_str); + + raw_packet_parser_destory(handler); +} + +/****************************************************************************** + * Pcap file: 12-ETH_MPLS_MPLS_PWETHCW_ETH_ARP.pcap + * [Protocols in frame: eth:ethertype:mpls:pwethheuristic:pwethcw:eth:ethertype:arp] + ****************************************************************************** + * + * Frame 1: 90 bytes on wire (720 bits), 90 bytes captured (720 bits) + * Ethernet II, Src: cc:01:0d:5c:00:10 (cc:01:0d:5c:00:10), Dst: cc:00:0d:5c:00:10 (cc:00:0d:5c:00:10) + * Destination: cc:00:0d:5c:00:10 (cc:00:0d:5c:00:10) + * Source: cc:01:0d:5c:00:10 (cc:01:0d:5c:00:10) + * Type: MPLS label switched packet (0x8847) + * MultiProtocol Label Switching Header, Label: 19, Exp: 0, S: 0, TTL: 254 + * 0000 0000 0000 0001 0011 .... .... .... = MPLS Label: 19 (0x00013) + * .... .... .... .... .... 000. .... .... = MPLS Experimental Bits: 0 + * .... .... .... .... .... ...0 .... .... = MPLS Bottom Of Label Stack: 0 + * .... .... .... .... .... .... 1111 1110 = MPLS TTL: 254 + * MultiProtocol Label Switching Header, Label: 16, Exp: 0, S: 1, TTL: 255 + * 0000 0000 0000 0001 0000 .... .... .... = MPLS Label: 16 (0x00010) + * .... .... .... .... .... 000. .... .... = MPLS Experimental Bits: 0 + * .... .... .... .... .... ...1 .... .... = MPLS Bottom Of Label Stack: 1 + * .... .... .... .... .... .... 1111 1111 = MPLS TTL: 255 + * PW Ethernet Control Word + * Sequence Number: 0 + * Ethernet II, Src: Private_66:68:00 (00:50:79:66:68:00), Dst: Broadcast (ff:ff:ff:ff:ff:ff) + * Destination: Broadcast (ff:ff:ff:ff:ff:ff) + * Source: Private_66:68:00 (00:50:79:66:68:00) + * Type: ARP (0x0806) + * Trailer: 00000000000000000000000000000000000000000000 + * Address Resolution Protocol (request) + */ + +unsigned char data12[] = { + 0xcc, 0x00, 0x0d, 0x5c, 0x00, 0x10, 0xcc, 0x01, 0x0d, 0x5c, 0x00, 0x10, 0x88, 0x47, 0x00, 0x01, 0x30, 0xfe, 0x00, 0x01, 0x01, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x50, 0x79, 0x66, 0x68, 0x00, 0x08, 0x06, 0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01, 0x00, 0x50, 0x79, 0x66, + 0x68, 0x00, 0xc0, 0xa8, 0x00, 0x0a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xa8, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +TEST(RAW_PACKET, ETH_MPLS_MPLS_PWETHCW_ETH_ARP) +{ + struct raw_pkt_parser *handler = raw_packet_parser_create(LAYER_TYPE_ALL, 8); + EXPECT_TRUE(handler != nullptr); + + const void *payload = raw_packet_parser_parse(handler, (const void *)data12, sizeof(data12)); + EXPECT_TRUE(payload != nullptr); + EXPECT_TRUE((char *)payload - (char *)&data12 == 40); + + struct addr_tuple4 inner_addr; + struct addr_tuple4 outer_addr; + + EXPECT_TRUE(raw_packet_parser_get_most_inner_tuple4(handler, &inner_addr) == -1); + EXPECT_TRUE(raw_packet_parser_get_most_outer_tuple4(handler, &outer_addr) == -1); + + EXPECT_TRUE(raw_packet_parser_get_most_inner_address(handler, &inner_addr) == -1); + EXPECT_TRUE(raw_packet_parser_get_most_outer_address(handler, &outer_addr) == -1); + + raw_packet_parser_destory(handler); +} + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/common/test/pcap/1-ETH_VLAN_VLAN_IP4_IP4_UDP.pcap b/common/test/pcap/1-ETH_VLAN_VLAN_IP4_IP4_UDP.pcap new file mode 100644 index 0000000000000000000000000000000000000000..769ab66fb01644b2b0d0728243bb22ebe363be30 GIT binary patch literal 954 zcmca|c+)~A1{MYcU}0bca`f}(M$cB@VORxZgD@DdEIH5L*h>k2G<=7 zQ(ig6V^6Y*FEnP0`H9y+Rh?j_T aejb==|FdXKAnfwry5KNR-QPt^mjeJz*ul^M literal 0 HcmV?d00001 diff --git a/common/test/pcap/10-ETH_IP6_UDP_GTP_IP4_TCP_TLS.pcap b/common/test/pcap/10-ETH_IP6_UDP_GTP_IP4_TCP_TLS.pcap new file mode 100644 index 0000000000000000000000000000000000000000..af72b4df9826ca64a6cfb849000bbd446d78d047 GIT binary patch literal 1510 zcmVPpJnq|+444w^w+FsL`<0JQ4)QFVQ-@Zg3Dx4SeTLIQCF z+S2Q|ful&sRINfa#7}D#P$E7iv-~hgac@CpqAuyv8fmD9ab=>%$15crKl|nd;R(5R z9l80hw0WU51zCuo2emB%5ekX%XWQjmT&fNsrlcC5p2#XTZ_J|;c=5h8pf&Fw^m)rS zK6e!RE+J2A<>@+*cBoYewrIa*uJKjCc8?Z@Nt%cO+jt+|!ZVWNBF2m$7of6v^k9yd zQ_YXEkAT{r_5cj{Yu|m7NLnK`4jcDShm$F)p}3fziLwd;CzSz&S_!!SBLa!<8)ULL zfy6fB+O<1?MN*cl4%@bz%f#ima%WddxmC++#Oy{*qu|=Qa6P7{KUmVAun}gja|vK> z;Blp8K`1_O9VK^aPVKoTZ1B2Njd%ODPU5SNbTF?wo|gdL z*qX1*a_b7`tHD!ZKM1-*1``T9T(scGra?uj8p();F(m>@c4oG1|4Pd`1CC z_n9+&#(~6w2>>8JT;x%RTdXk`1f;CLJ^!sy2Mfn0r=Not*e_~FmJ~;5)WEomh z1Vd`qVD(cCE-m#^Ow8rHlw=*7rN4e0mwr9I zg#4>*Do`Vy5kKOfXy?_sUL=f6M&?@(!-6swGTiK{9&_b=xEyRupJ01tOps~BBW7pL zZ6qDcj(DM`-iLv#VpP0Gf;_E~DcQj73=s~+b)>z&%Q~+spT4`iF$broW`bOt4DNST&{J$LT6Qp&(md%TW?(X(RiZ$PgH7g3|JGeND6z*gkh(BIik_P z+m4qYhL<4P%2s$`!Y?-4m9Bn`m&-+JZ6~U$PKYD6YKqEn$~mH#4O6K&)tp@pUdihr zs{qRXk|3RBosCIGzEPLKVFI40xhyArbNLb$MJ2niQ4|;ltH%bw=^XfN;ea6_Xe9S2 zxd+tO-#-7mnT_wS9Au9;9U61_`hR$2Qqdzr`Ofaa18 zL>(8x(0F#8TYWsV_{%SYBQ;6Dcr8Q)WDlx@ojI+LDqEd#HP8h7BuPIyS1)<2{fcmyt?UXW4>Gkko MU4O*H(FIf!+hO0lc>n+a literal 0 HcmV?d00001 diff --git a/common/test/pcap/11-ETH_IP4_UDP_VXLAN_ETH_IP4_UDP_DNS.pcap b/common/test/pcap/11-ETH_IP4_UDP_VXLAN_ETH_IP4_UDP_DNS.pcap new file mode 100644 index 0000000000000000000000000000000000000000..29b89628ec28146d75f1cca27787d128c02fb960 GIT binary patch literal 776 zcmca|c+)~A1{MYcU}0bcaw?M z!(a*wnT}T%+FBYLfc|F#nTRc14)B0N2Po;sz~WUao{7fKLgQzk@zc@xlhOFepm0w^ Q@+(HT!~Kd9?nBbA0AR$hqW}N^ literal 0 HcmV?d00001 diff --git a/common/test/pcap/12-ETH_MPLS_MPLS_PWETHCW_ETH_ARP.pcap b/common/test/pcap/12-ETH_MPLS_MPLS_PWETHCW_ETH_ARP.pcap new file mode 100644 index 0000000000000000000000000000000000000000..50c608508f8776e2ceefe418576539ec9aad620d GIT binary patch literal 1256 zcmca|c+)~A1{MZ5P+(wS1aeLnT=o(bVP%K{vO)L^18)q2z!^pm+2PK>Xz-7Lk?}uJ zg5f_HFa%VlWiW8CF)(s4u(1G*0P_#5VBmtt16d*<)tCTe(@_^TgiT;OAohW5Qh?h8 zF%wKO!fj%L@WJN8?ZmVNByt95)8APv46}f25XS8%h?x-kI2c?R7(&hzI50RczBvi< z5y&nMhM?JJ3K$tUIJvlac=`AR1cih}M8(7OcED&B7Gq(%3(2WQ8e`OQB&JPD7Vo0J_w*8Ofz2gy{uvn7%*?Q@`04 YXcnd*mww&MPF9#AxfB|vVKj6p01qYT3jhEB literal 0 HcmV?d00001 diff --git a/common/test/pcap/2-ETH_IP6_IP4_TCP_SSH.pcap b/common/test/pcap/2-ETH_IP6_IP4_TCP_SSH.pcap new file mode 100644 index 0000000000000000000000000000000000000000..ef8d64a1602e3d715c7386c3af849cfa7ca7b70e GIT binary patch literal 1356 zcmZY7eKgYx90u^;Mx(Gvd7Ch9bg(*w`ts3N1*c^T@B?`}#MV?6&@E61UvlB{$EFlzs`*!(dLu1S z5+MQan9A}o=aNSy`fszi!i>V{IadNxb4A5&JSy+=_tN-3)M(j_v>j(Nb*XT(rQ%qU z@kZt=FYu)XouSSPbJK%trsNR{=iE;o$Q zTX@{Pbv7`#2f`=t%rbiOahOQ_vcH>J$hI&7^=ri@u2cr#2#jVIy4u5SF2>1CeuQT) z_RjL@5uA&|1_omgQp!klaw%|pBQi~OESCtMN)lczdPK4y*}t5fV%Ykhyx_}2mfA=| zhgMBY$=rE#{NjKLGQ^*VB-`r_`GGE_A;ZxD?F|A2;9Mdw**F*c&9Zk>9SJ!0Teq}88 z83(VYx2uizqm<3AS;BaOzB$%-~tRsza*e0FA-~fjPn3xlk z(^#2#c>N)wc8s-&Wq=sbRTUZvM?9^O(-W<82|?#W7O>3Z53PpobM!1_?NM{Fd@5D;QuaQ@(XyeSfw7uX4rzDYyJaIZ~!v zk@pS&v>_bmmhdg;ojx+ zNQ#<`9eX}SCnr*F9B1BN+hr}ft+(C8-u=L!f09}vl%<-oDOvC`n^Z4W_-S>y7&M}{ z`$dO)=JgESjLA*sIC24RRa<9w)VZf8LC#M!$B~a0Pu#s*g{*5vNt%?p!@1RD>3n3{ z#T3GHh2kPF3_s^(+s*s%3Fol=-Yj1weBtfz5+Auh8@^LYB|1N+#Krq8;lt359pswv z`t|y@nSmd`6}a_EBgGN)zz*kfdcH2E6G1CC6b7?x?5G}nIynIe)+!TlR;{|#Ux$vm z%Hu*ZKv`w*DG@9pp0B&K= oZ(7Sf41up*HFElh@~796QCv4$sa)sLz(a=AITt_AXH3B-!45n(+8D_cO-14Zg8SE^esiqZ$ zCAul8C8^0Jy1Dr&sYbfy7Rd%?Daq!#1}2H-x+WH;#<~_J7RI`Xre;PKmgZ)uM#jb< zUja30el9}= XFDEAh13L!;19J-lr&R+3V_*XS7g9<9 literal 0 HcmV?d00001 diff --git a/common/test/pcap/4-ETH_IP4_IP6_TCP.pcap.pcap b/common/test/pcap/4-ETH_IP4_IP6_TCP.pcap.pcap new file mode 100644 index 0000000000000000000000000000000000000000..6b074e5f56343ab657e51634082420fd4e3d063d GIT binary patch literal 1424 zcmbu8&ui0Q7{{NaU70Bvoffv^dlD-Bk!~V`Cxsz`sBHy3tRk|*{s>PKnJ4S}rqq%aM#cgUNbm-XEAL7!B>5JZp6?_Yj zZw689OBx=J(2)L1&Q3yb1bpNWV2lndwNfD6t1fSlb(Kc2e*=K_dOuSh&oD)KX+jP^ z{MGJ~JIoHhLw?c(*b8k-gh~i2Bo*>;{iOas0jZVxAnwF5y<9zhZf3ql$V1Nv71M{h zHK%bp5(70${;PJ)-yJmvUDsU3^n?_ksOHn2!XGh-!~pHJ|C&wH9kkLRY2y@i;wQjt zQbsXdHFHs`Vxc)%mSTLAXPLO6>zZoi3|&C+ykTlAvuJ9?$c3z?TLKbuYSA!_jD;@8 z7cQ~PoMGbKa=9EWm>J8?ry2#8VVQpM25ggRTg0~dn!~oBYuggowp}br!5EV%OPpwRO3B1p(_`f;5c+;-d4@R`b1Kip0!p5#80tILa{Uf$t5j} zI36W572oDhBW_Zd;)RK`XjYV+a;qJ=u>*E4`R+RY-qI>oY(1~!bbMJ+SG5Q^FB*9P z6_iRu$!g~(xiBh}Oe<2;aRr uVE6JjM=!gdoZbJEbEaqh0klP%wtWQJ9|`wWzET6D6AV1fmm+`wWOGd;A)Eib XMK)pIdsP0Ze+eLIqHS)x5D5eTP+LeQ literal 0 HcmV?d00001 diff --git a/common/test/pcap/6-ETH_MPLS_IP4_TCP.pcap b/common/test/pcap/6-ETH_MPLS_IP4_TCP.pcap new file mode 100644 index 0000000000000000000000000000000000000000..fa52ac977ead2b2eec4b2495057081529a59baf9 GIT binary patch literal 3726 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 literal 0 HcmV?d00001 diff --git a/common/test/pcap/7-ETH_MPLS_MPLS_IP4_TCP.pcap b/common/test/pcap/7-ETH_MPLS_MPLS_IP4_TCP.pcap new file mode 100644 index 0000000000000000000000000000000000000000..7578a2027480da2a153c4d0d3ad2f970f89e1e0e GIT binary patch literal 832 zcmca|c+)~A1{MZ55MW?n1agA(4p``|=VfpLvO$=^VA``kmOzqK!=l5Tfl=!}10(l; z*9{ChAj$u1%WiVXGca-~0cCzNh@Wq1wFHSLFbODppUA+##KI&2GO=V6FM}OW9E5S2 zr~z^S(8N1vCR!b}EV>*ZAfRx0D#U%SfTp>tq5|Y{$`>a$qY937Miina7Xq)9Qk=GFM}h{eISg}k=mdb0DAcv7SrwrNC=2r zY6dxy5#&6JZA2N!3O4X6nt^UW=Yb8}-3XNb1CEpypn*gLBOBPjb7%&7!VHYA0?PlA zX9!@B`2Qgo=+2}vMq=Wc9c(PR$AgYq7PABh@V~!Y%fJu-Xi8z$gwo^9K3PjW&z<4m!2mzq669^xG zKm-5?S1OJlfDi19#z=q-K(h^&;LjmoM|06?s22kfsUJGrQ}18@0Rlom0oYcoXX+I0nRHmdB{wYkz^C0hl;KbqXnzKEs~ zxq-%GRN;dT6`9%n`P1W4QMe4hoI2EFBEEUI{QfHV==qX}+hfuAigv~9WC+sC6$~JC+XE=x|_F8tmdBmV_OT6S`miCw`~-jyS4Tr7GZhJ^0CK$3OKdbW`NZ zf_^$hfjMx3b`t{R76^AF9C6}cNXzxYv+pEdV=oVj&~Q%hBVHKW!J5yPuktSL8wI&u zQDnJ?*FVTkvM{0tdreO5I8)qTSiJ3;M&=mhG{0hW5O}`$4#w+v{skbPdC<(E-x${{ zZZ;S;VA^$V!R*Kri)m2DHxC!w)h`Yh=eu4bEsP7Rzz zyZDAt^hLaR;9U<=nlWCRF-X%KiBHJde6T;Dyc$uedDDiQh&eeGA4NHqUIc7lh#^Kp zd%ikf9_MCe#2v3B7WL;uSXV8@@s|7EDTXx+gN9v~Mq^Z({3Q-7rDe_8q~N1#`!#z{}*DC4|MB!zdk zkSrX^kHo^&A8}o;QFJ4ukFeHoDu({1p|9DINAi!3j(u^M8c|z$ArY`)*?2!Mq+-YD z(*$8FDe4isyiEsp%@^k|4WPVe(E!*XbDbF_x8`DiY@))rXhEcyP@iegyq|u^`#@n( z!GBIQ6V8iBd)vJqZBR&=EUvKyI(fF~^`E_Iih$t&QQo7qEg_?K z1;HEBwaXP?(x5^*iny)EU*=1>|GCVml&~LE9T40+LrFGbe_gfk_L3m5XZoT$t%1{| zb28Pf1|SIdGz?ce1v_$n@iu4PF7dx?GsCWEHNepP8n$V`Io3sGlx`J6ra&n!+WKST zmV3cNhtWbhaEXK;*3JoL4;J^FW%sbLH2W3k-l*+oa>fTclKTuRui+ot=Iq{Vf)>RO zfI_^L_3JK#G*sf)7nExyBeU-;4@P(!t`i{6;HzQtV~)pq$`^*-!1av-mD1+9F{#7i zmk}Q{FwAmvi3M7F71lyK1-qpVD7hECrFWHX$?KgVwO!iEN{!$r|awFx2|O zU7J^6Z>^x_I%c1KG*gD`4X>u+^6JJ`1~pkT@E}kc_Vn?HR87e(Ay@$epsh zah)W>IPOMNl@$!2zHV)Zq}L}?3glD{0>k)(b>+~@gu-}hkWFG}@NBXCX#(}{WbDWC zmXW*E5L^mb*N@*x^YOO&o;DRk1@Jh9!%Mf~CuS@vL5~kAO2?`N`;>fA~WaDe?OJ-zL4>3{ejHfL^0!lIRf z(64!zGF5SyYip}9Q-wG{`AUtbvq_xY+O3(SYfxvXHM~rW4 zan>Yv_R7k;r0rO6Wn(<|9nW;1(R1ar{szfwFySo0yANdy)EC=e1s z)<1hz>$3+@Rb|E=Q&MSUs}#4~N=Y8{y~{}}mPvb)y`9nY80V_e_l*otiu{w^X<<$> zlaap(P14-}kLB)!Ke;L1auXF`ekGyZGMlz#kHXh4=u3rLBedN7B(d!iu@$R0puP3H;pj&m@pC$c^vPQJ3M z%?T;CyXL+DOToey@&ckiLX|YGDDi(3;$KQi*zdz4&j_@&9c)dFDT+(~AAu^vvZy zC$AFV4^b%-sDZT<8qJ_<3Cj#Sm5fZV)0oP*87(OZFH=RKV`-ldnM}BrNdl&5VhPh;<&esDg702T7)56%MS2^;jAzf>J2mM zi|wG9RJgSJ0T*9CVJB(`CV%ZG@CYJ^3k@zxTQn|;GEgEhp4h|{)gAO~<&L~3SYeB9 zKkZkeZLU1gSvBIumqOo-Gh&-thqX-#f>vZWJLVZzxXLZC-%{nyjStSDcv zi`nN-%wQKD^Gvue&xTvf^CO-4l&w10>Bc$Vx|M`ZI6_$WgKcvFfQ1Ujv@7mhid{a8 zG~2R9Kj2ByMz}P2rVYk@Fb3~>WpI^P^8)h%@%u5*)^p-Ezkev~qy~2#;e?+MN?c4j zocExTsQU{9-)slllllhN`~01zF%;*pFCB9fadfu|pYQ^~b*Vbk~aajn;(iu~>eb273STqr)zC^Df7oVyOn2P{X0RUQO*3`j)FX$pUSj6>!$^%EE zgt;mC;no@Hf(j=0l5qg5&Te(Zb8TV#Wmdg^Gi!G{#xO2Jl1mB=yjwcEFN(s0Lyc7v ziUvCyjBDc<8n68lFuY4e$8CkEV$FXGIIY6#u_Tx-*!gV%rYCbQ4XxAt{ODo@FjJq# z08qg@;Oa5yHK zB`$5bdEAoC7seutOIFg$v=by8kvvJ%-slWY)-`#Y_| zULNjx^YEPaohizhIZWX-?AWC%Ln_!iFF*^Q;?@nU`hELyaQZ={zX&?&_&d_n_Cot= zlt@8(%<(%oaX*@kRbAQdsF2J$t=x6o4*+KoS~;oRWHOR=cR8on#Zf@=G>G1tWQ?qA zy2mQG_x6wLP48W!GW>=Nsk|FEa?|Mpt-#(y@*QzOnrIQn#$!wL??9wO#BCtH9L{Y2 z^fS0V;=L&qkaZ9gG>Eg2kd-;uyr?*rG4Q8c{d?6EMaLRNs0ZLFEs)p_ok1Fe4@rLC zN-a2{MxcU0+zRQAqu&r(PTi?L>6{8HwhcoAF8$jX2(O6`9(}wh;;Cg*Q@O6a zHWxjh*KF7Q)EIaA_4ospM60-4KwwFglvvqDN~k-jm}n7(=jIqk0xRuB=x9K?LxL3n zNPNNpmt_}^DIMGJFa`%l*IVn1*2BkevnY*H?2x^jzvgjkw;hfPlJc_MA$ls^U|a=z zD@dX&)`I6}Fr+Bnt9T|0^FPpV5!LUahIc6#e_bq@uH4opa|(P!Yi)~# z^Q!KzzdJZ6$fB{^VZ??PTcK|yLCBba255RVU$E{&TCtMBV%j4yrvCyb{Ew!W{B1hk zzfGq_{Kxc9@qe2h@N^0)9;Ce%or$;N>9ZBCgv)5!I+)1dPkXgn8a z+w`XFqo{1*J*J1}DcyR|C#FvF#gr*=*X%;Vy8@t4fY>B-nyI*0;Bvx6!&EPI1!cV{M zEQpx!=@aIP$lw|JByyHl5PHtlq0WU+jZh%Qk)0;8o;G^yb)fBL&xRAvPLhzty7mNY zGRy1O0;K3uJ_OJA@Pp1D+l_{vJ9$kSw+85D_$pWf05@NWSe*<0)}G?VmiSE8MeWJ? z-e2j!Ay<&`Os;rWdzbI?eSb#QQolo2qYILt3W7uukGqk;Cj8kGDl^(B%D^=2uk`@0 zbn~U{qW6J>2>GWb2H~N|;_saD$rL+-!nC%236X7qd&n&M0jV~%27BgRql z`aX;n_{R*7uRCLQtj2P`-P?Y+g9_z=2evZKB4Mx<(+Y24e9mb?{d`;77cJGI!t2Ittqwn;g@FpyBkZIayJ za&-3Ysi#<{2ck!yZ6tQN$KiyZ4p-RX%EPh&kk(r2Kq`{Zav}j@O=P>Dxl6tS+7WKz z#%{P^8wjKH)i-jO!3C!*HTc-TF_AsmpWQm?Yo8ien&Gzx37!B*9t4KY2OU-Gb53hd z)WtN4Lr1}@UwgMM3TDq*^^5TYgnnHN`QAHe zcD~G8%QfXL_7Au|MCUk)tUY^{IlW17_U2O9|(GvPo~Fi zb{)(uTh5mB$gM^<^>NvK203>uKrBjqAn!>4mHtM3gLD%lE8d3ALqTULDWet1de|yB ze%jV_bE?&L+U1`_;09qjYk~4ftn{uk0ak*8xu}2jAz*)knASW3tQ%ota@0`U#beU) z3&Y}1niv!j!WnfV#N#~akeXdk_}iU^@-$Z*D|H0kEjOjOo7Pmmk(xfq=6ai{^p{q_ z?@k>H^SqYqvf@c^9)bP_CPQE;5i^Uxs!pvA>6)@4@V+7qa2H(UIScGzp4t1o zBTfeQ3!p>fJUbj;0E+{s?!j}LmArFkDVm51Up1MvisfWJ;~ybpP=>?MPe!B~FF8vp za2>r1kjWZan^?cb8^9~Wdb2dC*!T8mCruH*BrDUGF`SlyJzcp;JgkconzZWZpf!~z z&Cy)i1kMK1%=*!W&Ty67u-jANEq!G-e5=_zl{ZK)XLg_4l+0(IdoM9y7#KZ2$=dhqcB`1;K__?mO z3cWCwBF9J}nI8f)tM)QZ7-pturL;f`r$J3^UTaUiG&E3SgiJ8hwJr)eYU;R|?aetb z^ZCgz_`3e~SxXaxN>h+d@qUT5mwNPVtgi*4LN$8(r{&>u>?S*Oi^|H?hUV{+QnslF z(2BCiT2BvI_OyZhQRKP(Gkw)lEhAQqD4Xxl}EGN6d(nJ<(@@os^q~Fa4B)f^1Nt6_jYwC&h>{71a2N(5y<+-yq29W zgcajlS`!V8s=6)q1EpRSk=;=@)zgq;+i+o7hUR%--jj!G*v(+4lZ#Vy_A|(8Ga99i zY6mZx9W7iQ1$i1qdCxloBh_KnW}Zem#D1PMEZgjioOSue_eDw7@;@-!BL$(AmoClg zauc)~8sJTDei1hx3qjx|79~wo5gK}hzkGll_d|KzbO9sxjDWK|b0RLOY8Ja}JjZfP zQ+c}1r`PcXE304L$Du%-3`{KHFZiO#J8v--Yk~Y&SykV9VpL)}o!C~3YJGH{q!>sJ z1Nn0VP8)BxRq&gLwj;Lp^4BC{D8NCP-+S6q>xBC5*w(}!T->flgxD9oGVNXj_f;j? z8CN?Wsvxd15m;9X4}a5?;s<=|K1-{-VQoI7OLW|rcUVYB$}+^h#{qV-y(jagILuG# zLQM^A^e2i3a>1|y^+WgI+fSd4ED-a*COCBV?`vsDho-S{`5@0^>67rpHOM*2g3fq5 z*CAxQ);zZWE7A^)>BR&@Zo@m#6vcutNG-0Wq@4xfy( zWGDoD!~5$L=hz+=VVbHjErct@rX$$d|e>RoKO zOVNQQ>={cU8~SkpHC-;vEA*}b9Ns%=ry>#{_@dtWvhh599ov6v8~`c!M=WD&fIzqJIAiHr@atr^+gcv4x183IrSaO1wjk4P>6vXEpbEVL^vl@1$g{{M_IZ{n zNO?&M7r3apgQdSm5@KT=YP5RQTf5Pf>N?0GtR6(;C_Y!<(!Sk!Q&`&)L#*@#!J8!^ zmQNY_2~`MAm=)v)jLj3o0)yS=Xhfrp~J>yDT}r4db3~>v+)EW%7E8UpnN)Zqk@`{1N)xfpscUb`F9PpO@VAriR z%=b2rG^^CRCc@^Vp(@=FfLPz{;8;==&46gtDXmd3>4Dv`&<=7diV+G3JRKWL4F*6| zQ3;~$I4&y~tEI+lPgm2HM)(};?;Gi3H`}vHwQQR#z0NYlx~vG#wSR`q&_YYtYrxFO zmBQ+WOl#Eyy3vRqXKKC{6_J1k@C~(L0>((k2umSJ08O-Mda+%K1BiRe4RYQ1ws7uRd4O^;njA^}a=f+uAhPGTTNT05uoYfa9%Qs0_7x(a6uw4m9SF7 zZ-3wYvH=zc6Y&E2lIlw@52>|JMW34kf)%SU?%yK8nB;%8OrNF<+*ALE4L113gnN;S zYp?BbZikEN{f5+H$)vZQCLnF`sB)o@YsQW{tZwQi8%B?6iuC+`kDBz=CIYc7H&#tl zGF;FcRQDqiY?fTNoouITAPk&8=h8zGXJoG>fsaRiKL4<>=_(VJa&GRys^zi_LnkhZ zXp^y=;*Dy%)zO2Y|KRv&80;Ka<(#__-e$if)>jv@v0f^CT$VWet9oaW3!%rsSAS|a zEnGE96Pte5;T5dp-CgsWoe{}O)oZG>$2Q;4Z)(qgpnkZ0D@%`-G#Ss_^a&h-;w;6{ z1lajh7{T*c8tV#eP|2d7re(Q&;16 zHzojaE66nyjrE!){Koyn!%`n_LI!Ypr!Af$(v1xxK(xhZsQ(V>U z%_X`Qgtt)uULBDd9D7VZDeC8lUZ-9j^)_CZ>xf?;PJv!O&dLCAN-db@`5G037wmF= zBYTwVq^H}G8hWW^x;Pb1|NirAx*L>-i>e^%-PW=?FpYu3Lx%Nnt>3|R&aYu>fRK)5 zHT8v3o{i5KvA18l!`7~Fl9_3C2YQ@v&mCoSc@Xr`L;;x+~sFJsR8G;F15G1B?joVWZ-E$>kB*zF@Rn@$B=^ht+B zj!R@3Iu>ezf#35z!*oi!vu^>-EnftjJDGbdw7h+tV}y*!7!_ z)%{X~OAmX0RUXO8rga^0^jN!5$9Bh9F}j@zm~R3lxrc8p9sZI|YJ0@9G`jF!{IvJ{ z`?=}JRu{xs%-Pf=AR_6?lbI`b=&;0pt15yLWZ1xd_cytp-DgKKDLZeWl98#4wmR2d zBkwiJJ;fbX&aEQVESG_z`LiYpWer9QY;~)29NXDL2ZX!&a<{2 z%{8id#=Z3XtB*K;cJ71g_r?%T;dTyA&P!A5IBA=PZ_}~=Z91>hKc*{V z|J!u($!u7ap`@9-=?+TIpAlk*)-XigBm6CWjMlmuL#3`7b?jkUmb~SyxEuKe+#w40 zSLbdMTZWi=51)Xs-Hw^-z&IQ#QSI;01a^|$%3m{ZIO=o2MbB5zH4+RKZ{+8d(qeXE zcN4a3xbwt_w}XS;W~pmcwY0YXU_ zz6y-ucgrL8P-F5CBWQgk)d9G%0d%pGV+f(zxz^OHTpqUR1>hL=SvWz$Eg3Ml5EVx$ z7V}39XA>iPNYkvAY2@X_)W_eHXRX9wi;`Rj85MWp3&Wrrfyc&l>MSiwI_50CbKqCj zVD}jhrJH1Ga>z^T?|Ppv^qqgrp`1iL1_P!4VMcLET6F!zo~vPtf{yTh#?gD`8PPC@ zr3(&j4tfa~!m~Z851sA~lnK?-T7Gu(E<;CDe+T6`%mI1$Qw7-~zYJKGL}!0V0v4Me zPBQ)bQ3QCCT+HrwA1IUAZzJP~iWY9E)4G~j2&iQo&fJnf|9TqS)H4BdOOX)21NJ}m z%z7j@w2b`mem{xr{)rN1jJt=TkMMH4%f6OnxnrzrSbkk$B-n06+-%~Gcs zlL&q%V4itEY2#xuecb#L#Xe*|&1r^pWCw$dlq5rJvXVs-sxP&`B)lp@Vb%$x+zYG{ zL^-MC1p14X3`59fUZBMhg+H*IZP{-6dZP+|NBX)gdcAzjeTkBu*VY1JVt=Ow2K{E+ z6&LywpjxQPcy^maf2NXAnx=X8nhNxTy-NVMJ5lFo6(?n1Bl2RzUZW8uF8E3zRr_Xd zpv8ucC*U_SZ+1a>Wv&%Z1Elf$4^PU zn;)(i5V~O}i`5j0rm+_NYltPX;CRL%HE)cpWsSQy6+i&lP|Ol%O4!LZPXa;kEN#rru@vLeZxAO!MKDN>X8yD4&P+wi;V%-qq351&Uh_3q z_1;2#>oWI^>H5bey`2x?YeKSbm<5$39tx-OObW~8rECd^nM$Xxj!+b5^WMIEr6i8r z{tLGcX%FyZll?f(Q?sU#7s#wTj0j!I7xdfs@jgr-ko7sq6MqyETlP2>e#@8qIPqtC zJY#mBz zy@cm)&e{R>gmqsiy7s=|p<)Q#I2o+qK_a*5{_M&XjW&yuNE?0#3n*uuyG))~Vut?L zdoLA#n~wEw(}m9eF+DK)-=^PTUp0K0^l6ClJT|ziblb-(7H9Jr6fWO!RS>){38w9d z#?Z{p23dI_e@u+uNuNeZJ3=Ex*N=ktr?Sfuxg8Ik*#KByS~;E%9RNOL=dE|3;OE{X zyPLJg_j#O@VJ3s7gzSn0dnYeBokouf!eu!In0}#PTA=gs?Ht3o-`&P0#&sD{nBmx< ztgT*yFV@T-9&5bkZ6w9>epQna#M)PSwLWQPvgo?AM%^>6~HO zh=;!a7;P5WTa?f3Magi>)@Omf)aDM}Q4$H41*amP_0*&hSZs_XQ4R5$inu!=wqjC9 z=DF$4@v3#aYu8%?V6@E%mS_R{A80H7|kbns-lUP-5VoB1j5rL zhnNVgF>>52+qt9vRcI2&jL)SDjqtSqADRdHK1av3g{U_QDdke95oOX(5K)<_ZXZth zo5MTyR7O9ICm{`+-&_|%0qfeO#xdt@!Ct1*Oc`D8vfTzFLU;F3SPH+1pbK?EIWei@ zjJ$vc6-u5i>tRjehIR!&tt?e0Jhmb;lz+a16yD$3=B3UJAcov(G07YtKT)n0yfzV& zR3oo=#@~#l9YX`$oyuXEY3)05WIdUI$ZFk&m8WqRH&hqpKqI(NBJs+UXT1@MZYCc@ z;E@KiFq0iz4gnKdz~`*n3qJgzjdeGkVUN(*tL-jU=a#m6WDKthS;bF{jM3uEsHj62 zRLN9W@oFeY-@x%BM_6JYdpmU0v=LGdxh^~+v>KIf4yz#1DF;OOh5b}Fd@gDPYr)E* zfGgO6DSN%m`qtAkpj;`;haz3Q8XhEOuUV z%_b>cKr3K!)ar)0YiNUNJvd+ zhp-+ssvvFBY3&pX0{90639TcVL%1l5EJ6ku*{Jzy!e-xqXvi07^QX*F9%$(dJ5u)p z!+n>mXBBV{FDh=Cw*oep(C5|wWm}H~rKv@Ou8L2dH}ONao;ASk@;5K1hAC|Y2169y zxhu*31VxgvS*Sk_>w^}UN+ou3B!~{uS$}P+Qo^Wms5UAp3{vnf-zTs^OM(Swky{;5 zATX5dyvAYwK`A8|4J2%Q?Gj>9AD|A4WNI>gy=$>=9QXB{7cx4Mv%1cTmEdxMrBxy6 zVB{dKpa{KIb_9VoWrRgnobrug~dkWo(Q)gfX znd7cTa`H6zo6`%K8$0Z-2$b0AuWKUQJ{jKbe6A5A#?W?7P@j*0Lesv)rT~0RB}8vu zzs1FZ%Ec)j-6$AjLqumdvl__Rt@9M~EZPeE_<*&kJivA^*%^{)aA6+67;Lx*%Z<=Q z6(FeChF&FRoEKbR0A?J}4cU6%hpXarqkh_ji4BQH*_yTe>bx*LqpdZ*Sg2&HRur`v z+TB3qpnfG=M5OrfOY2Ef$2eT%!{1tqNf`doo@>SYiEcmO`e3jM=mx6V8o|GJWk&~( z{(ZtmRhPr1p(L7 zJ7D)q0nns75RZ5K$8&n+-=<^!+jI%Ne@us+`M2q>eJlA2KM1Llz~HW{ur7B%9gFfYZ6^l36!H4z-|>UiXT&NU zdmcV_3|vMgtroHZ`@gcD5D!X9R;i6BJTO`oRHBY|5hvz%XLGBV?e(4cRl z226dy9t5Hm3vul=bc%0u=U*Qa^*sin*rdS=W=@HX34IP=IW2yf zD};+c2cw!YrZxy>E>QHcXFb`PPF#T+!2)WtV<)Q&_XI@dP&xa^HxRJbEc9t{?4jrR z2*KQ2bMhxts1Q;Q!#|A~qE{eHTP?=k;??RDKlgldoaYu@UWOq{@Gne&6dB}CNXoEk z!hAv{QwHk`s$N1Sl9X}!Sf}MrGf%YFvqnzfq{H_OYhmJ#kW`SIG^X0)nR$d(IH}Ju z2NXP`5&~}3us!%cyf>CC(1Srx$T#(oPtsxL>uhaFz*_2dl46Cw?h>uTwX)yFPdYV= zXH4Vmt;^}6z9?GIcD;}{33+onbqny~mqOnVzH@y1O6oVEyY7HI-iNd@bm%b=%B*nv>PkxD^CnIRq-&Usxi#aNHfqf zF8m$)vJBY}CSqwRT%)tYz<%($iD`30$o6b2A7}2aI#QG}nQ;0`L6tr)Cp8?82HfY< zzGE4IZ2TDb<~3B>JX4lni2Mdsg2cFYB5mwF4a2%rsg-^&Sb4GCkD{viTEhz(AwV#g z)&+0h_`)(=L2;_9UQz?bbTUb4ZbFv#z1ifMr!`UIg_DrGA#l#E{CX?^n?gcju=h@K zMN_DvL?-GTM3;_Sh!L)7z@SLk0?7f^B$X0pn++}}XnC6Ho1cS#_m<7Lc7?whC~;;A zv&sVC2Vdo?bo><9Xwq*%AKn?(dCEa*L$-=;!tqq%j)ms z4etlrh&Kb4T{f2sY#OaY3$h`O2+lRp{|0-_L5Pl7(`}0V6OltYY zWK>Nscv-UA&HE4@7{h4X7G{c7vDwk1#m+E6%|PgfH>+<82@+1UgV3HM%*B5+z3Ok%G5&43?D9XRub%wd^tFWUmP`~z z;XiSsuGf9C7vnThbf}GVNK%+ClOMXaUx$4F;7$*cM zzrkHRCVt4iqCa^*)LCY<+zA_OdYhq==U;RSxoUIJs{IK9K(tPT|D@A?Q$sjEimKwW zJ7Gg0Irb+p*qZW9F_;Ui*NF}KQ|26a^siI}d>G((OW&dbUy+p_!E%|q8GlJ$si&`f zWKmd(hcYP89J72Q0c~8GP?=<8$0m=1j`JsbzHiFoHnN=){%V5bB_-Zk#QXJ~@8a9v z-f4&ozm6ONHB^%W@d@>vWgT=PX%xt!^>II;jsmzapNkhEayN7r)i8TNBlc+HCg3kR zm!F8J7#-0LF$a5u4JnRp#B5)<9x~e78r2+F(M?gE;+A7lg%4A!TGJxCy>=ok|P#E^mF@) zaoKFyfiiNXW0bho8#OMQ+Y0G;$m9Rnh<*5|>llREwNRXb2_yn`X2%QOXHUh-lQ{U;cG`6LJWtMo;Xtd^M-@Ry%e<2b@r$vd(e&#Z zy53KWY*SRjuU6aF_ z=_6yOxN8@e(I7WGj$o}*lK2RadVGU*`py+-)8jRN7&5cpMkt8I1uQq5e&EFFv?UH8H7QYRu~F`3~+ zC5HV)^jqUjY13v&^wAugG!b_j(pS^>Axh+h`KL*cfj(O^{jSI6@HJ!Y-bej)6rr|v zo&e4_#N|Z95;~FBfWjd&oqQA*Oa6@^<7JZCvm6XJ`N?3B*D8ckD|t8=Hbx62>V4zM zZJV%+L;W{=wY~`Yg-D^}=a;@;?^~b3bT~4k0~OHFlc_>@B_K)joVur$>jUOac#}U` zTNW;{gr+t{cgzxRH1U=CsCF;uI0cQhF^JR6lm%Dv?a4Z_zXJSb!DR!f;p^*ztw>LB z8X+^z3TQy3AP3Pmp(MEYF_-vT*n(6jLQp2PQr6ZNYbGnnBo&!ku~n$8_w3 zmnm)Gwp-GdAoa0A-IyAZL}`3B+(^O}Wky_!b)zpWj?BlgxqTs@p!-0s(I6(KJ!TA7aa5%v683_Y6TE2&VN=?(Rqj?m8T6TG0wo|`Em)~#Cq zw3RN(uDXS(w2ZkgcM^IG5<`PIr;6`RIs%2R!D#u|zAD6OHiI-p2lB(GDxB&{4Q=O5YAb zB>wwlX8DN$uCDs2)E(VOGa^gh93vVTLavKBZm>$i6YkMmi&AGbVl(`k{J5+}?^0%j zah;ZH*E3oapB?#|lKilC50gUpOZr+ZHi}Q0UgEMe>J!}7@ z6#5ro(asqy^6(G200Cwl&<$3~hd2TQ$VAfPKn87c^YXMl0YrY^&dI}OGj#Ko;X6dB zLELe=h2Wcbz^?z5OI)j3>70;vl%e#A*GFF(P*|n@trS5m;Gs{k$;G9PtfOK;7hzxw3p?N~E+|E|Y=L9T?UxGB^#h9|{0S@jc8tan0 zW$g-deSFE1P!`324)&%vw-NE^R)(7;b%N-21YQ%mP4KY$ErVC|-06{Jq^lltNmCrb z?&;%D*`TLXBa<5yRBjYXFZ*VHQ~z-bx>#Zq~rRK4$)MbSXGg7`J2njp^DR!eB{skSp4~TbM}U%%2jqKr$I>YOz#7k zRk2|h^CZLZ?e;CV$hM5Hy2wlZj&#wAwQR%aBbqzJwa@+g?j8Z!otMj zru_&wyeo^C_xZBT^)oxg?}F)~mF&utn0jBs@Aet$yMakAq$EH;4swbGZHm;yBp7>y znALJZJB~x{f1@pcv~%uY3u?9te?|Rj=kAexd|p73>){?(e%q#9huK)750Yq1c_;Zc z@d{7#_G)4#o?gX!Gg)9o1(RoNWgvZB<-IjJJXg>uGzb;+# zF6_5Wm*r0gL!oY3aQX#GWa|@w2iLFcz2nKWr!=gzRS&JwU4BNAG6GRgt&$s`Gun|*3GB%|f0AfMQ*qFi?{ zI7O$qExJH#LLfEg#%D4!?@K;mn1b|SF%7aO(}6ly6{fQpM947jFn$uS!63c+~BK&K!xNuXMJvDDfoe>Snz@}i(r zMwT<*r`jiojd^P{y&V&5k!lEtB#nFzOhT-`ktY5diyJqvslqkyQ}p1siDW1DbH1tH z62eWGT>wev2EQiYw5jeO=gekNgWE^-q)+#ErjydLet;UWFSp5Kx|= z00z?*B6mZ9l)k3BnGRNPPdH3iZ}k~@Rlg|Ro4#IVHWSm{}yM6}7Ab_1my9nDp!~uv5 z^Agq}suNB*kuWDJNaY;l3r#7s5)2w-_j3t+4(~q&0Yh6L4;J&+%k+~;7Mz81bll8( zfvW~io3%|l?@s$3P0g{$rFW?AZX)}nbRx(s0x?Ck;J?Qa-~7QI&fGBP6U*U|nYn(? z8xHM-aC7#kO3E$g3`ZK6LE)fY5ft^#`6Mmv?P3WtYXPXvqBfdUl5e?0v7uU>W@&DOkD)i98o*#mNGHW&<88+}&5c zpq3=9GNln#rdUooa9p2Rl7QG5i-Msxg3}b5hmbS3*9?!axq1Z$)(UwmnXk0#qi>Vr zMgs{qq>D)#{~Fhb@p*2`=c?gw=5zQtOptnQqyX3|5a=SD#TH+b1FX8)wQ8 zg;`X9>bk&ruY}eN2L1!}J4WeLC0HS8$^!%nm$*5+!N3u8lx5D9D($aX)wUO$@#kf! zICqrNa6oH-=@pvro0GyE$06~E^=^sd+21GDo{tE=VDO4l4i8$ zCkCR0myg6C_Yub6WZnFJ1OOtv>uH@IWu5b_Vj=>x2YE-w06W~xigiV11bC_W5n z90rQ(@2kr+q$(Z-C>+JhEwF$lfO*3vBxXHP0OWADcC!Jo$8umWaDuh5bpn8jg^Mw# z#rcXl;+fnGUu&EYZbdS7ornpJdJzE)IOo6f!PIFp$fZ(&5xZun3=9WIjGJ6nFPy&n zy9E0sRg(Qnsna2}Gte#aLkCufq%uap@*tV{WX9F21NhPiAOIALa4G^drj-e=;TLRz zyWP!JMHuSuVC?7df&E9EOGE-SY^3p>5@(?Apm}|&gv?qlz!1)UW;ObrY?3QQWP;n` z9#B}}$j(RXmYEs#+EJ#-WZ&kg`^UJse&H#D30)WHsMp#Y6oh?=Q*Y}3jd1@QCf{G{xM+g( zlCG1PXNHX%*TzwvdR@MPW&eg8Gmgt+bm6NZe46C58Mu?9&mwTU*1*PC=T2g~1EVf$ zB6_Hk-qO6p@tBY$_V^9Nm;q62JoH>gQg0eCc>;Qt=JRIFt1{Qn-<8tIR^ES%*1}qS zWLr$=FW5b4U#4SCKK*O-l4ubbkL@~e#D0*JP;qR?6J$f`iy&;<>+M2}z(7YCrdKOj z(DWCl;exR3T5)$@15t_<24k;us1FzF4^lunp^rs5k)3q!A#?UQG+_Bk&EA42uIo3b zn8Jl51M-DLCJ}+0-z`4A_%4d9mb}_aNamjlk*~P5z74q+g=OPCeueT4HGFE|qZfy8 zK7#wN-XXYJDCw1E*PxoCj(QG1I~NiP6!qTT3gUtvRWv|3|8pOwc#%A&Am!~yz0M?` zINO$09at%10$`tTWVoGyl>caY?cb)O{@Zk2_J2$t|7(cF|7J}5GaFz5>=qkm&ZID2 zr9t8f`b2+<`$b|dmcLu!jhQYp_o~sLCE3a9%d2?|(PJHD1rw_jQ`CTGq!X>)t4356 zd;%%6Gc_h#9-%*al<#^vg}YdKZz^asWN365nVQRw@00hrlfn90s-2GFF{g!6L@%x@ zzi~B{mBLUv@7 z-`~i&mFyO4wK)L1)&p|e2;#>uUMFZOJ@Rh70xdI?KT(V(ytXuYfwE#wUU80OGxFRZ zsXF{?(FOwm*C4vd+d*E<80#K#xdbp(dixqn?px|P%ws(WdubW^+krG|bkesN$b@)8 zPh7xjxat2;c27-$EZVk!%eHN+%eHOXwr$(CZQFKr*>-i=xV=~0eInN5e#}4c#mJF4 z=OpK4>x~0milC|sa+9^7Q@LgMV`rdL9~viapI}+4W66*s3lwG$3c)r3e-bPtOg@UT@d`ayK zn0t7aedZo2?xx%}V+C*b*_!eN2m}kxHib5Fv-bzy?DEIw3Exr%e$LdIC?-Stf?m6` zWk%*$l$l=QrnyXHiK{{S^E&g-{XE+$q2*=s%d=ZUaKa4Gp%pXR~?Q_{~C2T8?0p zbZ4}@ZcL!61Mz9u*%x2sfiUn1!@{nfoWQk6*1w{$iR zbw@*)AKX)GszFnBeCcoj{(t(R$HJhSGO{{8**g-zB~H*Pr%=gF2ZD|>>AI~lP5luI zU!9Y2jS1D{fBDjHEiU{WfLcDdMKd)xaS)eA*|PQ9&Z##bN#F?>X_~5hHghz&iC9!j zGn7nrv` z>>QF$l`yU@iPONP!XO`$?}&!t8jbo?K6GA05Rf*T2mEqoo^4W%Tc3sT`$RHNZac7& zd{F5>eu1BeBT_LteBUgT5sZ`x&xh7KcQ* zXAQoMIk3f}hFj!fDbX+T6X$W-BkcDF2R@y^1kCR|Vm9YwjdXzypuf|OX}qZIKIKt6 z#p?lpV}8qZyvhqWU0M{Fb9uDuJK)>hP4i=0}?!Apc|eqE%dZTCkTzW{$I096A&=B31oZZ%8>nj*h^>>E0g% zJH$DVW)rT%r#jXD=rPk%B)i@&&>z9{j&3iwv>poBpoEt;DZ>owdcD5RJEdIgTJ#kl zag7``GrvA< zy*<$Ky@V)$G*~MP70LR*&jFDQ^%T4>-(?f=M{>(c+C^2}M0fadfD9Met?se6KS0If zbwXkWAgtjBmn(~YHTVvHL4OUhfasXGto5kWUY;P_9H!lKXwY8CA5|PDZ$aaP+Bwv; zM~Eh-wB8MOp~658IV}fZ;tZ=!$!~sbVn;S^L-|?i!bhVIn^bQ)sYp%#qvhe188?nH zN4H%MdiQ;WXW6xi%j7nohxMBy8bV?sZ!B;C{Z&vACRx{4fK*_>QM8duS>s;0^|UO^#gEH*Jb>Zuyp96lzju|G=^M=ZGGRZ~A3%yp;n zAs>mB3xB`WblsGPG}6q6ce-RC=`3cw#wC@+V0mK7;Ge>lv4ssr#^QBX@cc=C*$f*o zjl6%eUmr0qdZtB7QNH3(Z_(r{8D^4}#Fwop&UWbQ#0VV$yR<6V&-DAs^m z5!F5<1UcV9kJ)CjH);ye=3HE-;hz;#dYLt*^oa5)pntGzA;s=TA$b_R`F@ljXYMM( z9zM#hF$rf?`nbZ2<{(E#dO4$zfE&FZ1CpFPnokfl0bjdmZLRxOO!oZUsnbeK8SC%q zT-u!-9??bm17%z)Rn-Qj^NSAlp!~0Uvr>(e?|(yD0DnC+A)0i&;cwq{4oHmR(cW2W0KD z>SKIy3o?l_c$_HtcG1eqB~<&2aMslM%$6Va5bmMP!PpVh@qdf%k=fjL02PMQw%5Ln9(ca&x|zYq*VgT7GS)hA^> z-eRyp&w-d#lPl;T9XtXOuHnk}mV?j7h)0?^N`Ib^hK<$wATekGd%+rBc^BJjGqh5ikeZ3~)^Zg403;KvA{g#4GZV`b0v?Y^Ykd!dbDsr@RPE5s$!{h|>XA8&A5#q&0bAtJaR%-vR|ED+p zV>-&eO}8ZZuj%i=|27>}j|a_!ydw=)c3sbJ(i>}H4uVTKf{>&l*RYu9mKNMxU@sUr z_@HP|Cq>G9#HvSbS_vZy8=oX;b+eMC2>?Xn5-*68gs_~&>pWDPpxC9j3C_WKPv%k0 zE4+NIud!y?zf}(6x`4459?k54z^8S37CO)u*j~td5B!}^$ga*SLrQUv>(67adF=*> z$BVpLP>=o&2yNHbcl)U;f?oCn#%9`H@J@s?2t{qTADOiUr6P?WEAi!b!1Pm|12Vg6m09QgRpg9LCsres^a4?5 zRfjDs#%sW(S73xuvpjQ3EHO?zwaj*QNd;!pDtkn0x^{eVt!4rxAY%UPe|jg+-0|Vb z4J0%1dto&?&;eZy=eNaaXBviUL5fYRcwJl9L%l1RwHnS z@5&@(ZiOuf-*~rHR;FTyc%&Y?6G?2`h-~3rhM>Tr{16?B>Y%G}x+elqXT3?zB^AWb zjJpEuZwB#co)_3*>=m{8$Ub}*GYUb*G$ljYS5lHU zD5@?Pn-<2M9A1^vbi>5yBPXYwX;&~0H$ z(HWw|1C+UwLz0|~B9ausnD539Sr1#BlASZl=2-&_{P9A_-WgEF2lbIQ>dAW7I5K}ZBwU!4C8<56~wU`iba z8t}T4N$x`TNLG0z!_mbh%@4T-!qbaO16(V>#LrwIX~ic@vb9X{|9Xdl7DggQF6i_U zFd>2ZQPHBr^xI8R5HKA6{j_fSH&hHXQnysrRUUqXftDWA%?78fCH zd8{qrHIL^EogEySa2x=!%pA>@Q8KDRqXYfi=4VrOrFh~Z4OLg{-Y-A>tNmmAQ7T_=iS?3431=Q2`<>T$EUilNVqi{AtDr|@8>Qa8NuzAP(lhuAtz|~kfe+{a zN~^nZc58zccu9h_vm2p`VL3u2Vp;OKf!@{$h2XAC!dmSPAhCM0w3U`|{0&T>Ls@PwEH7ha&SW86~z7U(g1 z9*49v$A#_Vc`3wC0VAW?MUM|Me%Q@5^TyT!NNb!dzsSosXALM3=TYN6v)m z=<6!d3CTOlhteTos9;qbEXJEFVFhCt{Skvk;Yk-_hicp7s^~N_{_MkHw7>z_C$Hj? z%|^~jNOj1G2UIR-*4GTl_Pn#j0-xAjsSAY!Xh99-Dk?F(5E;TIwRmCe=$TOn{ky!2 zsbH);%=kP2c)E}q?6<=6VekufzC_vvA3Rdk0CntLD}>MW*!_^E;aS_ zE=Y?y5g#OKeOJ`9Crk8R#IAOIm0_?008r9*%eEHVW#P>h0R>$I3PJLDzZlZe{P<&p zyW4T)&?OIj(-i&bj+H*(n!~ehsfVZ)20PHVlu~3tQU-#C`92LDPwvyx~DMEXE zjLzU9O0_3C6Mak3OR`-?5<Y0mF=nojy%K-P@g1E7?2-2G6lUB+MhS823CFb%zV88Zm^cksi(V&*rR zdTFG$2?1nADrz#G_xYNH01_YGq%Y9)1ThjK!b|(pYU&Op*P}8z|K%Vzkb3e>XxE za%nX&${cOHx;8ymVb?by9RZ=d3VG@XSrWPnzn9OreTQP4@3j9N!NY%)q-_%&kklng5N@NJcJh9 zufvo3(kVF6cshkf@Ze&V@XVF*qD=pI%yL^nsU7iE9Ps6LWlkbTV*uU56S~7T=mYA0h_@ehbM;po^zDXgGGpvMqdAmOQ5s^srJ z+U-dAqCnj-y#2P;^9%__THflbE9vl5R2IoAhGn8}Ur81QB)kDcws$AMM$LEGUF}QO z$cLZ^8roy>m>GLnisPIoxn`h1`MU=4I|%mIG){YJq;$4SQ4C3zLc*+RMC|=D<#7E_ zy!g;Q9_kz&OA_o)M;$iu1WAVMYu!>fr8t^F(En(9^FO8|{o8aGg#Vf@Km2dg6Hh$v z^#E@^`rW*uq@279h9i{gpPVVb{-B053ZG6v+HVKgKOlf#J^7hs+oT3oN1g(BujYs= z-|cCgSBrqUf4`0Bq!mw#vq4*Ekhvz0O8((+V_#(b@M39ZtkQhD<=zuYhAo5}cD@E^ z-}aLsVH5@7nt-XCsm+g81FSGp;wqmS zx{2TixGKlA02N^-XICVl(MNSTLcLb&$*1E#*#K2%pqg09Kl6=a(A zIBY`3)l#3^Kg0(L*Sj?q9@#>BWGnvuXA^+Jv^|FU5-VLO7*ed&e~d!GYVCEkmKqFw zPKIDlgZLyyV{BD_iZk(eajhzj+_A+42O}51S<<}LH&cPJ{K1AuBom6y1qrMhOLtrQ)R??$KbC<= zjqTRtQOnf1LP=S~a`l&s5`1*eI|Pere+X0n{WD3og5S9i00V9=`2v}`ML&=Pxz^JL zaO`^S!5}==dYqIk+BsnsU=VEwLC&bOoCLtKi1!L?4N4gB$Dl2bsrV4NY4Z+kh3cxI z$NNs}biSf|e%__-gn-On~SvOP;25(A7Ra+8^9d+mqEh6@IR7w~5gE#MQ_n6Hl zP2MTuPk@Sin`eC&t#*pbqbUtB@*rlSh*+On08)@zOL zV(D=D`<2EERjv#%eXsGhrC6nipr(dymHRf`Wq`=4RnLTGEY;R>l2035Qn-!8$P4#Y z>OT2Ck~g$mWRJh3A!+bkTKIWpWL&oPgQv=g2l-kKntbMS@5AGl$JoQ>mw-pM4qY*< ztG@okbD{Yw-v=y26vOpzv`lf>(NjlT$S{Q>UrtF8z$@? zb{P05i;ep30SyZp?!<@ahnvPp~&-5vRFPHoRAp>Z41Z#=}QC> zwDP4Ov`Ap9PRkZtxn24}Yv@tYxeN(#=}3`%TgWgnEq$|PHscRwbL;chw@U_#k$UmL z`g1*YrbH~Hx}jZ+TUPHmqyNy~yX-(1{R&z|jU+kWu7kI)#!T3!8~dy#ApEM;j=*cM zqS09)X_$0>KTL-R!b7B?Lbj$K`?*v}QIjV~-)+@1j5Pqxj1*TVTH9oiG2P>%$W+#Z znQM3_&M65KKowTc)eCCJKi_b5LqkPnj-FY>2>=e!YNFj1UEAoRH05aT)9M}NPrKx# z5k3H(D?X8tEDr_v$#6aniNX|{8&S&%5U*p_3JtV-Gpgy0q^+ObXZjyaZ~4b`#DAOa z8Twz-DfR!)^!oqgbdTZLSqjmVHK@GX{2e>O62dt@DfN!JVk%#M_1d0c} zs?skBHd1ioP9$PAOOz0+Qz_O04ePpZM1I;krHuv;$N0$Je=l3 z8TrxgRNa{~M!31vZs)7B@w?7@-2e-?B<5UJ++G-Wa?Q8Bqu={XRZ0g5#RmF;9vu>E zkD7~5jU0oz&V4;p9x*;@%M|A+yAZBTvl4AsAIeVDc4O9M2$lyZD1C_|!B*^j%xb6V z#S4#VC<1X?YLy5%j*6nmt@0i?K4;McCR}p~Z(m@}ZzU{It|k{Kip%{H`=Z0#ujC~6 zt9EDY=;(L#bVZU$D4L7deeGfv!oFcv(S<*bair zeY>;gWJ~**$ogBWI!+4_IYC;Ke4q{>IQb*R*uI_Uw; zpP~Kd&`WbtG@DoDF{l_ZM!;@xQ~)sk91!AV{mH%P9Yw8^Ya_#BuRs;LDl7w?WeF=C zMmq1~%&dz1XGNc1Q^Kpa?V`V}SL-zIX}x`%_58|gt-&3UOzEj-PV14w*}fXdJ`Jx* zXO&iS7>Pq0t4ROibC^s)ESg)`9i%wVCZ?2H_#kTjUln+Kh^ICg zk-f05lrKFa(zd3Hzan_HDE@Nl7Gx)KcPpWTfWdBM`D+tvlAzh$f%F)F7(97)1FJP) zal#fQrgd_i@Q%<6O7`HM*~3A;H5sYLH6J@i!A6BrUcX{)FeU0ob6#}g*bqP=Ka0E& zAjM<4oNfX$3c8!1|5rj2{fH~fXL7LIU;)I0O5~!tNBAa)$!UY?A`5As&gj%jb{L90 zFpGSVgquVpN!w{es*bc)h+(=v%6m=%&xrveEyX1k#UP*oGHq)NV{x3%ig#Kn@fK5= zb!W`xCNQBhdmcy}B31;hwET5QqdZPwPtnl6uY%;R!IJ5Qp8D>ViQ62;`Jqr^4Pk{D3mt=-0z$ zSw8=@kZCYM>?pACxn_jFuS?!_ja$Dg9j*n|+7@ISet#s5$=))DUC)S^Q3mfhIA@#|FB|&wQ4@iT+5^BuCM)s?qDA6kjS-g+l4Afgt_f6M_Jprs4xves zhe=4&lS)^8NR;$+8+)e?Ols#Q#}$Ckt+UrwyT1qWT77>Kfk;=8U)4ZBIC4qH(J356`q(EeN(Sqqu|ZIr6EY2RW?LVPAO8#`nnoq;ZgvMkWJIM9`XGHLQ#>}$posk7kKPbK6ja7_3v12zO?P4r; zgn!oG@>4vzkzWVdzOoO=seW*dNh6%^%t|9Yq4@=AG6z_C^UY=x z=w0044JS6ST?;IfB_S+*w|O!Lmt-<+fZh_~%enx4&M0;nut#Ae1ywZBZXSf^(?XZ#uh=`#a4WEU zsYD5=@>8BGnbAF(p+Z)B8-0PORro(-uOIv$U+`R(^Eq zETmnio{;d`HfeJKae=pMve420PVp3}{=^O_Hnh_Xjvg!!wJrjsvS~P!YhdwX-#2MO zWW&4BbFTHeDK9L6`f3I>VpO3FYz0VxV6tsnlC!gT1gn;^xEO2LVV*C1o_q1L$S`Hy zEdjXo`?6KJH`qN=8Pk^O>ijVv<(}G1Xa-n(9hGQZ5caZP@}APa1K*0m7Js4JCyXxj zLy;QIo(duQMj?iyw)_MI4wzh$J_8jRM$#2G|%MgvEf%G z>WCJ?hM}U>*78UvBX2q#(TejVXL)+mW_wMTBogUub2gMdimuxyv+OU--QW-ts@pH% za)BDbVbYSTpvt(Zmh21{Z{qwtv}-nBqW}{D7__qkN>)0=77RxA*}I8)H64BD|4eWEPfllk-L0`C zt(K3zQxbDLQ2Rwa5NXf~`kd-1wiobrR*o3mCZi$TFMpg}%SL8A-ulXC1w6L$mTL)A zfrS{r?RsRx^7umcz|QnzgkFNEW1_z=*zSiyHJq)8e#l>zy$SO?zH31A9P~RK1YO%S zY4ig-d9!?IhA5v(BHF-7lZW=VBkZ=k*h#?+Fqt2Otis8{_gpb(x;F7; zarRD4TU!^hwKY^>6Rf_5MenAblw%K}tA9^E@xrsv&K~LweI548wpQ{_OF31V1vv?& z3YWajNP%*U2!sj_#0Atn0nok%lXVOa9FQYjy9gXMzcQ}9=4zM`MUqqp8q!7piL}W5 zUd2hB%{I5MUB*3OmK-F0i8KtiiTevFh^VQ~xa)N|HJ}Q_$#FYm;ql*!%;|!gP^@eBcG)Nel_^AYDMzI7?5(UnH0(UBv z_8vEB=0fMv9mP+88;-GFm`lCX1Ldn`N*%5*=r5^^;j}yt*C)2@z1E z!RtHIQ7Ok&iB(Hy>aN^r8I>b?;@RoK{la@OVZ)ClRSJV}(h=Hx{NpG!Bz3o1Dit5Ig{TDmW0V5YI8;Sr6}<& z9F}_7PYgNP-`pWP?A!QYUES*>hCa@H43=yop34IW6-t>cJh}r*-h;=BwKEcK)UiBl zVS$z#u$T+wG!VpHQ@r#%ix%L{<0Oom@GA%I75DX3q~(=EnHz%Q-dzOnBMx!uM=n+y zeho>#CESe>02r9_8HAaEoXm-wLnn5tlQl_(J9gJiE69>Pjk9NqoAw$LI9Qe;q(jmz z#_vTK+)^?mBm3GVBha~uI;t>AETR0)Q}_LQDDv>8$EQN@Q2kn$nT&sWlHn0m`5H1rCdFV6V(Y$dfLqyN*FaM^J+H$Z?cLiJZ|*>47~O>zi~i^Y@;T}1N8 zkBV}BozpPc@f?H4DR5b2w1Lh`9>pt^8nJ5ZpR+#lfbr5T+P2rW zgxecLu;5M<+&6^hE6iZBMWdF=b-BGEafA^d;%oDlv%}xWsP$G55g+;CKfz!|7~sz> z$QXXkaSU)6t#d7FV%)~pwQ_m!&*hVtT4LUupB2i*DuQ>tT1J9<34uP6i01*IhZZnC#o&yg1yY63=Xa!Bm z8X9;iH#Arx`AienS^E5enas3;0v&Y>|CA=QAJgIfZF=O{ ze@(~3__yilGGsz^j!vSNpKF$2i3?Nj3?h)vv7H95pyUd(Ziag{W&TNRPX$~3FkX>V z)=rVcvf)*widgIg@V|7J-d( z3I$tOHK2tNo~)`D*(jMugN8tlANv}QjYZm#&?tu=U{fNXo}hh?+Nf~2?>}ZuBf+ac zMlA>8n?#Z}p*Ft(e|0)Z;RL=wC7z^fK=0`P$Bbjbvk!tK)w}$; ze2{PT@;54PZdiy0KeTbb&gfYvq-~NCmD;+;M6zL%bQ4Ay&U%n&FmCT`Ng!@*x>G9I4d(eaS+wZugk#r#5XG1RWWpa zH$l-SHo4yJD^$z~y>GFfN+~jyQE#2J1KatF;O-(6tFjA=2WaVVDrkh>`T^VzUW-&;ftX&@%f7$t03-a)pcCqM$%(o}HrA>2 zaxnr1SCprvFh5EJP9pytse?YVQk2m7sVkjIG=; zY5mzN@)PWD<@L6DY`uj~eav;|QBhoSLx2(1@Ph9Mi)fSro=|M6hlm~@ckyCT`QEpQ zQGz1=6JLJPi$A(CTI<`{w5tfPA5<_r`v-&OGwKB^@koq6)NjN4U}hqr(38IN{KUXS z*^!$XY>UB`*D&tch@w&5AlE84y&@dpr$=`kv(#AGY~4UbrS)&iVS%}A8siV+#0&v~ zSgK%*ZQ5Ds{q;iU_$o=-R}yWiDS=~-&)ZSp7Xr5qL4926lUq*8K=PDvp}|>! zgp%KIcV|7AZT1s?7E*mbw@x}F-3fE)4v?}NQed0=6SFTBV&(=BAeFo1M(_Fs?~8V_ z)$!j>QDt?u!fEqMS!tZOy(T=2c@2PQu>9Ux&>!I+-zP==Bm;*`vv2a5%ch5@xh)|! z1FR|GP7c?Sw@-9COEudD=W;RlDWn+GUAEVyC0CUAP2}5SM!Wjg2)1UGO zIzGIQph~pMr5$6#Y_!~+rCJHWkM1VSTec#1L8)pA+=JslFr&qSY^25z{W3{aZR4j! zf-VCWO-~X26IN`soo#&PG)EGHztvqAESKNEWZfQw05; zDphEE7?91Kx{RY-Hl(T4)R^ZW;2Z#8SlS_A~bHEr{aH&ns;(&nP7L8Ed zI2Sh#gd_jv3lg>A1OXGy(;q6R)K4&?YPeyNCgkt@J+AJV`j87}c)+|K_h>Ng*L_J5 zgya!c=XdNp{vxP0b$G_^4t={Mp5*f5Lmv+Eu9bQbzT)0W&>g^>18X;$SrOr-2^oTE z$15N5sfF)I9^n;X#w7X77r=#25};hXxjg5`;=O1PQxjP46nl!>0D>ab+zw4~)bAuB zmU&=?(&fVPHaCYD3KZuK9meMReMSD7lt9iFXUQkD+1a3E%IaO#BB!&^IFE5ETk$lS zpA-2%n%?n`>9GGcJzn>}rVp$BpXts2$?0$jQOX4;Wd;z4XUtAh$RSmmf%+i4ISbsG zS*of!#i|ik4+blm@en!csDnlX@NRKKUOaZ1@&@Efk(laVGHJD${qh!?*;Oa&g|Y@k zRh@db#MCI>&2_uQwrwy_k9n*!RV=d~p_-{3JzCZ1lKyJ|R?7}x^&Up6s6LIhQ|**u z^2+8FHuzeRc-64}Dm8g_-Y782%|oy$nCWw*5gmK315>nTZxNF_O0JNT*rBCyLjHaR zldoP!%2wfjFxJEK$DB&|gn$~fo|0UNtvfcHu#}Vj($OWRe|~u1`;}L<*hQB8CJp5? zCH)KGKDI}C+Y8~-KkX1Tj~Pbwj%7_lux8QFNzN8V~$Ou8wYZ8}oI z&?c%Qq^DrVWm1>X8TL>fMHoaE5^dKt@aO2EzkL;8liUzb37eSqr$?+Fs4KegY9p~U zZmAJrL7_Z+-kGp?ceTdrBzzamwBSShV5LbW@ECTw2fegMC;Z)sojJm)U${U? zfZk`F#+%`SazK4!m3dNVdCtQz#^!eblMnB6?2`oS+A&6f3zynLMJ;GNh3Y7iMNZa) z65lX&m_2Q|V6*QTO4X0V9^iG2jMdCgV$x!L?_-Pq_%lwG%0kI|kZe($a z$ah4xF^RzqQ5sUa`i)1BLzG9$tj7^|i$5+85IL0BIr0sV7CAGkQ5@;&VCv+_>JBiF zMOrP030Q1R7@Kf9YD4oY@pF`KpYOmg9?hjjX72H7VPIg6cWB_?ZTh;s^+S%{{^yjF z2={Ljciq2NGu2nGLf1InCsUE#m3b4r^HM~DjU>mGVwTTXkZ#xiUh4e;wJzM-Aw%hd zH2OPIV05m7tSE4!AW~a#wNfa{Qdar!wJDV}byJy_^cW-$hNlwQYqgusd!k9w!|uCj znJ@f0Cc0w-DOo4NTw5E=z$2uO)nz{`O@2>_msubbH#Zmk1VUm%m9d_+<Q-5$ghaYTG8 z;EceCH*nh-s5`iO5wQiz926RFVZVbC^V1)+z1p!@F-u-wIo;X z#>z2YS}HKq!yDI?8(&=mad-N+ssxH2-R2C)RJ@{aeSmH=Tk>iz_6tSI0 z6yVh=g?T(as6AqrDjGKqA3`~|* zsk%qQfYF!;c!bglh{y|O2yY1oops^8)UC_Gt7X(^W*^d|CZ_8>nXeMkI%QqwA1>Q_xhDct)?>W!SyzXkhlDo8?UfgymS!5M%9U-Z>ygA$ zvqTttWfTrbzylX^X`?r_Aq(W9`F0X;TMoKPQ{Z%O7waqsMp9Wwg47lUBj;=FkQozz z>awsbVqo3s)^6cr0RfdUqdSV=x{`E)vt-&$mOG{Rg=zpBeh#uEg8|Q=D{d_rm}eix zxDm{xg}_W{6G{DzVi~XwfOEd)FcrL)eyDgzoA7$Ni>Rxj1q$==IUcyDrJ43Lp~CjF zmvbGwoZ!uCD$VK>x#z_=C4LaU1a_fg*4J=2di8IAbNq?A(QCCAk?BNZe`hCb`6Yhr zikWf^4z*$B8CC9E;`H{RtP_t%x${4o-uaK|F#k3^<@bM0*Z6z!{Qu99{$si&QC6TD z4_Hd>pIim0n~D=>VB^roK3n<)Z*b)m{TA8i*kl7nUm%b*rji4?OYF0Lza`mQ3#>7+ zsJHpQJ2o!LUh?$3&>;ywK0V|Em`MqwErp=kxz;|bBQ9ln7awUJfqo65(;egO2W#kE`%!@&{E zPW54zQrH*#y~8AMi}L7{+g4XwC@91YR9hktGy8V0hntimu%QFs&0Mi8uf~g$b)-?` znfx$lfF-A>Iz9b+jH1vl5%%})bhc{Ak0CxJrQJB{ntABsBVh=8zlfz_HZBlcVc=!Y z{-)&UUd)>bUtNH*UzlO!ukjh&<)>Z`q_$>SK!J(yPx1yi3WibvR)n!&-Iy}uHFC&B zuWb>X)sG}C>HSj>!#Slx;Yb+ z@|A#?h61|5(xCe06*kyWa?GfmFGJP7_tQH&`jVkr6UEOKCQdO4MAWl0y7vi%hy)G7 zllf(>qL6G^Di7y6nR-y9&49sAL8zlW&fz#JVB_>W^hQD^UZ<)`Hd|)*s-CxMc{zAt z3AgnW?oCb*(s9+np;Wq-XMw%&y2oW-xmqwAp`q^9yf3A;h~A{^=?c zI^ct4Is;5O{2_KW(w!OY$*SE-3b&K+is=_ic5gu-MgfCLt>uu{P#n{$jTWv|@!9obxET1`jSY}#W|MfJ-6 zi{1Q+NfkdN(tUI#Tfkg%lZ{MeAFcQAuFrNBsnI^IlqTL@A}7d=zp7@ zCHY^|htvMg^w$66^!3-CUynudi((+DgIpu6g8)+JG$Ni(Gk11}v^Xmo3EWYOP5G^d z@&cXSyQ?`WX%kQNp*8}yc73w)|_YC4Q5|8eYh22Lvj^j?eo@21T5}B)i*SImw*~8be5wKY+?PL?z zD~gU;ja%p>PV460mStZPSvgeX;i-m@LO;(yB~XE0)Xk^uJ}>c-Oj4c5Zx`QH(ug}g zX!5qw>;z^hdKL1utMD$$)Z`(r`ng5f1m|lFJW@WXgqx%&6R%SY+i)4|dGB z`MA$a^36K4+`tRqPlc)c>02+Jfxg2HMG8V1FibTPbmlZe`+mxZe`F~wd$-AjsbU7Y z^Rnq=x(9)Yq0UsT01wP#lRlo;1V~WQlqA_+nlnj>&?;0+#|)2j#5im}h8U<^htu_k zblg|fGzG_0-M4k0zE0ep(wL!=ThfyW;6CJUdZ+s?aOr}_F#p4Ry(F2~^VY#-E5UpO zu)9L$@;sX%uSg5lK0u&+;ZiPuvE%82LEwD(tWHcVn0v(I2~Qu*z4~!cQyZdiE?{?> zmO&vJ?m6BR8`)#aQVw(9*-=h!b`9p$dL5%3aIRB@1%H4IUt6t za!f=|b+(3RF#HOUGHki6wuT0(Lrfhi_#-FQL6MDPnOgTmH~Lf8c`0CkXYQH_swF}r zwba1qQ(sl z5J4G}%-=}G({!Tl9$PH|pC@W*xt@h7JLL3KvRGM7WqaHXPG(`aUA%RAj_gijdcIYK5cWjy`DN1> z>WaD&8z|Mcu1`!oFmN6VTX^D!wn1l8D$D4N0O_NpMqji%yJ^9eUQ#VG`mR!OJtCW4 z=%CfenkDvZ!Ww4xMqhjH-b%(Y0Wuz1u&&juIEg`&u{uak72*DEO$-el4zHCyXBD`h zHa{o^9&Vg}IteaW*~UwB+;_(e|DJ|5woy}cwd@UR_L_;u_tMz>NJkbj!fh{k9;Idd z%7Tj;enAqOj?H3DDzmG zrt0|B&5vt@nSNAf)-b=U%N}4CqLHW%kgPw>xfsmAzQLK2)IxahT-v?uf+0d3z;IGI z^<5cSi&Ok&S8W&bL6{)9NTI4(hWjpL`S^QDeX6 z#)0EN)-&_C@C0 z_C!A|yitQ++SA&@9Mt%d_M_OJ$UsLtQ{kAdR7#tg5$MfyI|3GEQ*@J#`KchFc1GCE zxG0<(V%M3+=UMjFiOqo)3!vNs!1RIsT?tlxNE<8qD|z^GQfT!xx$hogY+x|=0o9WjZeEj zCF{Jb=Zy)<5){9$6IqLEJNxXSjebZh2cyx;A$N+cL7=hrGJd!DPKs>|I%VrLx@&W? z{X$zsN*hUx{>$If~*3H&Pqas#|eYgUgY{4JHhxTD?qMms1B6uZ^I4gdO&cTM%+lKEmdANizU7umBs&^*cqqIn2Iq}V_ z8q?hr77t8W9h8wIt!Ay1T1VXO`uM;sUp#;N^J+_0rSyh;Px6fD0D4kM0n|L!X;>?D z!*&Vjs@F)~e5MGia0HWY)kuj-5&-Ae+|w3q$Qpu_R4>|PaHY#dK!zR1SDlmu)w|w$ zVhbF;0X6-8l+Ox+?%^hF5%*m4ehjl$Ky-EE@)x|Lq3L6Q3xtfG>IlR9)i!VCf_WlL}3)A%mj3?B~s}X->qQB!2WN#IQtr}1 zBna}3Bfj!{0m~{oq!vBdF=1?h7wB_Gsh+8PECo8dy>)ftLr3{p=0{{~pyfTlGa@;>brnLE7z}|TIQw1^Em|F2o2rWe%G}=YRSWEfF&^x235)N z;*%=zMQ^8p(bXK2`|sTI8Uw>+3dM%3p$ zQysBQu|nykx)LB~Hb;O4c4KLuiX^}+3%c^is)FIHtw?w;?Y9G1=*WbhnG*YgzWD-8 zv!5@#W6D|7L0WAZC$1uxrmV8{-In*e73-sdSSt!HlMDCrw4<8(X_*0ZvE7#oh%Aw- zsDpBpCxTYG--?ML(bBN^Pi@yZ!(Bdoi1F*AYgN(Nc$^R*WTvG*j)agqOb9&oA4>SN zN?9&BYU@B6b%u)to?CPc)mYYWMz#ClAxO5OYx4Fukj0osw4+-XJ9$J}>pj~C`>i1I>K2ssLl z-vvLn;`8ww@@k`61O$5_2{kZkq3g=;)b6~qT}n?!48%5rZ^XbEsQbIzz0+@h^ch%k z7w+y2e%3FOVrAFt_y-;co)G&xDLAp|YzRr>aluH@Va-8Rh!O?@ov_4~x2qMf^!A7> z311dhvj8=0{Z5{5xwQ(!@+cCpECPYF`t2IOu^RVZI=%BRr$heJ>BTI6JDueJ>?3XY zSDyY@cK<@%&@g=j`2=!08yaL{QHac?j!-OL3d95PwR&E>q;{9J8&;ej+*BRi(N^owV@t|-rOPNK@Qz|u1Lkr31g zN2JA~7cE|8(6V~w3wm%pc%LNQaD-5&DOCm83WL6+y zi=0GMSIqr1=-aJS|Myp_t>4jB=;he+ZIGjl@OQcY4h3mt@pvdYy)D!0J9Udg8~pDY z8T+f*jz`$;C+e8PgI{pLM58rt0J20gvJIuCWs4uhmq3za@t zoNHyTWRsre=n8{~_OTJdH5T=>U=mkM4Y?{A!-?(-iIe3ivY=+W_4hQRVh=~iCB zSR0EU0DjTNZ z{%KPtl7ZDfS?-hQfD`@Q2yLnsLpTLXLQsHPug$s z0fl5sAdZb9uWi=D@L=Lcg61J2yDlRnBt=R&Q-;md(!2humtX^$>rK&i679~Yc!mUW z*;8$lc4trKkiQrh%n66vMVR%*dI;BLA$;^a=A}2@%P-pXc)o6R|A++-g4n#s=u>Rl zHrb5Mr+Cx-G0+JKa1>rZ?WEwV^&wdJYh)&2u?-udeCjn^Jo@T76t5U?>QR;LJK3PQ?63B=*#n=|7&W@{AeRAvLM1c)P;#>i?zF zyZ&-I#6O*0QTeyiyQu#+Pyfs5_(qd`mOq2~e{QMVd?lDA^l4kjxrT@uRr3MCL_iZX z*x~@=0DA_rXdFhTAmU9;dd=dV$3giVazHYJ_wWrf5Zi%f6Ibz5)%7?0o zE4dpTrw1#z)FV653i>s&y^*1IlX6xL2LZ^WwPoDqko~re*%@@F6y}h6^70(p5?>9s zq#Vr;aFCMaDL9HV;rViU@-0GG#AqK->IfVms?Ci{M5xE<%P8f`*n%dMiMcCntXxy# z@F35YpBUzpOO2CTfc<@(#IdJU8WX7^RY?m{!0i<4kk7pT;L>OML-{u*cd83ETOxbn6!bbS5Nn&WeYDMZv^ccpw=REX+*!_2$Bv)PG<=i{7RqaMCB zt)Zg(SC}l)@4dZ?{HSJsme0U2>3pwi*`+4*9(!xed>P+Z6FT3w+-b0;#=St_2j9V7 zMsgb-(Ya@5MI$}7scP|zB9SCxB?Z|{Om1>L_S|Z}u%bCr7{SULKqGl>;Xxq8V53qf zL%Ajy?AE)_e8`(!L)o_>my)43cYhlf2M0$*o3-4m?atDk4m3_5Sp#wi&Z@G|^U?Nf zSjKwDrX-0(P5*y0WdvSNezcO7d{j8)S$ z?rzc|41RY)*gOa*8*jb@0LeE{EaGZXP(Uei`^!?B-FVHrKOYtj3jZ>cU)f$lI-WqA zTV7C3gajp~GTc}gM#9&26y*$)=AAyF?HbsPV+k>2c_Ou={h)BPrSbLFSYxP7_?5OD z!Ly!Z=QPon99Dd*f*E(NamYu&IYwwAkMV;Da0;6Ks;GX?kkWa5n}Zo36Wg2Ni;LA1 zz@r5k=@4PRX%+}a?35FQ;r2%`b9Cj_Li<-g&$FyEv=Qs`P*4n+7x(dv44Cs4Kaa+y zVU(w-yD|*WyQW{F6jM0&5(2nqPQ`+&P-It)o7;mh^bA57#8A}@Tll%|s%>l|eOYu< zY$qZ5D(GABY#P&uhx!0Ox?#KsdDmDp&N|#K*MqP5izd$| z`li^tkr5a&2o&1|y#98&3;+PYzdq;)AOJvQf4%^Ki1-5-4}=>10BGm{`13~@ zVE_Oo$s|4iUa)f{BYswZe|bIR0I>aEKKEi^`|?A8-wE*fAE*Dx(Kr7!Z}-2X$Nxn~ z{dYQf<{$cF{KWs#H~t^!sejQC{+%wa_lG{>!1TZL|CeXa_=^tv?{xLPKlD2N)Biht z02Bb=U!VWqxr_dva|f;bbM984Dk@VgP429+Z>5;+{ zUKIyLLZVKmT3QSyc)FF{#-efDJ2e=a>6qIz-<^^zhU}#BLQ*^KhaGCNy8Ear_I9zO zVQ?Cos)MnqdElKLdkP52Rh`h7Rr5fWAaBoqI;OlvgICGQZNGu)u0#UFG%} zV6ToppBeIJ`OQU)sQ{VDIy4hqc_!I|ra3b_E@wer&tgh0bD zaWn*`2pFEqQI&x)oBXc==qmC*ZLgSFg|T-gifcDuyTCH!zGpCl+RN$}>60_7q^yS& zcaOz?`b@fv@=D)cj&&MjUBB>cX2rH`QkGm;3?_KRUq>$p`1|!X!uV(PCGm!kB^UtV(VoA?h&ICa$fUCQz4bG4^pOMmipd2S zmrs@>Ob(CWob@~e@W&q}ch4l`dTQrh(~qW&-oq^U0k;rkpsZZ~2XB&q1uGhq##nMxf}#!FSKI}*xtLv^{33GqbGdi>yG z4#5@Mexg0XTWEL%fUWlF)m&6ZzVJa;H+LqRG-`AW=5?CSdozS6ss+A9#e6#3fPSrO z+gK{434;9F!g~gUU^-yR;4Nh)`)rQ-tJ_n!6vs=u-)BepZ@xtB0 z5A#gj9dv}G@abWpT`Lje=*?iyqFbG~WxpG=^H2)k-B~rX-lqQO@=IVcOzW<=JiP<$ zqqPcPG`{lMV{|9eNvdlwDKICMV_(H#7+cBxz$ObN9BNN)+ZQmv+sJ{<0K}(Q%#Xu* zS|P7LRGnQKS%?^&o6=#8d#ecyvWnv{3KBsc1DmaMn|o&u53l!m8bExZ<&SG9W;aZs z*zIm1W|=rj{PF!1>eN^|>!ifA>4^uEZ&HGzu1!6OC=aoB)clo&86MvN+RHu75Q%)J z=_Z}!;zcqe6tH?yb~Ef>in&ZvlrHT{JL*?0auGH^!iE7-M^ag!HDat6_$=P z=6!5py|;N4>}`mRl2W=Cu0o4r48sx8z*Rrt)Z88erEQiM(eyAOX!3T7=p&xQ&K(50bsLuE!;pME0wA2W5763 zl148Ia6mO;%e$UZz^Pj)G(2CyHHu=C2;2`7Cz|Av~A%P~mrgP`X z^=0I8V9s;culxQ+2zO~QF;*6R%5!VW{#6WF;@I7g z_V~IBjO}1ob6j9-8G|Bvg4RPdyOvTZ03dclkKGjHfpU0O#P1`;;)cOpn1$5kbX7GZ$b2eSKfEx?6Zp8MqyvMRbDm))1QY50yUv;GUP2~p zObq67@P)tQ3+KAgC=PD4H{| zK>oMH-@V6Z5^t^F<^Z(`8p$T;ymP?$fT#e>5PonsD&%?`Q9n)I@@J;?QF~H*L9i^9 z%|LRyA;19ff4-ML4fmxH5tB4a$4dLSD}cB@EGTe`zny-_@lU6>?5G8&1UpS8#3&T2@5B3V=2!C*DI`1G zmW_mf5(wl9?^sn0EIu;n8Gj%rivIvXTsr@e_9O431?ZWLZ_Y4#KK7>R1vJimq#c#q z1Xj95emCFhDvk@>oaru(M%-w>O^NY>Q0llB;CsN~XO(oB^94vP9u4B4cv0ZgT`(MD}X#qHkYaB(rNvid2tH@@V7T$O6Ey}X zS4b{cSzYAtu~9&9@!pc@XY4SnvP^^Dz?;)NY!m!21%DAj{A$tNybfddtbtFqh&pB^ zopqY@rzMzDaFbbm>KQR$0R6!;Bj;MMB<7T1aU!NF!_yzwC1x|Q4{M|5F88i*^>^}4 zej@m0stF^`g98p@z|taL^AEiJk-3V)LtxLRCkWob+dK@A$}rEI>;r{6xLZ@(CY1)* z&&SuaM(*ylMpUC|O%y6DI3zlo#ne{moR7$v*Su6aqp<3e>Yz5)P46F$%0x=z0jdCt z$48PUzh3P+xW+f-Z(6PPGFh47_RXXWHTaK*EJO$ZMq|ISklq*JE-Lj3GAqI^th4buG#HXNx4?Xbz0ai2;c!?x1(M%QIc%doOv$h-6h^;|{q zuaPuWJsN_~5dpz|a?Jo{fo4|B4a8b9LYnVdDnb;At^Wvs*mZi=ZmaZRU@kp~JqtHQ zbODR)^{+d#Kd(T^KjLJ?_!?f=`zk*A5lXp(zT!9T@0c{t zyx}l1L5wKOtwxB9jAUP*veF8mXlviZSyrK+l&YniA|uJldTSG|Y{zLT#J`zOW)Eu4 zF>}2U?$rQpQpcGM;aq3hqV2w(9)cAxTco*RkbkAd&0?vj#0Br3IoFV_j~R>2V{#6@ zUY84|+`CaW(x<9FE^Zq$LqS(qLlZGDv*sr+)Q8e?7Y8h9kqo8PqwBVn=Nr)KOl0=y z|15$_s6f|WR@%+23jph)wn^3n@>*rlv0X2x-81(cLA@gB6XiZ3IR=05wj%VNN`j5L z+aW6R3MfuW5}4{%lMXu&%S}OW{FI!_y3DgB*sUCI#bIfedh1rw6>KwH3FsuiU>=~F zPP*MIAF0uJhsOEv9qM!7$C1w(#3)s@4jfl}Wr8rSv;xcBs0$DcDA&ZI3PJ6a#&!`c z^FVNPLo)5)Q1FEVTS7Qxt!&sex-a%-5O!X#as7gpeDfw$yqdiTzv$;+@wC$UsHdn< z{2ICNG6F~_68-q*D^lqI8!#RVF_{sd|K-5|(*&n(1qfEEAw(zO`&u0H*1j%RSm7AE z{&pmWm@J|&lmT2fHrsKsY%O09W7(3(XcqUUe-8u0Wz-Q!=-gDW{ z3yc=F=NDt*M5J5t`1Y-AShW_Ppe@71ne@0<4+kZXE5oi~-#Ii4!@*cm#J$~u;SjA= z9Mess1VuSTk{ba-TBJF7hgn@e8yCV0t=@oep(Y8e@8T*VgKI+YU0r;vl`=aRJ_yzc z$S^NN)s1XB3W^)U@~gro@syVfVYG(Q<9^_Jl3x|G%%YyesSwU5&nsi*Wf;QS0dilP zeR*>v>#oWuelG2+_US^n_R6(YpwQ<1e>?p@ta)YsRoQ|1_sWh1@t?|0s(kMMy|VKk z()0eJga13-BjgX=k6-y8^y>fe+4KLRgZ(?bqVf-&4Z-Pu>05tlN40;`|HB(h-(S%V zCrLn(Y5&jM9~uP(RW&dWPZ5gt&QYpJ z$4v@HH`*7~{V$4kCG|t8WPwJtS<`84m<4+>R;Q8F!q^ANW@CLW+x%!13xGKVQHSx^ z>YPj*YF+*ly-Met$nnPC<&BK4erSeaNTRLD9cr-v^u>82apotP_fvinDakLMu3h^PlbncINhA>d7sX>hBH_wO^z^t7K~aNF7i2`7QPgoIX`c?w z!?yrr-VH0}Hcs+k7fOU`_#k>MYL1L9S(6_N${`FlhbiQ8M-XDgdZ5AKe09V)(n^A> zb4=+;-GbD_fxUVsoGdO4y?t=d^5}tV%tDq>d`3tw*>U7lSE$l-i>SV87iDh)aTr}4 zA8*R2vCVd{r?GrV376PM3sMPR07A1bAmgPuNa5t6K8P^&SsV5OJg)t-S2@&uhY~p( zyzU6Qp6YnZSiLj3yNLSaYSblp*anAnY0Y^oFU13owXt2` zfHcIql@w2rt`-&Uv&9aPin}| z%4LM*YlmvchU-xk9MTjFvbzaA^&YF%JCMqK(yWqMRrhRRuK>po$x@ z$&gmP2%QFSe#_6aQj+X{3r(5*I9~g4>e4$x}gM<7!ErsOj1W@n^rqeS_o{X0#3Rw2H?7 zf=DktH;Bakn`-7YKWZ=oU8XkLq%lGAs3NZiP!$CAj7J`BjQim*!LxQVaqFS`BDjDT zJp4J>Ou_XVA}whuHdu-t&S%gF`QkSxq+TCAAj_4(2xEVf^cl_ayx7Oo9Rrb_M@K0N zbq^j3)yo{k=a8k2DIr3biq;PUdc?^O*peeG!%{RRG-!4nsMuT$Q)I((40hGx0?s$4 zY@ZDxu04k~U#Cnb_(e)#dcB+$u?3Ek)sa)p$AjlF=0(aY z&7DmgNBw@sRIiQmz{s%IF=k!p&3?X_3g>8QBSDDSkP+P4uW${-s)Gc=lSfaU6*#_H zC(l$u&thWTSFPoh+mj?UZ_b!PRI1yRrQ7Ps9-}D0*3hRL;_lMX($CmW+XbKMi^Y6@ zxW`#zBuLz*Xqaa&WIq3DJqj{@xaOelTCAD`RsftZ>-&Y~Gr<_gp|7DwRk}Wqe;DN$ zRPv2W7snWF{6R>`z}1{t&D} zTGx;7#ixtkMSHjEH{#2xh|G~vS0@xv27d0Ep|;|x9a2zPhy32znV`dVG%*kOqOQMH zTIQ@)9g&)$*E#bnDS=#c?|Q-lTynC6xZ2*XeTWJF`R(tL?e8b_neH1cW6#$+?GBzF zh4Iw}c1A*aTrqn<5%eKG>$N#iZ`_(0pr)04<<=kuCG*>cB`_vP5bPCB9i$}!J`uIK zUr1FPtaYrG%sG}zlxeJQY>hmJ?pyRre3JF`Niy`&3w}Ig)UHR9KEJrXm4QITqXlHQ z;jXM3pKAB5>*V4CnL*i9e^G9xK1?;-j&OmoeOpNrmibW#iv;)XbccSKZtrINR4@!B!W3ovs>CuDHpcq#Z)U zIAu6tBJ0;21~Zvp{HxIn z$T@tf@UhB|hk9idkv9ie)6D%iQ`zn1850wExVPL+mz#9~2tS>c#yb># zi%NUv#h)i6k2Z^*!EIUXDB@dT#9&Ret=&VA4`1r%p}- z#hG=vR`rB1xFqvcozv#*&%}8u|LH#cbaB$qI0w>x$sCC#bAL2=qx!@`vlr}fS#OQ_uw8az=3j0{NrNr07ILdOmUKz#>fQgu(c!T1;J~OsPZJ)4j z1VCUz=-Eg$Mfgqjn~_$us6(z#FR+tCv}nD#~PTjpro?JOm{7Opj^IzXO#8?SjnZ`JN# zsW(mTC{4Fqjeq(2DHMhzPv7K_+|Il+m?J6Ja6RKjp?N?yn1H-GaI+SLE%`pt8|s} zitWy~;MWGLN9hKx$o8lxXST`K-J%m>M>Yx5ZjK%Nm`Czn<4Wc$EFe@uuM})o$ z0vX6!_H*{kgN}0r*5)~Z0kU9&P?Fm+tOMvo(J`Ln+foxLBV-@KvQ;{}%htADacZK% z#2842ADwLsvJk;k6`dmf_?vY)WsT#kb171@T(27@|gptSDuKuQ>@53tUkuB&5IsIr}fr#{c*n!WHaBF!C2iWE*#Ly za%Z_G!@YhHF*3z&FF^CtaQKDPS<0jofFny6M#R9$jlg@i^k3=y$}u=*SqWD8k<6r3 zy(G?Ap*$=g_`C0z9Mh~Ow|RYj9A%cke(n3fk0BS6%98O2P_UTN?&8M=cFh((OiiA8 z(P%~kmz+a&lrUQ47`+Ae-NU!+C`*tRd(;qp$0S_V>-6kF=n`WuMXrCcK$o#oaTu>B z(HL1*YN8hjm?$^Ta3x=?Rv%J09rBbQt&#Cz+|`f~JDTkIK`OYG36my5yEQPZKH zfl8ffXCJz~BJI9efoF}>`zlL6=te!Ns{%dDmGTvhjmCmUFF*(CSL_? zv$6@Gq;P^5!gWY2`T*I1UY?Z`u(!J~G7+g24D=LT5Cq!q?n>y5tser=?VK=iIsvmK zqD6bt_o}oa?p=(2sUEnbYrXs2uCmrmTm8F%ht@D&5Hejycpb6&pC66oL4=*n=dBuQ1xy{H@H_&H95k(_8Q7Usb84b<}n zgf^uo#Pn3Hs9_T2Oo0B>Os7-Lx*A^ltWuyjOh>-;?J3N;@A2U<=xY_v&1oJD@X@f* zyuKh_$Ipzzg&JC*q3-mxrC=X4z4Ub=cv&tDVX@PwKxT72#ZEO5x?9+bagil#Jk(e> z$N{3FEa}`<%FIp$#35G;)JM3p8C4U$j~Tgkk0Ke?$bI<=Y^0p`?2_LdpxCkd2QP!R z*)^1*-Jz7rMF2~nCXHuT7h>#08sZnSdRe;_fRhmc^^EWsJwulpQm4D@o)u)#T6nh3 zpXoSk3EpzF0K8>|4ChZ2uMRg~1EH&e=Gu2l}qCsuD;w7eI(W9zz1{*E*S+ z<`_HDfk9bjr-N3jgo&%noX+=T?vw?n-UFn@MOFnAI@a$w^^7~)^zCNdHHA!s%8~Xs zO0Ii7;HgU)Ak0F{!3$01>>!RMpOJsv+!4#g%sl92q-X@l?WcN1i+>GyB{`PqJdieP zB514~NC-v4j%Bq!v?vIzKEB7C`uny=g$Cvkem|+27I;&&RBYyq4l%uzM_ocUYg|W} zQ?U12!?+8i3E6=$O5DUAjb0eD#~AW5(?;9g7{c?tnV&?kmHL;K0o=eTT-!W}8QpFu z1l$k;2ShM%ZOuAYJo*>m!tuqf7%hofxUb{m$*Uk2MP#J|G)>mHjydH<>OBg>Pcobu zw;rv368E+c_l>K@X4Xi_BB#qySa8U?z?r<5Fjm>bvHrh_DAG=D?r`>5@OJIuUsLvV3BP* z3=>GgO4&3=dBd+-df=7*ME6^(?5ik-b56@Lq;BCjEJt5NKf34z4#-4ih-bB`NB3+V z{{dM=CS_D~h2w;qt@cBo$@eoEfuDm3&jRXanl(>P=Wo!9cGg=XhCXX({qr3#Y{=u> zt-&u8*>+*;Zyi=0BwhXU2cm4b?59L~{5%;6mzBWWo04bPW%CVnC#e%b=+woqhQKqK zUols|+g4Vny};Z>@aNy2-J~|54(|yuaG71Ag1FujooMTn|5WQ+{m<6U;FA$*rCk? z34CZ_7Vo>IYJCQ#A`py97t<=Nur$`S!w}ELcipCi0$@jD2j%c_h*f_69+Qxsz4kZ8 z18dHigf!hTY9loPrt!#9tAb)tBH&_};XJ*ErQydr?CX0IP?<;NGYxW!dInWddT*3z zd!x|F3dOGu5V@87-&G+5yy-Zpd1)%-;AdwKHN3PYKp1^&&kb-%s2g4XjRTu$pbk;w^SRMW(L z=i~U`5)9D06koqF5$c!=x=rHU6@FS#pBYf(uJ+KgnPswF`OB4jX^dHzFd|KA)@=Z~ z=*J8{o=V3mj#R2*T@S@eSg*HQprVGd%l*afIigWxNzVwW)k?9%wrPa(=JrI(491ay z=#kBr@yV|aw2s3P%_vd26o1y*T27~^bum*AHAqU85Y1T-`L2>AtgiExFqk@m9W^*;!%yt@k^8YfZ*x>^f8}4 zl6j{xT_t5q7yuq>QQ@&=Y`_E;W;uN-eerfAoPEPpC`te%jpl@8;_qeW9^ zoD3U`lbX_!HRdN*%Gz6`5Kfg73#RipTa;V}gQ|iLVh~y8G}bksBwQg%BV9e6u0XmK zYz66+{qe_*%)L3lmbYQAJF`wl4Krvxo&{Vnhh?hCPeo5E$8l1WN9u$za$+dd>a`}t zHSi;x6M{x$7Z6Lx*By6`3^`TjbF9AP9ZDmEx1(HtE^y;g#YCJov$L{-rotXJQ4zu% zFd{NzNOZ5aB%F#jK|a~vixp~880VgL(t16q&&OQ#S2%jqNE|GWik%>C{3nd*N!ogX1Oav%qH{!u|W zvGu3#&eiNU*XrP%0qIqD2<;Bck$wC?8Va%DGYWj`(^W1U3FM@ONxUf)2KbS#*>UT5 zkf+t~-nTYib+m)1v7NCOS!r2+VQH7-^7nvjSed#C&sp|z0+ym8zZ|`IKIvFo@{*?X zfrr&{gtXU@)@3u9v0TQ$s{{E22Tw;AYS?Z3bRm?E;$Fd1PuJazRKb-az)v4OkQ>GH zr-yGBd||3LM|~y|tJMvZj+@#vs~wUnI675MaN+Uj!b`b{g=C*6T@Ml-E`Qz9*jzM# zuGp9TL@*H|{myjeQx~oRwe+UeEBUEu0(>Nsri2q%+_qbrglIwRDy><|+V!NK6)iIB zQ3cazb#-mF?HpOIpl8a)sqMG(H~V2Q?fTb~!OB}GBh{tn;#!TY`E!OEf;Y97$9f9@ z2DnQx3IF)qc4N=)YH<0BYlQO_hP@~*u&KV_n2QWt!|Q|*Ydd(^&qZBXEf#Ap0P|u9 z|E$4qxh%|;!M;==JG_pt$SXQ~UPAVG>_Zo`Ts~wp_*#SD&#U5u{^_|rS&=FFI0C(T z_ds4?3Yji+Fq%Y+OrCV`??S=ukMt9A^UJd9)$HcEi#`!mLO29;V_R-nP)1(3fc$fC zN74DJvMKr)itmA2ef)HCQ0t;&U4a6FYYn(?Lx+(AD#m*8plV4kN6H{$)U}i#*tFBn z>+}uySbV{74?h-CsBa|;Rp%fj*h=G5>LT^bCY87!&Ho=|?-V8IvTf_8ZQHiZO53(= zR@!EzQEA(mm9}l$wsCUK-PSsL-gO_&diY;^`yZlxF=9lF-Ytdk?4XswX+e!S$Ysg= z5qGSsPS31_`a#e554~HbJH^|@!ez$0tV$nEr>28gEq6|a z&Bp*zy7LdI;=v%{JnOnjKOL`R8>b6UIPOt7M`6&zq!qljN5XYzwtyq!C3lB@%z`4? z^qio?!C<*TqhwJ$OOw^3-#&y+zJ&Lewd`~}Q5vS~F2-9%?`^X*Qi?dLcCU)o5oikf zG`Bs{1tBD13R|I%WNq;hgc>F^m6s!zII-29V|4qLCom1RQVcw zsAZ%<9Tx-~8KexB6X+Y+1;fkxhlP0GEEr9>*A{1T4ugI=H66lw4SbH5*^eR+^tw=r zPff-~jrpoe>~#ReV|+m8j0?}EIfT8tqE6`q{T>G1h& zl%68mF^qQ|zh1j|>bk2zekOw`JQ?S-?CYY6wNrRCT)s^|=ZeDskNRsj>;!}V(}n*~ z7I*j6eU5y1x=MT{LH)*fdqbczoUf}CTnz6Z0t6Ps;OI_r;xs<`ywufFmrsILYIrw7Akb!LPU4@R8u$^C-`y=!pQ5KO_k^Nbw`59SWrbC8#*QVjpr_t zCeh+4LzCdwAU9)nP;U@322k()36D@MwiKkj%1?Z%>MQ!myl805N;G^HwFA>VFfbiF zjCjlnM8!DINwQUA=|AzYeS6vaD5J2MhER<}>osb;f{ zs=AOi49-=<89po@v^Gr4hjU9cSNbwQy$M~94%)RVe8D<=8gu)UV{n)#fZ zUU<=XhFq?wAk=H(W>*v9fw0Z0U6MfdwAyl%@iUrO+a5y|EFT1M zp8dy4I$-h?VAM|wxGh+8&PZRW@#WS~z$hMfHfbxSpU(;ZMZmnR0V?-8pN%P3o-wIa zPGc~vJn}wz#++3HT4BiA-Jr_D{X}clQC3shS^FIu1}RJK+S!?yv3efV%O8U4GVzowFI=Xw+dV}OrCd`4TU}fo^#D869JQZ zmlC(!&K6AR;GXNfl-)R62r!o82bq$vZr$hPz%?L#|M)C2p-X%qGkGPtbChoDik53Q zqeiSn2lG~uZMMcKHjMSPSAgLas6dz&|?dEgJ~S3pn{0RbHd zAi{$JA&HXGjjNt76QEo@wMFO@F1cUP=-#)$6W^{Fy^t8Q)4+_Jmd^=9=hWlK92272 zG@)p7&+wPc6wV*K7ejt+7wU!^VALJMML7DlSxKL-s+Vvvd*Cl2aJu2RsawfQubm(f zh$KPfixK`_<$&Fjyn?0N#i>zOA;k{?KV;4rgZE~ZUAPN~7_T%b%H@j^TNW>r>v1qp zF~$=<2;QN^iuvkS%#6%BnOT0sjh7tHHDu;mOnoqrOWaUUH&40UMF|$WZQVc+9m&?| zQi?oX*WoQDZ>znM3=znK?`6|nX$DQgm`bfLQ~`^^&6V60?NL}c{d4VcPEt8>7eUV z&Y>?)ttsMKe(+wt%DGRGE#o$lSJd{j)FCY1TWFbmisXt;#*7Ork{)gPK)hI>EP7xS zdh%V?Eh3y-ggI2oBKq129G9Ve_P5XebEQM`>74@R)`TFqA|0Lc-2T`{AgSmHS+xKp z4mwIDcBO%CcK?p)!M6WOh^fk3{&xhPq$7fWQlgeA1kd6YV(5)p-(z=-+QO(V7^3qw zu@V;7Z+!DG+~IW#y(|nL1G{d5HX9th#FjR0cB0q8+6hR{7Ly(B(Hj;Mc-bXt1${^N zB#jcBE5vNhX*yP+^YDrVXM4u2Wkf|AN%>~U6c~gRa&q<4Qfs#IHQfZgbx4B{5C=|{ zNQ~>}owbnr@d_te@Mv#gnyhB!rP{B89{M5s>f`j3sj4NJh)pLG8{bk_1wn5M=8MJQ z9;URC9aq0m{a@FyP4JjH)rKlgF_8q#`tqc??rv-V{c+j)2$wDtO1Wn@)lA-g4>97^)Q$%3x-s=sfo)Htlx|=Yt!%_C2kA)xuXz%{Ov`@$8X>NO2f}JXR8v* zpJ*#r=^pbi-rExqc&Sbj>Mo^a@a<9pii@;|AjP%BzXV>C0jJ(Y^MKdy1}qTNAt2}@Twd%H?>l4MBCrNUgqYy)bR-9rK-ZYYFy6|E#tmT zw$IEONRQxeY+U3q9KkM{_fCstPM;#$YJlUNdWV1YOWhvAOt;uf(gzGw5=b%XAcEBc z%~Bi@8XqDq?Lx9P$h7(LJE(^pn8uIcrg|^Vu4G>Vl?~yBBd`DVs0cMaqH8Q?LfSU( z5#s6Wv_wf$EtdJZ1u~!%n3lX}VQ~O!_;aC)l$L3kEcEF~Rf}l(gQ-zT^MC_@T1@EA zSG4*&d1w^7mlb>*Pj-Lg2)+~~QSXIal{}8e7v373vtOwfS?8scG8wSy*@SnbA5rUJb$ffg~s;IUz1mmxsaUo35b%G(#V6zFh z6$Cx$6LX5ZT@o2Ql@v5uI5|r98>jS6m}0mB^#jnC{tTkw!UvW7N?ZB$VHlTpbuuwu zA$YZl!7!c&Zh+~D7tmImr8KCS7fSL6!^@j6{Y*(f2iGMkp!&Kb8mB@M`;1_c2ue`X zcd!{fi=}|Gvbs=&EJG3JrQ$fRQKX3$SYA*ydk;=jAz%L#)q3X0v)7Xh8(@Zx_=(bK zt8&)H?u(!?56jQz@=Rk0Ng;Cn$~HayT%#D*c#d`=VpCCn~F(H{NzEb z#+gDyHVULa{`T!0RBoux&{|k8-ZMx`-g=pOK#04apD3e%QR;oq`zS77S3t#(`HsY( z0)638GJTf4oRK8+(em6JJ9UgYq70fq7kw2Z5!a)}gG!$o0c@5O!X5;ljQkij)7`2N zm)nNa>~$Rg=lbo(WrFX|3CTP+Bx_j?ng;r7K8?yIOU{>C9J?oThRXG-KjgzZ1JdXyN;VRy=I7I`nu(A%7)KK7~4p2S0^39pol2S9$DFz^a z%6?Pig0HP*X9YG!_Osd(U#CDQGX+`@3`wCn5`e2m7a<{og$r6wdL(X|3BirpMSs@> zp0Ga}j{sycz)-|V3gc@yuc=ZL0?KxQZYOme=2QPrDq^{e{71$GcB*_Bjgrpi| zPdo?HsOi>;L}ug~L*b3C`#k}FLEhW?y!r+f8zh&2^k-<8Xl-R|5h%)(2CkeHi{jBg zVFHS;KP8G9^Cj-|2SDb#RNHf(maxzdU(+*nK^mK#)(}=85?XSM^a;cTJ2KH@|#vYaRq1o4U<&%)yg;zjFrDL4S71>hIr>`DcyxF-Kph z*l68+?G%iWM5ZJue}X~73(Ft;lX^8}{)uqIJ4E|uAC5)>Q0Mq$SO@(# z61bLOSWRn?&#rJ(pm5@}skVJ4u8xqXVlngWl z>sr)5TXxPV=%7GKL$EL@=ZCFc?yNgyH*V%*$QBY3snoZp7mp;qpIJn(*}dMzot4zy zt~6n@As7ERhsu*stSK^+;IpzT!!u3X`tS-PMdVuGNL!V>#c?6%wjm@jC<~ z_EMfT{ON-mxI;z$*>LJh%PF!(FMy zMknmmoIc{(e4?y-w=`w3RM+ME`7iBT_yLZ)cdg)qW8^tR77_+>KbWJ{GF?7nIfJSS z<>gs5!J1WPjGMvaJvRjX`j%SQ<@e#{Yp)e+*)2!Y_r36%0d~>Xd!GPcDEUu+8hTNn z#RO&Eaa{<5n<`Y%_mYo9k&2<`@uf7sqHRjG7!h~ZYzL?Y4KI;RmMc5_mrNh~o9S== zWcqE{znD&Q_fMuff!eUnEXyP8Bf6DNm*>?U(?O%K{?1H9&b&tDbF!PN^pZNhJYXl0 ztKb6&VMNMr zkamrdqKPq|36e%|w6ZQO7t33KB!Bdg&z`Wvt>tIQbY5kl2<`5`lT`do?>&+faR-8{ zlQkWdOPz0Rz?qQ``uaT$Q4x65d>1w|BbZ5)isKt`DX{j2o*T~Mz47`gK!SffCsD+U zH`&4G0CZ|K{JOB#4JmTh0hk+c6#?`U01YnA8weT}bj%dfUJ1SC&oPv?{k`Kgpiy6v zdN=18si_dKFqifW&_Y>&0j>*6>YeR_Y!0_%^N~4+3XqT~q>aw6+fY!KIc|nI??*%C zjMv4@QJ2r0JE(&js~aWKApqavmr9N4+G2R>h$iGu24Hs5k1=QIiGFnv3(1%PhTCaT z;`sHOPfEzyu?-|&ks-`Gc>b4;0H)cB$rfY}$f#P8aA_+sKYQ68?cf+y^;hAn!NvUS zm{>EZ>dMkTu)uUJ{jJfMt3D#Vg@RmtKo(U+>0Nb9ijDIl25F=oeufOBNUv7rw$8$1 zsOJ?5r*}=>HA}||0IWF#_v4JS9qv>_u5DV}?9PxP2BX?FsO}S%bUZc5Y^ln6iU}Q9YL|N7LPaCB#H1lMK$LJ zCWYL~s0FyOMiZ@XNS_XiF7l6jucmF==*w-bt{qqM460I6IM*sFG5Ia8@0v6lB8itsTZ=xkK?ol zaf>?%Qgm*_9Oi3^qmZL$qz%Vx%@l&W@b@sR=+M{B)I3<_y)Cgoqwmu8Nk;woMMO79 zwOS8-K6f=Fs`)(VsnqYrzY<`OS@cC&5Sa|Q_ffMG+%JA=YRV^rA*FWtRx^t%^|%~7 z(Yc;nh~E{##lEO|1H_+diWGdZa?0g7IoL9ZvWLmhsCm3;`q=qr!dsLSlW%F3`IlLt zM*Psan7Y=o?ruH#W+9;livwO7I|#}g8qnHq-kxmYA5m)UQ&d?s)vxqO(7OW_sV zJnaH>Jd+%4sBTotMG@ZVpvo@wn9qvI?|DNukyt$*H(qL1KkW*0-&L#NiEwk;=#d97 z-kqf7mP60*Q9A4xHlI*$qEQ3Z@*wV(`F$RHW}TL-)VFFbWK3W`%{!G2t6_~pXZCfZ5b zR276p(nIsD+w+UP0R>04vq2m{e`v5-q>*1a+YCW0qDsh>yy0kq#bfY?seEhsbvSoe zJ5Oj!oGv=5%Wf1l}y)d6gBqyp<3` z*h@yTt)8Vg)DIS3JRQqxaHq9!XHP|x8MKu`FV{zI>D{qa{E3H8-jQCQdd(_7MluJd zZaz01fE)DRPBe}G&GgrQGX0t0UrgW4`!~~v|Ekknv&@KFG2&~z^o{LuZER=DpZBk& znuyqs2MQ*&1_Vci1^Vrf$3YQ3_7ajz*uC0t?KV(BNZ*`s1F!M}qpW5gBACveCgd%m zpRO{)KzEGh?;zS6FWJ_WysbmK2^hlMAVD!bXZWb@(s00uRpO|Oi7u0C(Ey+78LW@2?uGB0ruONZucJdLGX6O2jkg57F}Rc52R zh!G|>Wtx5{Y6kqa-+t3&bAp^x5YovvQZ6?#K22!sQa`g_pz{eis76s zm4EaZYRwZPeLups6vh>LVUU{~KU6yJF!$~j!3tYi@me~^!E+GR^bycIYKC+*zL$_M_`Qo6BeuAZFHy4z7vY3xt) zE7-QBJBz?d+*zAnph5v3oXXZqg#-vEg^9$dH2y}>P$%q%Kr?>0Gplm3HN3wNdg&p>nI&q|F*DEUxv z(+!;#7V3Uw@2t~oT+v*$kR${&R=BXk?KEh}8)D<%=1kF>ZI+2}w3LtIpTS}2n-NPh zo|~U-=wv|J40LnUHO151IN%)1fSfx}kB~Dw_;9sj$}rIIDMbt?25YB{=!IU`qmSPP z?6wh1;k5oYE1vS|D5#)*PmJZd6942BJi6q3YOm2F04s3m}fZB zx0-RvctqY@ z0#(Cdx~T`E?r}0jKRmqC;oAO)O0|A}tY#iZ201N`Lai265Js{rMPwHV{Rz|>aD$sl zUWAE#3kQSBN9|&G3z;W(^M}ohJ3E^?x99*q73LRc(oJw5npomN3f<~x3swBvV|{MW zmEAjF0(^BCApJQYMT3MhnlTfqdB?^@@>~0*?!qZYPIHsUm;^CF3k;=zaius>-K|8_ zoK~&cSq*j9_qD@^WVbQQWWOfMfH3NH3!I9HuBo^s)P(&8Pzs4Rdny{HSKQps7?{=! z@5$aG%GCmCl8G|EVAV(5nDDh|!w)>=RhQdY(!1S7<#~!e$Bh9;MbOKh;`{T&whgZl zL*;EcoDg%Qb-o#K1O^6#9nIGL3dxRpxhZ{=|(C)1y` z0E_G{sXe6%iiAzj*q!!z+AuWP&V}Yn{|Qdaa1#nSG`>g^zO&p8=!E2W$vIR1WffYm zPQ-Nrl%klR*t=X*{G*{b1_Xr-WWi2lb5ilhMrsLw4l}+f&2sF9v0i{ z=mXRC2thB9wmB2u!rPLtC^7yqa!i3Ov=FHBE8-l{%j9g*?)d}b8QI~}~ zMjg}KoWeJ{w?X#jhsQKx!5`k(t2i(IA4g9o?*|l!PYb&o&GO8&zJ5ZcL4AqGJ24Z7lqjw)(l!bXRL?EB;pn2M=c;4$n9>n z;7)RpB+5DFUQ>>j%o#o2N%gUiPfR*32z?`1|KP-izuIdta`+OYs8E{gkDUOJ-U}K2 z&e_u#`s5K?p4i~z``D+dc{mCjXrBVBKqpoH6V`yXg!L3tq+x3a2i%t9LJ;igw!&QO z_^2v{i4x_wR2v9*ME9DZ(-wpR`FvmuHa<%4iQ1MVg%WI5%kmm79Xm;gmDT}DUjH&) zBshJsH1?k4#QohFp%o!!3cp!qol-Hwa3Aj}IY_feerDZ7lFoe#8EapUF#_Jma~7Cv zbGX?s%3F!#t28&kG163E4v_O#c7BlqGUHaL8^%mWoC_#@+K+Owg#8KcnAa|8A?+T3 z?nf5X=v@ciE8HO$7^S_0EQk^+BH7AGut`0Il-e_rFjfH%kl^%dzp}SC?Cxp&E?xr` zz4M2puFt;R?IUT{qI0ym)hm~fAzY{2>L#1a42radgpYxwtb@x6^RgsU279sl)ggxw=?5@X?cC1%@~@_B_><_=oev(+zQl%PYz>#s>H8pkZom7w=SSp}qC&Fd{; zi)F*RBA1)z)jfdb$0dgx-%#GkpQc2GwHUCkIWqRyVD1pem5tpVbsdGGn=w$h2`{<_ z%dm^i_44l`fKNe0+z43IA$Qwqa)4>gc5%{5Wf=40Q|D6A{M_m$uM|H#mD7yUqtO8Y z%|o>OEkFS%iC8~CW*!>QAFyHJbP*Q9yN@gSh~`jVLk~9q3Zs2i)N|vb-Iow0a5zTj z8ZqNb5&g@1$a6S^2rboVg%wwe+)E&i6BAx-pPJ@S4<~ja?B-gA#}N5Azuit(-u+H< zdiIi_EKIfroS}`@1OFsY8tUK~j*F4X+>1M@050X&Ct18IW8}zxODahP-t67K!dfl9NJCoySG`P0DpZL z|JN7$fB7?m)c@tr3{opO`PY6la`TV=IhxUS5BnOHGDo5O%)W7HemsquBiGIsB<-YG;p#48&pPteL@$M*;=x`XCAI=L^` zVail_k)yDt5P1|{+Au*@XqY<((Mi!D@wcX&dnZbEZ5Y{UW(2C#2pv0aF}y6LyD^p4 z5p8G)cm+S|z6VRw&WqP9vZS@rBp7#LcDV?q)$M!nM?Kh+@1j%fl`Ax~Yd>(Yzc59n zr5=g$x>(xiM%Nerl$<}Pwb@>p<*Y^2O`J3i@t~&8?)e62_T^4#Z<_dm+@tOl8?JgE zoZ>&g+Fm4sG1LVygjM?xtfvDYWjjvcA+qr2??pZCZblLaGJ|@aDH;Iu`vV&gZ#)xhZ6Q6wD$TP3;B^5^NAm z9pPX_g*o2!H91a|=E(YPWK+G4ZBW&mDP)n^YxGD(4~zDiLtzYq=OB7DiFPbHN*;DN za$JI0Q5CSScKfFX)dILm3sUtM-7twi6l`-tF4Z6|Uyj$$#Ma+UVx;UDO&vEiwYCI!4o zp8+ISw~llZd_)qfVeP0T0XJ zcW4h_)wuI|{KpcfjCI3FP7LM#*TbuWdAur8%EjT5rJBH7BJ+&~GAIg^?~1Q?w<6Td z6og`CVeqLvdvYeA6f_6P$|6Q$lKC5C*M@mcQ)=F%E{{7IK@qAQ=7Mr!k@-8CF)i{Y zQZ~OB68UKY^6`ylHGP9li4#PyU9IDpNe^{DvI3Tih=h@?7OV!1-@$g8xV87yqpe_@C;}1mEi2eFpzfANVi-z2t9ofd5ecM)P0L zZ{hui`tX0LUiz2%>DPa%gNOg6Uf=a^_3iHs>)-04KmY)LegA7q`Cm~BKy-gaEdbF- zmHnlD@}JgiOyBAl@DF}b|5iWzOZ~e+0eBaa9!@eIJHDmSLztFflP#H<#(A0tqcXd& zhbht&lf~#Qez^O?Jb`iR@hnyAbl^8FPA0QIPOGu4D*!Elq3EzkMVy-Ho6=5tpyqqJg)Yu(I^kvfnAAXrGiQlog!vc#^C*@R!b<23)%Ko9#)v`UJ z1*GwT0E4uWy_fLd9~!ms!qqR@P?WnSQgJz!4*TWi`L4jM^&iWlF=^SI$F8lB7i7py zZI1JV;nE(A^Z=U+PR1WG*Q|r3yltHJDGXz^8={SkEmsdG^ODxngN``_f%Ub?!QBy! z^0kuWoyzA5Cd=vcTFfCy7s`iEx7=C8+L(1o)m_J~^PTf>n(3bB=&v$L{#7(L>*!>m z=W`&FzW@hdzuIk+3y^$5*9^J6Fhxw-7U;5Mk65Qf++09xZX33COm}y6DUgnb_K#28 z8LrdKHaSOa;w$5?U_GJ?RfobUw>}}RhgxUIL_64)pCq90nBk1i_PQAT2ZjPa2*!QX zEx%T&90t(dPARTI!m@(j_gA-6X3VeO7p%-T_>;dQff$pVts?PAyjhn2sEvyd8qgn$dot!O$@-nw; zDNV0J6cc8_zeNWxD}<>ia%c~#l4+`Rv7w!cwRr}LlJYuXzq$X`Ne78*kIlS~I`T6m zO=?4n>o4D3IQSjzfAg4Kh?}}V;}>UCX0;|@b%D% z*DF!Ot)NZzsMe`8y`ag0FXoN?vH=)$=N??ecJE>FRj+hptd+Vzs71~$V|N*lV>Sbc z&pz(dDOs7V(h%d}R2zBQ)CF$gc(iN+iq+hO8|iP~4L*`W+_LnP4@suya=RY8$%4Ou zb9j++4o3+rK}&JUC`FK=49#BclhSNOxL*uQN-?u+`E=KZda`|=SprsZ3Ce>ZARCaZ zg1OPAHVRYk*haatXumON7lHp$XZEYc=(OybNK?ZLv|W|w&V1%Jdm-gE&N}NPj@}Z_ z9OV5yB_r^Nf1 zt8suoT-5?pdvDlHtng(!qVg&MtCLBXsHP^DjyW2@oTG4=lu78WTUzKu2HuA|IToGDzU-^0xSH~EOOq@KdEp5rRKLH|Rrpo5&aru0OCY>5? z6;?g8z>TVQqmMw*P&1|g`hjeZPHNe6n(N8kFNcvgxW`p>Ozhe*3-gkl`0Pumtzlc! zPu_<|jQXp%$_JYGF91+06Z}^TvHgGg*L`f^TxHDnln+g`1p&XGKL=FMr^u?Nd*#qM zfQr`?kwU15TZo?Z$+I2`J)J0V(JBd*Xo*oR@j=C=m=;d9Ei5nd%04WuUht-A0v|G` zQ(mpZ<0W19>*60)97x1bkkK@oqS9IM>0V48t%@ye?A5n$zJO=^L3O!&Kt==c0kgPG z*gSnOy80kT3!dK9Y3QSMl59A0i1~qivYVy4G-BvK^}zAKy!V1C?i|j~5q%$eT)lnP>>(ZbXRLVJG3kue z{=BKTGc6W3KosqfSlp#A=72t9U&ze9lk!}JR?XQOty8By-UC@9HMhCVeI-c(lW&tpR1OVcveW$nm$fR*T=x{E}bSplV7a4iD3E;o*NjJJme5pB6^jzdCSp;N$Io;!lE* z3e4tui@WQgK0a>xL&b;=VeXC4i%zBv zd0>kobaQGl70#b_%)WdJ`Y4FUfjHExHB;0C?h1u)AZI&c+XN#*ePN zla1Y;n6=6zTv3_?g3RHEhs{;Xh=-8R@-+A%iu1UJH`*k9AD&M=)~_dJM$Nl&?>d9k z1Om~LO@!?O+ta`g?ZIz!`3 zpDvYWC)1J19Ed9%G-buyALR$_n`eq|@Te%-EWip4G^Vl<-4dhH@gr7AYNBB`UTQwr23NZGq`4skku*t66OZ10_?YST~l;~8dn=Q z_;FRZEMI=6tY|)Kp2J0LusJ!w)ul0VL1JiPzX|M}{@^Nvekch=+v zV?Km3yy<92ah*%{=eY}Lk;Jy_Xj5L66muQXd|iu-CV!pP$QOw0s1|?y_WksSsbL>g zOTiM8sTAa*q-p402BJYOpV+>Hjt)*_b2(PfPMbweAuwp2$^59w=~iiQ6Oz++Kk15@*Ps#M3Cj?FIL~q^vNk9 zzl^0pN-=(A!pmmjgZsi)hikAR4BQ1+6j+1%zUoQ9b8Of7K2`jp6a+pfL&i8R%!1Q+ zs6;YVa&_raJ@iU~RsBJQWxkgBI}&p9J00vPQ`8afVhH_o7aByQcBocoLXObfbXzaT z0d`FA2rS<0$4+9_Ig8iGfNu&4>q{IfQj>g8p099<*((oVP?kyQw+Wn7$VMR~TS?PW z8{`qMfRYmt9oQ@b4j95BD~pZji{(nyu|||!;;+HL|)%_2wEX}Dt@jY z)?CylKK++WpZS~V5C3F3lFPrCo)-MiI(^!T-S)^fFihD{-Yy^Tq|K=hEpvDRJp`=m zMYf%>M}#46(aja@YXzz)W1G8xoP1>T%e@hz!UQ*=bYJ8n2 zCBu9(0&E_1j8woL^UR2B82Noe@7U0%ep>uY&1WB_!jW!z)81x&>*4Q6!68giNZm%UUZU<`%Q?npBe46lpjGoHc;08^v zwW9Baou_k-(|#@h3ye$U;Y*>rMqAGHFT%m4A@h<~%}l^r_?(?4n5}bxV}Ghh(gP-J z=_2FsS>9NnyR9j;MH3FRjxdkCRTejgn=UrGA~6h^O&is?6@Cf1jHG$FZ1zPvdeRju zi_ASrcz(u=TLK0G>Cs@xlK>I$Pb55gzTXd%h^HtfHLAA5+`G?UF{ir^tfWdQ1C!Jq z=3(Aw;zFJjNBhDc`ZMKOl!=W2K(JbE(83qFq$LDwCLYxM(kxNUM}5JQD=nk4uEaxS zAkFFreWo?$+64OeniLQs~pX`E2Agk%|+<#`0mwWP1`r?1FQVI$Z0BF@--WXcESDy zG|3R|QR5(L@fACb{KSU-#R)0W#>chcEZ2U*C1-fkw+#nueEF8eaozW1blg{k!^lms zMg(Bcj;A-grhe^6lwEDlk96j!oQ0NcU+u(TfC6hB29=csF zqYL;e`veh79jZ&mQ$9^RubgnfY1lSvT4rT}Dttq^Pf3LAZKr`*gA*`!DmFPy?gBg? zdP6F{RuSonNNlwI;SzZRZIoZ6Z*g^EA?jBg1a*V5d`7QXzt017l;Am&Xs9Q>^%p2q zr`!u0UhQZ$j3G_`aE=M2O~Gp`q^>xvr`V4{eBc`BCkkivcKF>a2HEA1UZ~FsGRaA0 z1K0cs&ER%Ric1+Fi64_9d_M+UEsi$*!P&H1nLE@K9i7V&XRAb(7a6z`lp@_`on-D; zG`-I^7RauEk*?RF#ORK>k!?HV$R-TmY%;MgK7~x_TMTYX#k<*hB2YR#3xy5WO!}74 z^5`}O1-Gyke)vX`SpAXc6R2bgDJ1_sa^R?Ky0J}G#JH67Y26TKz7qg@Pf^3Qt|Fl?CD!p^V@PG8V9P&?(-vuD_VZ__?i4xfsJ!RyZ+C z2LqJ2lF_BL0z{QLtMTm!!PgJFzXF&3&>hO0?p#-_HQrWamfKg5AmU!WoXD`Fx`3wr zTb&sFCVsa0Qt`U!rIOONc5}V8EIVoZp5s+DU~6NxPN9lao&Q`2@V8x4^mwt+#IFrP zu|G@dM5Hl5Z(?@$TF4SsaDD&+Y=kOBlGyRvTkx92@@|l|_z|jQKXaQx2?Vo>NxHZ_ zLorQqwGLxmn5;36JQUXZX-)<}53mK7pmDIWpn!DtWKY3CfF3JmhZH!h(q5R4`JP;; zAy|9z7)IZfGSOu#LZm zrNGVXN5EJg!#eQ`2Ox;|b@RF`Vx`%j{nd0N70W>&9F)Yds;WZKL@ie^tAK$s3X{Rz zcCgV2M*B8i)HUf%eC6PH_uLlrXTAA$czLuZNjp?BR~07QFaHFdvHC_zui-`ScmtJ7plBbEyHU@F!D*{{fY0CJhcu08M|HfK@GeML(zV7vaZKM^ux#UDepC_Q)0uzKZ9)aCNcZ}E8SLYA8%sx>AOAWppi1LRlK+6@o;tZHqRXs^av$#0@ zApviWXBkl^JUSdqPHCdBQQc9810bl3^`hu`4V?%*H1J$tfAW#qH7Bl}&N8R8Y5+fy z=&!q_r3^?<|JeFbOM#FX80tD`!n4lvbt;-3@(UJs#h!lE?wzH)7BT4y_^43(XVJ1$ zQd`kuP&;0}Ctx!fm1CY8bR`F6o8mTiC&o~@lLuE7xD9+2r)kD#1L)|F znGA~Kb0AV^G5Iovnsx?(M09{(B-hh?y|T#KBN{l~Q&GlbwO@R8{_mKFD;O$D{xSs} zQSbM+h@bKh;Hx`4JaT0`>szw@!A_+#j07NKUdGiR7wT$k;vU02#E4;$Vt29Su&qhj z3cNdK4gvlZR|N9$0SW!k(aJ=H{<7r-ntM|3C(^_^02UUskt7z7ng0K$vU_Z<1cBBC z9NXEkZ96-**)ck{ZQDu5wr$(C*-6KCC+XzQovJxib3UE<6Hl#r*Q)h`rL$W$Srap< z(}Ylq#3GE84b6UDKyu2&;W6jP3WON_F55@nE7F5RGow$dkVqIE!cP=Iw&ME9I$Fp6K~K`bUMo7^^N}U){%rp!dDEhb8YUpiiZgM? zQ3`vvbLV%9MPY?zkll)_E?aYP-|z5^Bbb?_WTZzuJ|q&}vQg)~y>P;EGF8l#hNk=w z42y$4QD*VoEcj#|4s};=uZxrb!7OKM3xqoOZ)=;S>zGPl3T==7Q;7Y4%+r_tHvQq> zrV|+cV|rlEzfHg4m}z&kMn|1=RNYiH_p6-)wx$M9B^tvq@#V@$}1V^0_n={H7UjVTt~Q0Tp* zM--CO6RT-6?xvlQatPhDsqUw*O>MDN>TGH*#DMD~=v%>H7Kd#z!2kKQfg{MVt(>Ul zO|cE;-Xi{<#%L=mtc2U%2tO%1{Ic&ao)VDZ7m+hKPX8-JKW8s{%1$}0Dk*ks@L3?A z(({0JtL)9Omz<#3S=xt!YrX0s8adPZTJ}M`NQy9bT>D$_R70-fBZ87WpDdvj;zE76 zcVR^4pPp*To0f9{&EY`Y2NSsux>|?{r5@m%M)l;19gK49{eo?O7%Np=10|;vAH@&1 zxB5KL3jRohc?s>CrG=q6OfKauCL*P%El%@@YFOi6GjqhPm3RO$1)j0y_B#t*Z0))y zS&sM>ny?gY$=$y218dCNZSafGHvbio+vfnB3_6!th|-w3;t(dOhCe1C?nQNUVp-x^ zAc!X9gueXJ3{K(NpU6Ib{3>SR+gmT6V&{Jk@5 z>MRi~GaeP&ZCe1{>1Q7-@U4X`TytxqoSNULxT0fXloQ$8rL6ByES-}cluT3J&@cuv z!vBEDCbR}g(>;aX7R{fJ;&1K2I`PF_+(t9(V6?hicMYV*+YO`~UiD>Jq2_@++#~qQ z{q${cV|Z>EwQ*n>E8OJIbfQjQ;=)PIfzbb>AFhI4Q@t7X4T?dE_k z^7)w;F`ljfhy#$2XB1hTjaTxrOLP~MpZ0l_n(0jql(%mpp7MQtzi7aI=%S?@Z@nlD zwvF0rJb8Zuis~mX`)Ws_B|@4%xx*)6#966tq$#D4Uwrrknapw-<^Zj3ry^~%`>{Sf z?Xdb_uWN0xadC8;XqG`^AzK_3$-nU&8@`9IiQ>jn2^Qx|(7BI&MI(7$$M<&y#C9YL z9PP+fU{vhF7-984wfv%h&9Di8Fev6CUNRZ83*@kdzQ+arc+A(WPQ|a*1yLN)CP?Gl zPRxw_gGrsIiucQq@ttrLhRXV^2?a}~YZh0^`J3`*TL1(6$3=F$d z9b9Maoy~9ynpmiR5G#3W|09YJ0*7GCf^JMcP<|_CVaz0uI+SWG zK`^CWx(=w&2Hw`&3)t-)lilmbk6vVgbH74Pi#UCp*d8qR*#ZKN{Ur)0pg{(osk9wD z^_}25ZcEr%#-SPWB|?AKarc*(`plF&u%tqsoD&!?@*xKW*LO=TQEgv32q9Kb!k23` z|Elt(XK1QIT@f+D(J(bXm8Gjp+Yl-@isaNY0;~mUd0f2BL-NzcAX|5UT0#Isz%OQ^ zyS#+fO17^{8HFDb#i-eb zB+I`jdgV3htROPbfO+ie$f#FHh)=GuEESB6ypo3$VR$LNI#NH4@3Cmw2rO}|AW-xx zjGGfP=Y#vdQ2|ZM+9J3D&)=IJ(Cyiht*F;v+ z_n74&AX`gmbm2_n-son#jFvy~fuyMi!{(SZa^ATf`nGvbf>%)Fks-a0^l#@MZhtj< zT^sg~^hJL-7Zmfz0#y&_m;JX9#Ra)&Lg8|qz6zhlZ#y0 z(`_1EL7)%IY ztq$$Le{=bOv7KY*JxM&?PGKj4;rGe4QTu8=?{PunGZJ%m{>qTU|LOcp>VwHwoyFpO z!$gpA#_`#kDyuR~hVyFg0l7*^PZV5X502!pI%?2;g0Wl_DFjk4{Lu_s+wmo@yii%&NCo+ z%Tk;e)t8qmsj2)?8N7dnyx z{#6xh^*xyP=DAgknY_ax>jJ6u)d51X z5(Z(Q{qaZK4$cQ^N&@acz{w~;r&Gag#!+tDUsqHaw}Nt8n{NN`p&O!a|KZ(ll3~XRWj(1Q^*p$;g0YNW9UZT0&vM}o?`-N?;sO!G#XjSU5A$>+#n6Ue$-Dg`a8a{NdKrC4^sW7 z)F;LprxU&U5@nFZjmOY#7S_2~-^q_br3`qA2^#w5t}QHh7kmkSQ1Px_TVz3PQ{4JC z#~wTdc3u&*Fu1rn0$~C^+V8nETdyT(anRYHljg(un1upVEQi zw!-C6GEeYSb1ld}|N8NI5`R_ehDBypPc5nT2eTpPGYn-znwlg(k(?Ewwu$;3KuwEJ zeTQNEyuEdQ!0@KA!WU&VpTZW!V4R3PAdC_GLIXIYcGz5pj6VAPNlcn*lSzAOH#JLy zpsEO3`yP-!4O)LHfMf20_p^f74MqVy7|h(h7pyr^>ig_n2a_8UO3<)kQnzMmuP=xx zQm_ST!ad`6CY004(&aFH7Zg$9|7iNk-=_bs16^wQe@ut}^>5Sp9oq4)>%QfL(@zjc zn`eKo>kJ4f8zn$oh-fY)f9nZa9l6InU^<%Yw+6LN*Z4skZ9}$ikg7J$R0*jnX!BuQ zUVk+m1mfP4rqpLdQmly_GqtlwogcMYQAO#oKsGrL7H~vAy$F(j()?mT_pZf$uy*~b zL?b-#dbZLM+IEVE;W94vleRfAe+H#%vr3O5Fo8R41s2Ms_3yw2A+m zVOAa4m)&Y_`!u)SL^NWi&2Gr{y3g`f-W$iQM&L<&KMWTCZgdM_uu+l~3og3Zksdb; zDlk-OEwOvL>%5LFYUK(7i6~JVssvyX7B%hwvf_w8<~zXcq!gH`@Z>!W6u2`ywltsS z?GIm_B3l)G4%94bS8`vn{w9tbf8p`M8rGNV#5;Q5Hjg}oszIv*oI7x@3#s8`?^QW3 z3gnde=Gcvb0WZLzJ@#DybrNc9szShcI-)%!jSDY{p!N>%PKP zj^nOcD`T$vXBL6vjx*_uJD7ae_$eFyKX=E@~9hGR6<#1TT2i zP+UVzQN#Li6^cE&zAxgPffudD8I00cPE2Xs&nPUR3l`t=wtCy)k(DkAGI&S+aUPZ5 zdIBqNTgA5+jE(y<1`UZ%XB=SJU~ID<16sZJNA5;X_vCc9ur(7B>6OL=9@_O5@@%s^ zPeK$qB9M%1aZ;`bp+UITQd3Hyv=*a12-=a^hKTwLW#Wh$4d&E)#t1iO-qp0>sA-6< zUUvO#7EGh`6Xdtj24D5*>+pJ6LuntbW-krv>SIK?=!gN}c8=Aj6J$lL_92BXwsAc{ zIt=}DGBj~H=zM1-%?;VoPD@wr?KyvR584nK6$H76WjJ&TJy-&-Ip@E#vTe$WEJ?8X z(5_1-G0%-xM|AKpr?@2(xhP#u%apv=yo=p1VEPJAeUWAWRe7AT7i-1iw%yj-p> zBjth~h1x#Qd8_YS1%WzCv^KhDS=*2)QB3ay9apSA=H2a(Llq-WB`nFlwO?zu*JLOh z(Z*I!yfcUxdM{u3e^rN{(&D{s&zgCK4qxP<!j5tWNh~xK@Ppn*S*O^Z@Zxe zH1WW*sR(hcD1S}V6bJd}L`}U8MEOrsc-cFJL#qRuU^3M`7(W;Qm%%Ot^oHgEqgwq> zkqMh{Wxtt({z#F4l0y&=kmQP`e}i&&aCjV$C>%({wo#2oqm*mJz*)R<5P0sW0R5zu zQ_{|)-b$2DrrCv%c#QeO(#nZoz7^W<%L=UsHlkm|gMis+6j=gZ9;D-pzLBQ<#eT!6 zX58cKPc+UX0IT0dz_V4RlQ?14nDbDEcL9kfJ4F5lzt~vdBJ|fbx=;;ayzX-N<8E90 zH!!Uw8L^oLaw6azBM!|?_J?#W8pdZ_{u8J*P7a{bTx~&c98k zL#LVn86|8%^L)sQFJLhn#r(y2jMW5SgkHgk%|8%e;-IOrPUq_C|MYQ_`pu42CU=wL z{h=FP^w!V|MM5FsTN~z&rjnG-ON;lygEI&bE!21BjZsYm30#d8+omYIAneH9P}a3dHmG6pV|!sm*w!iCxNqTvrh}2lVBXEUjpT zqIyZHe8aYcp913)@Kf76_nj<7{$*8187I=oQgHeDZgx4V;2Wq$4+7A;_QYYEi<+76 zxYJ8$VZH?3(+3Dk`}7reqDGTCU-fE_)+hRhiz77IY3o<&-u&ncPVwDvs*pNnXzo|& zt=UZJmh!*rv*PXHT!x+dpiS^Xzu8KY!Suh}5{T#PA#hSD>#G0wNZf9*-1DIjh{zKa zAVa|q*zB2gg=%kD#(nM+=(^jK-*K`-)xriZ{7zm!uD9Z5d{m$) zH*jdtAeb}VPdS(nY)@k-+E{H}!D3*8cya8d`Ov6PXW5D|%_Fx~&S)zNWY37Bt~o zW>+i*l@+3b6eY^&B}fR8R8BADhb=RA7(ejVY$kr0J)m*pEsu@}(1JR{l|2?#?LK}T zonB|p`xGP*5cst^+zlMX1)l&~ecFkbt#^K_?61+SctzzX6g0aT`j`S_G$Ak{(g?$d zwVink4$I!uejI9|aw5BOlkxELZmms@ybQL%N=sqHF-)e&JdpfXep*+cNJK z9ULdBF>#!sbn73?-}~M&CSN(`?02uPhSBNa@IaN($%IRu9{i;C(}wf*v^_mNQ{ONn z?CISAIqAO}s(rM4+VySp7>nXzZlQ$SS@j<9&;368pZAH)qKkj03)>lF^n2^adhk2`I@7?KvBU z%)uSQrlUp{T_sVTu`u_x9Ix&hC^c1c1(Fp1rWd zMyfmGn41`V*>|yCEc-xUZ6U-$++(lIbgdx_FIW%v0q3#i@paMhR=@(I3L~PuR;7o% zmN5-}x_O^#J!cue*p+fG|3Fm9+P@ZqR(oy6Yc`c-%AFswO)kb(d*+4g!0f6Hl?LHp zRDP4-=;LY@y4fB3TZiWc6#aZ7<|5b6W0Y99ba>F5_-HfX)-k^0>JCm_n;jkH+?tn~ zs+(!$O%a-cr#LxIi_k2)-EpToId8g5d`lj^>{U)Y8a(J+-6RU=42vl;@-uRj!SO$6 z^%U-4P+iP}Bw7+mz{@}T$UM;~T%k6XW|Oe$Aq>mGba!*^KgOpR5#A5ApsV(-r>zeL5W47t9EhupW1d*Ld(1oQL-OToPs0xvGbiS#kkbiQ4oN zm+wZQ=b2L9_u}L-8l3aXOgl)%hlJ2hwJw7S3Q&+RE9RfW`z>eGidCd$T`t72s44y! zR&=r)GLxF1uweM#6y#_fu-Z7J7CaJ>Mv)X&`ElMD3qgXuX!cn1TcvvZQx@qI2n`AX zlH++bLbpWkeacXEFw(nkmf6}D&`hO@?SXQA!0g9HE3}x8X``-gT*gM?Z@&kZitL)w za2sAvp>{Eexn3U}(SZlu!BEOh)#BdydlzG=faBucE9b0GBxD`Dt_@J2@+Z{LgET?1Ifxl7^*5*Fx@l&%L}nAlNQ;bIq&Big4= z5vH#7V%^t;l%TCBpj2Z(;0d?m5bhW5O6M(#OOq<4~+JBqLKP>kd zhi!K02dK7ryrK+3$XwK)WF6jg4FzxaE9I`EnqcAP@QE^sD<2W4IpOUTiiR!zAw7R# z0kabhu$F!{@#}8Tm``Mq>|fQeiHtR@=N}&?~;OT&z63)*_0K5nmf9R;mI0a1P zKI1l0ZL8+0W5LhTiTadZ9!rXw?~d*o`Q6uPQB0L^1U|3*Jbd&Sn%FO92j|v0HC}Ji z{E?MMmX3G%qQSckm$$&*D+UKSQDx)nzhIZD8e@W zOBLc{!}5Z}REraA8F-Y#4wwryiV`FG(=yKv>t#x58l-7J7~O;Sw{LtY7%hY)ft zO_A&fg+W;oRQ4hbw_l)&o0>a+vR%&TMPK4yJ>Vi|qKz%Sqnk_^XS#h)P>#ei4ssoY z*YF=Q<{jDx+1oF$u~K%Ze_)Ae<(br-Xt4}0?%^fF#<`l!oB2`J+B~cdV34`J`x1l@ zSircziHMUbW=)m3O#P}1&@7Y55L`0#CqjJoz-iE$jP4Wup-@)Z%T8ih*CzaY=S8Vp zMb`Hsf+s!nosz;eeGQXth1=OGO(?4_Oq7}43NK$^U7B)kzhR@R9Nb{OcW}z#O1h4! zbb0%dVtz2eq27)ne=JE((R00fZSItx%)Is+Xk|(pHzM&MQ)g5NTJHWS`Qepbz}9$E z68Xwq;wE`D?xzYzxdK{lFtlghVs6q*mgSBbo~kbSt2lI^I+Cwme3JC~58U|*sto@U zGw_AS2`+J)cx*p<0=R#nOV&+m?}J-qV?22+0NOo0QP9rXVQNVB>5HUDZstaHShd7&BeR zB5UtWC!71rrXL21Y| zE??;~;!Xjif+&4uC6v&MA5nA(G58*IAbuLff3gYmKdN4z@G_+GT{ksG1w7r%o*-Vp zkF3&u*avaEa>|>?`i?fi<&1yrq}gK}FqH#2-+|0Zcy}og$G8=RcnSpQj*ufkD8N=p zSPnY-6gOwnHv`23xb+5qRoCnQ)dA8jE`Dq2wl$TtXlIX@jGa>%+PP^_Snd@~qH~l% zNB1?`GT<|WWH%&xzj=$x3A<;jujwh;YhNae*^g=sm z+mj7cB_58mTX&b~%0zR%*5fj3xE^UM1P07Z<@#Pp5IuTD;L#W-iCfY;TjTH`;wWtG(2EEI@$5EcTy!PK62!%!6JAs1k%oE>}E5nu4R zfb-W75-za3K0->uQbGmkfK)Q-mopef$+SWIE835VaAR~r$? zWf$uIN*M1x5WPA`;E;M1$h`FWcfyzt;b7(W_Q>OporYa2?qQmJfp)A~+_1Bj!bEDBz|OPGiU#X7 z2o(nc<*XITIbl1e-vJS}r>otxTp5MVNT!9r{JH*80t)Ib`OCA*2g+>a zN?a{1j@%8}k4JQlSH7T;UwOgt<%-o>6)6R=+h=v-Vb?pLd^o-pzlo>l5%+^E+VBmq zwB8;z=2#hq|vr=$7kH52y-e4z`vRsNuQgX$0E7^D~f9#W`EpZd5Qo$GgP!ljI~uL|EW7 z5@KaH53vLETOiL7+Ems^YwK_wpOG5FKIEeAz$8&4NsrvHU#~|ai1MV?$8~1mm|>yN zYm01F5!6aperYs}(P9W+3XL3TAHYDFAKeaN zopxp-k$6n&ycqMm!t2ocC$+Wt`uC?RR@M7z00|W}WDUuWqo?LuQ#`jzYbmN$L=kW? zyF8O`!MG76#)Zz*?@^$;I7$g~<0!*SRzt{fvg8d0wdMYs#0+hm7KlC)8}5r@THFEb zKf570-mO>G;V&`aiRA`?IlG7zV!HE#lbrR97wpc)WMRm3qz1w+F{u639UJ`kVO)|; z_I7q^AizbY9IjXSJ)C)lPtA3}&wJ_b*7}xz=Ow>2DsAfBld;lB?p`EJKMRbYpXeH( zMTS9Ry|J4~*;0Prz7gfZtq-F)!sfPxZw#Ku%qmb~gO38%eZqBGjDj#NuYSZ(@V4uQ epmY#o1`9+f*- z=eq#+TIT|BetmE9S*cNw~ z$RRk{hzzF|mrfvUXxbY)N@CzP4ZkpPvFW}?cbU27CaZxiM)4aS``3YBAxd$D_ne&6 z98$zRjdJ#U(^z>8c-MR>E5nu@$Cgk+*V^kTjw*5^R}XJDHL^l>c)!1lKQW?F?4Aw% z@~?5APpS49&pu1YsyGIr72e+Oniu(332GR#5K+X;QCYV8^siKXGe{ExjeswQ+ z$<+k1Psx<0f4p^jw)-qb_TDxzDl#2^FrG?;X=beNzO#djxG>T`&$YW{s^@ETNG!5) zD|oNaaRjNfW!e06BmarP79*@#7KuT_+uK{3JqW}SDEJVit;T`CVMw~2@5lJ9n!Shm zf1*xJ;2G6N$I$x#d%qWe{`Dv9ltevBG)Z8zjd5+9^h`6UNmy8K;n96IQS{gi3;gS@ zSOy5cX9p;LXk~eZ`$G>3@6eYs5PBf!BIVo+qNPw92o=KY(gr2aJN@ILZ$TAppI^iW zT$y9p*W8fFk2*_ggy=lSaZquFaeY*Td$-EfLa&IWuB4p(ad8i4svBX~^iUz4fNoUH zwz&d6Sy2D-+96y}ZDfoybtoOw5>`&4a12BlkD6e?+ID|Vp=_zqg zqX$Vl#+SzVQkIdDWA2revaC%F#HbZMpd8P>m7Y?_gD{lYOa5(cAbaoRwk^R=2o z%(OffKxHcetX!_vG&JLBb}Xpf1d^tDz=v>)g}Pn4VsOkKAuN8wch4gt^cIfsJ=`;y zUaF!{Xx;-vyT=wP@(Ala5^c*Wue5pFbY4`6a|}qF12fW+BHKEkjN&kFIpnD%1iOWO zV#TSLlJCaI(Sfa%Kqm>uL(R67{2YB8(?|Ydocinh0xn2m&tuzT*&&ALmQ?F%sCYbE zw%(}(NMo`P0&0J4$OPS)B?$k35mWnm2RYf7%J8Q~oWiay1!ADpT^9~qewwnkozzd` ktYq779pBhN*V#~2FzZVh&UhcF9*3Ndlb6(@C@gs*$Vg