#ifndef _FLOWOOD_H_ #define _FLOWOOD_H_ 1 #include #include #include #include #include #include #include "MESA_htable.h" #include "MESA_list_queue.h" #include "MESA_atomic.h" #include "MESA_handle_logger.h" #include "MESA_prof_load.h" #include "MESA_list_count.h" #include "MESA_list.h" #include "stream.h" #define FLWD_RUN_AS_SAPP_PLUG (1) /* 转发网关以sapp的插件形式运行 */ #define FLWD_NO_ACTIVE_IP_DISCOVER (0) /* 没有活跃IP发现子系统入数据, 手工指定一些可用ip, 自测试 */ #define FLWD_NO_GDEV_ENV (0) /* 没有GDEV的环境下, 转发网关模拟路由模式, 仅透明转发包, 其实主要用于测试接入网关功能 */ #define FLWD_NO_ACTIVE_ARP_QUERY (0) /* 没有主动ARP查询的情况下, 且无协议栈模型, 如raw_socket, marsio原始接口, 要手动指定MAC地址 */ #define FLWD_NO_MAAT (0) #define FLWD_SUPPORT_DNAT (0) /* DNAT先不实现! */ #define FLWD_ASYNC_LOCK_FREE (0) /* 异步无锁模式多线程更新 */ #define FLWD_IP_REGION_BY_LIB (0) /* 1:IP的地理位置信息靠第三方IP地址库查询得到; 0:IP地理位置信息信赖手工配置和动态发现系统, 他们说啥就是啥 */ #define FLWD_USE_LTSM_FOR_QUICK_CLOSE (0) /* 使用LTSM库而不是靠超时淘汰, 以尽快结束TCP连接, 以回收可用端口 */ #define FLWD_NAT_SPORT_VOLATILE (0) /* access处于NAT内部时, 出口源端口会变化, 即便access绑定了源端口也不行, 此时需要forward也记录收到包时的源端口, 否则包无法回来 */ #define FLWD_RUN_IN_CEIEC_TEST (1) /* 在ceiec测试环境临时写死的参数 */ #define FLWD_USE_SPORT_HASH_AS_MARK (1) /* 使用源端口的某些bit位, 用于标识acc-gateway, hash值, fwd-gateway可以确定回传给哪台acc-gateway */ /* 以下是使用主机序时的bit分布位: |15|14|13|12|11|10|9|8|7|6|5|4|3|2|1|0| |accid| hash | sport | 最高2bit 接入网关id; 中间6bit HASH值; 最低8bit 真正的可用端口; */ #if FLWD_USE_SPORT_HASH_AS_MARK #define FLWD_SPORT_ACC_ID_MASK (0xC000) /* 主机序, acc-gateway ID, 最高位两个bit, 同一局点支持4台acc-gateway设备 */ #define FLWD_SPORT_ACC_ID_SHIFT_NUM (14) /* MASK移动14位, 变成立即数, 当前局点ID */ #define FLWD_UDP_SPORT_HASH_MASK (0x3F00) /* 主机序, hash值,用于跟真实流量区别, 6bit, 四元组冲突识别准确率: 98.4%, 这是复用客户端和真实客户端同时访问一个服务器的识别率 */ #define FLWD_UDP_SPORT_ACTUAL_PORT_MASK (0x00FF) /* 主机序, 真实可用端口的值, 8bit. 其实也可以算作10bit, 再加上acc-gateway id */ #define FLWD_TCP_SPORT_ACTUAL_PORT_MASK (FLWD_UDP_SPORT_ACTUAL_PORT_MASK) /* TODO, 以后更新, TCP可不使用端口HASH做为区分, 靠ISN重新编码区别,增加可用端口范围 */ #else #endif #define FLWD_LINK_MTU (2048) #define FLWD_MAX_THREAD_NUM (64) #define FLWD_REGION_STR_LEN_MAX (32) /* 区域地名最长值 */ #define FLWD_NETWORK_NxM_MAX (16) /* GDEV和转发网关之间 NxM 多连接设备最大值 */ #define TRAFFIC_STAT_STR_LEN (32) #define FLWD_CONFIG_FILE "./conf/ip_reuse/flowood.conf" #define FLWD_MACRO_TO_STRING(x) (#x) /* 编译分支选择优化, 最可能命中的分支判断, 使用flwd_likely()修饰 */ #ifndef flwd_likely #define flwd_likely(x) __builtin_expect(!!(x), 1) #endif #ifndef flwd_unlikely #define flwd_unlikely(x) __builtin_expect(!!(x), 0) #endif typedef enum{ FLWD_DROP = 0, FLWD_PASS = 1, }flwd_pkt_action_t; /* 此值和配置库表需要对应, 禁止随意修改 */ typedef enum { FLWD_NAT_TYPE_SNAT = 1, FLWD_NAT_TYPE_DNAT = 2, }flwd_nat_type_t; typedef enum { FLWD_IP_REGION_INLAND = 0, /* 域内, 此定义要和pangu数据库表定义一致!!! */ FLWD_IP_REGION_OUTLAND = 1, /* 域外, 此定义要和pangu数据库表定义一致!!! */ FLWD_IP_REGION_MAX = 2, }flwd_ip_region_type_t; typedef enum { FLWD_ACT_IP_DYNAMIC = 1, /* 自动发现的动态活跃IP */ FLWD_ACT_IP_STATIC = 2, /* 用户通过界面下发的静态IP地址池 */ }flwd_active_ip_type_t; /* NOTE: 为了程序内使用数组下标索引v4, v6, 此处并不用4,6两个常用的名如其实的值 */ typedef enum{ FLWD_IP_ADDR_TYPE_V4 = 0, FLWD_IP_ADDR_TYPE_V6 = 1, }flwd_ip_addr_type_t; typedef enum{ FLWD_MAAT_SRC_JSON = 1, FLWD_MAAT_SRC_LOCAL_FILE = 2, FLWD_MAAT_SRC_REDIS = 3, }flwd_maat_cfg_source_t; typedef struct{ unsigned char addr_type; unsigned char addr_len; unsigned short dport; /* NOTE: 反正这个结构也不对齐, 此处的空隙增加一个port变量, 仅用于DNAT */ union{ unsigned int addr_ipv4; struct in6_addr addr_ipv6; unsigned char addr_value[16]; }; }flwd_ip_t; typedef struct{ unsigned int sip_net_order; unsigned int dip_net_order; }flwd_ippair_v4_t; typedef struct{ struct in6_addr sip_net_order; struct in6_addr dip_net_order; }flwd_ippair_v6_t; typedef struct{ unsigned char addr_type; /* flwd_ip_addr_type_t, 每个四元组使用独立的地址类型, 预留v4_over_v6, v6_over_v4的地址转换功能 */ unsigned char protocol; /* TCP和UDP四元组需要区别开, 增加协议字段, 实际是5元组 */ unsigned char dir_reverse; /* nat转换之前的key四元组真实方向, 是否根据大地址作为源的HASH规范, 做了地址反转, 例: TCP_SYN包, 如果此值为1, 说明key->dip才是真正的SYN包发送方, 写日志时尤其注意 */ unsigned char __pad; /* 字节对齐 */ unsigned short sport_net_order; unsigned short dport_net_order; union{ flwd_ippair_v6_t *ippair_v6; /* 此处使用指针, 因IPv6地址太长, V6流量相对较少, 此结构凑巧和flwd_ippair_v4_t一样大, union可以节约内存. 注意free!! */ flwd_ippair_v4_t ippair_v4; }; }flwd_tuple5_t; /* NAT五元组 */ /* 真实服务器IP和端口 */ typedef struct{ unsigned int actual_server_ip_net_order; unsigned short actual_server_port_net_order; }flwd_actual_ip_port_t; /* 数据包来源, 接入网关 or 转发网关, 其中接入网关与转发网关之间, 预留本地socket通信模式, 做为两个进程可以部署在一台机器 */ typedef enum { TOPO_ACC_LINK_USER = 0, /* 接入网关--用户终端 */ TOPO_ACC_LINK_FWD = 1, /* 接入网关--转发网关 */ TOPO_FWD_LINK_ACC = 2, /* 转发网关--接入网关 */ TOPO_FWD_LINK_GDEV = 3, /* 转发网关--专用设备 */ __TOPO_MODE_MAX = 4, }flwd_topology_t; /* 用户接入终端协议类型 */ typedef enum { FLWD_TERMINAL_IP_LAYER, FLWD_TERMINAL_IP_L2TP, FLWD_TERMINAL_IP_PPTP, }flwd_terminal_proto_t; typedef enum { CAP_MODEL_PAG = 0, CAP_MODEL_PCAP_ONLINE = 1, CAP_MODEL_SOCKET = 2, /* 复用平台的类型, 因为flowood系统肯定不会用读包模式, 都是在线实时流量运行, 将dumpfile改为socket模式 */ CAP_MODEL_PFRING = 3, CAP_MODEL_DPDK = 4, CAP_MODEL_PPF = 5, CAP_MODEL_NPACKET = 6, CAP_MODEL_QNF = 7, /* for 奇策科技Zhuanyong网卡 */ CAP_MODEL_N95 = 8, /* to do: for xx9 UDP新接口 */ CAP_MODEL_PCAP_DUMPLIST = 9, /* 2014-11-19 lijia add */ CAP_MODEL_TOPSEC = 10, /* 2015-09-29 lijia add, for trojan detect */ CAP_MODEL_IPFILE = 11, CAP_MODEL_MARSIOV4 = 12, /* 2016-10-26 lijia add, for DPDK-marsio-V4.0 */ CAP_MODEL_AGENT_SMITH = 13, /* 2016-10-27 lijia add, for 资源隔离共享内存 */ CAP_MODEL_DPDK_VXLAN = 14, /* 2016-11-01 lijia add, for DPDK-3.0, vxlan三层回流 */ CAP_MODEL_MARSIOV4_VXLAN= 15, /* 2016-11-01 lijia add, for MARSIO-4.0, vxlan三层回流 */ CAP_MODEL_PAG_MARSIO = 16, /* 2017-04-07 lijia add, for marsio compat pag */ __CAP_MODEL_MAX = 17, }flwd_cap_mode_t; typedef enum{ FLWD_OUTOPT_FIRST_PKT = 0x1, /* 一个流的首包, 需要进行指纹生成, access通过vxlan保留字段告之fwd此包是首包, fwd要创建session表 */ }flwd_output_opt_t; typedef struct{ void *low_level_mbuff; /* 底层IO库相关包结构, 如: struct pfring_pkthdr, marsio_buff_t */ char *inner_pkt_data; /* 内层MAC层开始的数据部分 */ char *outer_pkt_data; /* 外层MAC层开始的数据部分 */ int inner_pkt_len; /* 内层MAC头开始的数据长度 */ int outer_pkt_len; /* 外层MAC头开始的数据长度 */ flwd_terminal_proto_t terminal_proto; const char *inner_ip_layer_hdr; /* Vxlan(隧道)内层IP包头起始地址, v4 or v6 */ }flwd_raw_pkt_t; typedef struct{ const char *device_name; const char *pkt_filter; /* 可能某些驱动不支持 */ flwd_cap_mode_t cap_mode; flwd_topology_t topo_mode; unsigned int device_ip_net_order; /* 本地设备IP地址 */ unsigned int device_ip_mask_net_order; /* 本地设备IP掩码 */ unsigned int gateway_ip_net_order; /* 默认网关IP地址 */ // unsigned int *device_slave_ip_net_order; /* 单网卡双IP模式, 用于物理网卡不够用, 单臂模式接入一个交换机, 同时连接转发网关和接入终端的环境下, 靠不同IP段区分流量 */ // unsigned char device_slave_ip_num; /* 主要用于DNAT的接入网关和真实服务器通信, 如果只有一个IP, 最大并发连接数只有65411个, 必须使用多个虚拟ip地址, 才能保证高并发数 */ unsigned short socket_port_net_order; /* lo本地回环socket通信端口模式 */ unsigned char local_mac_addr[6]; }flwd_io_para_t; typedef struct __flwd_device_handle{ int tot_thread_count; int sapp_send_thread_seq; /* 以插件形式挂载到sapp时, 非包处理线程的发包线程ID */ flwd_io_para_t io_para; /* 物理层接入参数, 网卡名, 捕包模式, 接口IP,MAC地址等等 */ void *low_level_io_handle; int (*low_level_io_init)(struct __flwd_device_handle *h); void (*low_level_io_run)(struct __flwd_device_handle *h); int (*low_level_pkt_recv)(struct __flwd_device_handle *h, int tid, void **mbuff); void (*low_level_pkt_free)(struct __flwd_device_handle *h, int tid, void *mbuff); void *(* low_level_mbuff_malloc)(struct __flwd_device_handle *h, int tid, int len); void (* low_level_mbuff_free)(struct __flwd_device_handle *h, int tid, void *mbuff); void (* low_level_mbuff_free_after_send)(struct __flwd_device_handle *h, int tid, void *mbuff); /* 因marsio模式发送后自动free, 某些库又不自动free, 构造这么一个函数接口 */ void (* low_level_mbuff_send_back)(struct __flwd_device_handle *h, int tid, void *mbuff); /* GDEV模式下, fwd网关回注数据包 */ char * (* low_level_mbuff_mtod)(void *mbuff); char * (*low_level_mbuff_data_append)(void *mbuff, const char *user_data, int user_data_len); char * (*low_level_mbuff_data_forward)(void *mbuff, int n); /* 数据指针向前移动N个字节, 数据长度自动增加N */ char * (*low_level_mbuff_data_rearward)(void *mbuff, int n); /* 数据指针向后移动N个字节, 数据长度自动减少N */ int (*low_level_mbuff_get_pkt_len)(void *mbuff); void (*low_level_mbuff_set_pkt_len)(void *mbuff, int pkt_len); int (*low_level_send)(struct __flwd_device_handle *h, int tid, void *mbuff); /* 发包之前调用low_level_mbuff_malloc(), 发送完后自动free, 无需主动调用low_level_mbuff_free() */ }flwd_device_handle_t; /* 为方便程序处理, 内部为每个表编号, 与maat返回的table_id不同 */ typedef enum{ FLWD_MAAT_TB_IR_POLICY_COMPILE = 0, FLWD_MAAT_TB_IR_POLICY_GROUP, FLWD_MAAT_TB_IR_POLICY_IP, FLWD_MAAT_TB_IR_STATIC_IP_POOL_CB, /* 用户人工配置IP */ FLWD_MAAT_TB_IR_DYN_SIFT_IP_CB, /* 自动发现IP */ FLWD_MAAT_TB_IR_DYN_CONN_IP, /* 扩散IP复用的四元组, 告之其他子系统, 避免系统内冲突混淆 */ FLWD_MAAT_TB_MAX, }flwd_inner_maat_table_id_t; typedef struct{ const char *table_name; int table_id; }flwd_maat_table_info_t; typedef struct{ void *flwd_log_handle; /* MESA-handle_logger日志句柄 */ void *maat_log_handle; void *maat_static_handle; /* redis动静配置分离, maat也要初始化两个不同句柄 */ void *maat_dynamic_handle; /* redis动静配置分离, maat也要初始化两个不同句柄 */ void *flwd_network_conn_table; /* gdev和forward网关之间的互联关系 */ void *flwd_arp_table; /* ARP HASH表, ip做为key, mac_addr为data */ pthread_rwlock_t flwd_arp_htable_rwlock; /* arp表要动态更新, 但htable只有互斥锁, 这里大部分是是读操作, 需要读写锁, 以提高多线程性能 */ time_t cur_time; struct in6_addr zero_ipv6_addr; /* 表示全0的IPv6地址 */ unsigned char zero_mac_addr[6]; /* 表示全0的MAC地址 */ unsigned long long cur_time_usec; /* 从1970-01-01 开始到当前时间的绝对微秒数 */ flwd_device_handle_t global_io_handle[__TOPO_MODE_MAX]; flwd_maat_table_info_t maat_table_info[FLWD_MAAT_TB_MAX]; }flwd_global_val_t; /* 全局变量 */ typedef struct{ unsigned long long eth_pkt_num; unsigned long long ip_pkt_num; unsigned long long tcp_pkt_num; unsigned long long udp_pkt_num; unsigned long long eth_pkt_byte; unsigned long long ip_pkt_byte; unsigned long long tcp_pkt_byte; unsigned long long udp_pkt_byte; }flwd_pkt_stat_t; /* 数据包流量统计信息 */ /* 源、目的方向以当前来包为准 */ struct __flwd_tuple4{ unsigned char addr_type; /* 把地址类型防止结构体最前, 便于v4, v6互相识别 */ unsigned char protocol; /* TCP和UDP四元组需要区别开, 增加协议字段 */ unsigned short sport_net_order; unsigned short dport_net_order; unsigned int sip_net_order; unsigned int dip_net_order; }__attribute__((packed)); typedef struct __flwd_tuple4 flwd_tuple4v4_t; /* 源、目的方向以当前来包为准 */ struct __flwd_tuple6{ unsigned char addr_type; /* 把地址类型防止结构体最前, 便于v4, v6互相识别 */ unsigned char protocol; /* TCP和UDP四元组需要区别开, 增加协议字段 */ unsigned short sport_net_order; unsigned short dport_net_order; struct in6_addr sip_net_order; struct in6_addr dip_net_order; }__attribute__((packed)); typedef struct __flwd_tuple6 flwd_tuple4v6_t; typedef struct{ unsigned int gdev_ip_net_order; /* 记录当初捕获这个IP的GDEV IP, 实际就是本业务的loopback ip, 根据region_id, dev_id通过查表得到 */ unsigned char region_id; /* 出口编号 */ unsigned char dev_id; /* 设备编号 */ unsigned char link_id; /* 当前IP被捕获时的链路号 */ unsigned char this_ip_as_sip_route_dir; /* 此活跃IP作为源IP时, 在INLINE设备的方向位, 一定要是TCP按SYN建连接的流量, udp可能会交换sip和dip, 因为都是做为源地址, 通常此值不会变 */ unsigned char inner_raw_smac[6]; unsigned char inner_raw_dmac[6]; }flwd_gdev_associated_args_t; /* 存储包转发过程中的底层路由信息, 避免每次都动态查路由表, ARP表, 用空间换时间!!! SNAT: access网关: 接收客户端数据包, 记录客户端源MAC; 根据选择的活跃IP所在位置, 动态查询下一跳fwd网关, 记录fwd_ip, fwd_mac; fwd网关: 接收来自access的包, 创建hash表, 记录access_ip, access_mac; 当收到来自gdev的回复包, 直接取出nat_info中的access_ip, access_mac, 直接转发(从哪来的从哪回去). DNAT: fwd网关: 接收到来自gdev的首包, 动态查询dip经过哪台access网关可以到达, 记录access_ip, access_mac; access网关: 接收到来自fwd的首包, 记录fwd_ip, fwd_mac, 再收到真实服务器应答时, 取出存储的ip,mac直接转发(从哪来的从哪回去). */ typedef struct{ flwd_gdev_associated_args_t gdev_args; unsigned char inner_terminal_mac[6]; /* 主要用于access_gateway, 客户端或真实服务器的MAC, SNAT的应答包不用查ARP表; DNAT的首包需要查询ARP表一次 */ unsigned int next_gateway_ip_net; /* 主要用于access_gateway和fwd_gateway, 下一跳网关的IP, 实际就是vxlan外层IP地址, 不区别v4,v6, 肯定采用IPv4地址 */ unsigned char next_gateway_mac[6]; /* 主要用于access_gateway和fwd_gateway, 下一跳网关的MAC */ }flwd_route_info_t; typedef struct{ unsigned char tid; /* 主要用于htable回调函数内部的tid参数传递 */ unsigned char reference; /* 此结构在htable中被两个不同的key索引, 增加引用计数, 防止double free */ unsigned char act_ip_region; /* flwd_ip_region_type_t, 记录活跃IP所在地理位置, 在连接结束后, 回收端口时使用 */ unsigned char act_ip_origin; /* flwd_active_ip_type_t, 动态发现 or 用户界面配置 */ flwd_nat_type_t nat_type; flwd_tuple5_t inner_nat_tuple5; /* 对于SNAT, 就是局域网内部真实客户端的包; */ flwd_tuple5_t outer_nat_tuple5; /* 对于SNAT, 就是出网关, Internet上的包; */ signed long long signature_param; /* 数据包指纹协议相关特定参数, 对于TCP来说, 是SYN-ISN偏移量; 对于DNS来说, 是transid偏移量; 此值是after-nat的参数转为64bit long long 型减去原始pre-nat(64bit)后的参数之差 */ flwd_route_info_t flwd_route_info; #if FLWD_USE_LTSM_FOR_QUICK_CLOSE void *ltsm_stat_handle; #endif }flwd_nat_info_t; typedef struct{ unsigned char tid; /* 主要用于htable回调函数内部的tid参数传递 */ flwd_route_info_t flwd_route_info; }flwd_fwd_nat_info_t; typedef struct{ flwd_pkt_stat_t pkt_stat; /* IP地址池htable */ void *flwd_ip_pool_dynamic_htable[2][2]; /* 使用一个二维数组, 三个维度分别是: [v4_or_v6][inland_or_outland], 因dynamic和static存储和key不一样, 分开两个变量 */ void *flwd_ip_pool_static_htable[2][2]; /* 使用一个二维数组, 三个维度分别是: [v4_or_v6][inland_or_outland], 因dynamic和static存储和key不一样, 分开两个变量 */ pthread_rwlock_t flwd_ip_pool_dynamic_rwlock; pthread_rwlock_t flwd_ip_pool_static_rwlock; void *nat_info_table; /* access和fwd网关复用同一个变量, TCP和UDP同属一个htable, protocol也作为key的一部分 */ flwd_ippair_v6_t nat_key_ipv6_buf; /* 构造函数栈内的nat_key时, 因flwd_tuple5_t的ipv6地址类型是指针, 避免每次动态malloc/free, 此处为每个线程分配一个临时的ipv6地址缓冲区 */ struct stream_tuple4_v4 addrv4_convert_buf; /* flwd_tuple5_to_stream_addr()函数用的内存缓冲区 */ struct stream_tuple4_v6 addrv6_convert_buf; /* flwd_tuple5_to_stream_addr()函数用的内存缓冲区 */ char __pad[56]; }flwd_global_thread_t; /* 多线程全局变量, 注意64字节缓存对齐 */ typedef struct{ int tot_thread_count; int flwd_log_level; int nat_htable_max_num; int nat_htable_timeout; int global_access_gateway_num; /* 全局一共有多少个接入网关, 用于区别使用活跃IP的源端口 */ int current_access_gateway_id; /* 当前接入网关的唯一ID, 从1开始 */ int use_static_pool_ip_if_no_dynamic; /* 开启后, 如果当前没有可用动态IP, 则从静态配置中随机选一个 */ int use_dynamic_pool_ip_if_no_static; /* 开启后, 如果命中了策略, 但是static_pool当前没有可用IP, 则从动态池中随机选一个 */ const char *maat_json_cfg_file; const char* table_info_path; /* maat_cfg */ const char* full_cfg_dir; /* maat_cfg */ const char* inc_cfg_dir; /* maat_cfg */ }flwd_global_cfg_t; /* 全局配置文件参数 */ typedef struct{ flwd_topology_t topo_mode; const char *cfg_file_section; char addr_para[64]; }flwd_packet_io_cfg_para_t; typedef struct { unsigned char is_valid; unsigned int policy_group_id; /* 动态IP为0 */ flwd_ip_region_type_t ip_region_type; /* 域内、域外 */ flwd_active_ip_type_t ip_origin_type; /* 动态, 静态 */ flwd_ip_t active_ip_net_order; flwd_gdev_associated_args_t gdev_args; MESA_list_t active_ip_list_node; /* 静态IP以group_id为key, 相同的group_id的IP使用链表索引; 为了方便处理, quiddity指向本结构体头部, 使用get_entry_of太麻烦; head头节点无数据 */ MESA_list_count_t usable_tcp_sport_list_head; /* TCP可用端口链表, 初始化时, 链表全填满可用端口, 申请一个就从链表中移除一个, 链接结束释放时, 再插入链表, 使用MESA_list_count_t, 便于计数 */ MESA_list_count_t usable_udp_sport_list_head; /* UDP可用端口链表, 初始化时, 链表全填满可用端口, 申请一个就从链表中移除一个, 链接结束释放时, 再插入链表, 使用MESA_list_count_t, 便于计数 */ }flwd_active_ip_t; /* 选择活跃IP的同时, 根据当前IP的已有记录, 同时选择一个可用源端口 */ typedef struct{ int tid; flwd_ip_region_type_t dip_region_type; /* 当前四元组真实目标IP所在地理位置 */ flwd_tuple5_t *nat_key; /* 原始的四元组信息, 对于SNAT就是inner_tuple5, 对于DNAT就是outer_tuple5 */ flwd_ip_t act_sip_net_order; /* 可用的活跃IP地址 */ flwd_gdev_associated_args_t gdev_args; unsigned short act_sport_net_order; unsigned char protocol; /* 此值仅用于htable回调函数传递参数用, 返回时就无意义了 */ }flwd_active_ip_port_args_t; /* 因为活跃IP是MAAT回调表, 更新是独立于包处理线程, 为了操作同一个htable而不加锁, 使用一个小trick: op_flag最低三个bit定义: 0位: 0:callback线程已经处理完, 包处理线程可以读取并插入本线程的htable; 1:包处理线程已经处理完, 临时为NULL, callback线程可以继续添加新数据. 1位: 1:被包处理线程占用; 0:包处理线程释放; 2位: 1:被callback线程占用; 0:callback线程释放; 第1bit 第0bit 第0bit --------|--------|--------| cb proc valid 收到更新后, 将数据库表转换成flwd_active_ip_t二进制格式, 根据不同的线程设置不同的端口, 然后操作每个线程的flwd_temp_active_ip_op_flag=1, */ //#define FLWD_ACT_IP_OP_MASK_DATA_VALID (0x1) #define FLWD_ACT_IP_OP_OR_MASK_BY_PROC (0x1) /* 增加标记位 */ #define FLWD_ACT_IP_OP_OR_MASK_BY_CALLBACK (0x2) /* 增加标记位 */ #define FLWD_ACT_IP_OP_AND_MASK_BY_PROC (0xFFFE) /* 清空标记位 */ #define FLWD_ACT_IP_OP_AND_MASK_BY_CALLBACK (0xFFFD) /* 清空标记位 */ extern flwd_global_cfg_t flwd_cfg_val; extern flwd_global_val_t flwd_global_val; extern flwd_global_thread_t flwd_thread_val[FLWD_MAX_THREAD_NUM]; extern const flwd_packet_io_cfg_para_t g_packet_io_cfg_para[__TOPO_MODE_MAX]; extern MESA_ATOMIC_T flwd_temp_active_ip_op_flag[FLWD_MAX_THREAD_NUM]; extern MESA_lqueue_head flwd_temp_active_ip_to_deal[FLWD_MAX_THREAD_NUM]; #define flwd_log(level, fmt, args...) do{ if(level >= flwd_cfg_val.flwd_log_level){MESA_handle_runtime_log(flwd_global_val.flwd_log_handle, level, "flowood", fmt, ##args);}}while(0) #endif