/* 通用协议栈模块, 处理如ARP, ICMP协议的处理. */ #include "flowood.h" #include "flowood_fun.h" #include "flwd_net.h" #include "MESA_handle_logger.h" #include #include #include #include #include #include #include #include const unsigned char G_FLWD_BROADCAST_ADDR[ETH_ALEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; /* TODO: 本机接口增加IP掩码和默认路由, 实现完整的IP协议栈, 跟目标主机可能不在一个网段(跨路由器或VLAN), 需要主动发送arp给下一跳网关. */ static inline int sendpacket_in_cksum(u_int16_t *addr, int len) { int sum; int nleft; u_int16_t ans; u_int16_t *w; sum = 0; ans = 0; nleft = len; w = addr; while (nleft > 1) { sum += *w++; nleft -= 2; } if (nleft == 1) { *(char *)(&ans) = *(char *)w; sum += ans; } return (sum); } /* * Checksum stuff */ #define SENDPACKET_CKSUM_CARRY(x) \ (x = (x >> 16) + (x & 0xffff), (~(x + (x >> 16)) & 0xffff)) int flwd_sendpacket_do_checksum(char *buf, int protocol, int len) { flwd_ipv4_hdr_t *iph_p; flwd_ipv6_hdr_t *ip6h_p; int ip_hl; int sum; int is_ipv6 = 0; sum = 0; iph_p = (flwd_ipv4_hdr_t *)buf; if(4 == iph_p->ip_v) /* IP版本号字段,IPv4和IPv6格式是相同的 */ { ip_hl = iph_p->ip_hl << 2; ip6h_p = NULL; } else if(6 == iph_p->ip_v) { ip6h_p = (flwd_ipv6_hdr_t *)buf; iph_p = NULL; ip_hl = sizeof(flwd_ipv6_hdr_t); is_ipv6 = 1; } else { return (-1); } /* * Dug Song came up with this very cool checksuming implementation * eliminating the need for explicit psuedoheader use. Check it out. */ switch (protocol) { /* * Style note: normally I don't advocate declaring variables inside * blocks of control, but it makes good sense here. -- MDS */ case IPPROTO_TCP: { flwd_tcp_hdr_t *tcph_p = (flwd_tcp_hdr_t *)(buf + ip_hl); #if (STUPID_SOLARIS_CHECKSUM_BUG) tcph_p->th_sum = tcph_p->th_off << 2; return (1); #endif /* STUPID_SOLARIS_CHECKSUM_BUG */ tcph_p->th_sum = 0; /* 2012-03-19 LiJia add, for IPv6 */ if(is_ipv6) { sum = sendpacket_in_cksum((u_int16_t *)&ip6h_p->ip6_src, 32); } else { sum = sendpacket_in_cksum((u_int16_t *)&iph_p->ip_src, 8); } sum += ntohs(IPPROTO_TCP + len); sum += sendpacket_in_cksum((u_int16_t *)tcph_p, len); tcph_p->th_sum = SENDPACKET_CKSUM_CARRY(sum); break; } case IPPROTO_UDP: { flwd_udp_hdr_t *udph_p = (flwd_udp_hdr_t *)(buf + ip_hl); udph_p->uh_sum = 0; /* 2012-03-19 LiJia add, for IPv6 */ if(is_ipv6) { sum = sendpacket_in_cksum((u_int16_t *)&ip6h_p->ip6_src, 32); } else { sum = sendpacket_in_cksum((u_int16_t *)&iph_p->ip_src, 8); } sum += ntohs(IPPROTO_UDP + len); sum += sendpacket_in_cksum((u_int16_t *)udph_p, len); udph_p->uh_sum = SENDPACKET_CKSUM_CARRY(sum); break; } case IPPROTO_IP: /* Dummy protocol for TCP. */ { iph_p->ip_sum = 0; sum = sendpacket_in_cksum((u_int16_t *)iph_p, len); iph_p->ip_sum = SENDPACKET_CKSUM_CARRY(sum); break; } case IPPROTO_ICMP: { flwd_icmp_hdr_t *icmph_p = (flwd_icmp_hdr_t *)(buf + ip_hl); icmph_p->icmp_sum = 0; sum = sendpacket_in_cksum((u_short *)icmph_p, len); icmph_p->icmp_sum = SENDPACKET_CKSUM_CARRY(sum); break; } default: { return (-1); } } return (1); } /* 局域网中乱七八糟的包(广播、组播, 非本机IP,MAC的包)识别并丢弃. args: check_dip_expect_cmp_res: 检测目标IP和本机网卡IP的关系, user->acc_gateway不能检测, 因为dip肯定是外网的IP; acc_gateway->fwd_gateway, fwd_gateway->acc_gateway必须检测, 因为是局域网内通信; gdev->fwd_gateway必须检测, 如果使用marsio驱动, 则由驱动实现. */ int flwd_rubbish_pkt_identify(flwd_device_handle_t *device_handle, flwd_raw_pkt_t *raw_pkt, int check_dip_expect_cmp_res) { const flwd_eth_hdr_t *eth_hdr = (const flwd_eth_hdr_t *)raw_pkt->outer_pkt_data; flwd_ipv4_hdr_t *ip4hdr; flwd_ipv6_hdr_t *ip6hdr; unsigned eth_pro_type = ntohs(eth_hdr->h_proto); if(CAP_MODEL_SOCKET == device_handle->io_para.cap_mode){ flwd_eth_hdr_t *inner_eth_hdr; if((unsigned int)raw_pkt->inner_pkt_len < sizeof(flwd_eth_hdr_t) + sizeof(flwd_ipv4_hdr_t) + sizeof(flwd_vxlan_hdr_t)){ return 1; } inner_eth_hdr = (flwd_eth_hdr_t *)raw_pkt->inner_pkt_data; if(ETH_P_IP == ntohs(inner_eth_hdr->h_proto)){ ip4hdr = (flwd_ipv4_hdr_t *)(raw_pkt->inner_pkt_data + sizeof(flwd_eth_hdr_t)); if(ip4hdr->ip_v != 4){ return 1; } if(ip4hdr->ip_hl < 5){ return 1; } if(ntohs(ip4hdr->ip_len) != (raw_pkt->inner_pkt_len - sizeof(flwd_eth_hdr_t))){ return 1; } }else if(ETH_P_IPV6 != ntohs(inner_eth_hdr->h_proto)){ ip6hdr = (flwd_ipv6_hdr_t *)(flwd_ipv6_hdr_t *)(raw_pkt->inner_pkt_data + sizeof(flwd_eth_hdr_t)); if((ip6hdr->ip6_flags[0] & 0xF0) != 6){ return 1; } if(ntohs(ip6hdr->ip6_payload_len) != (raw_pkt->inner_pkt_len - sizeof(flwd_eth_hdr_t) - sizeof(flwd_ipv6_hdr_t))){ return 1; } }else{ return 1; } } if(device_handle->io_para.cap_mode != CAP_MODEL_SOCKET){ /* 在混杂捕包模式下检测 */ if(memcmp(G_FLWD_BROADCAST_ADDR, eth_hdr->h_dest, ETH_ALEN) == 0){ if(ETH_P_ARP != eth_pro_type){ /* 如果是广播但不是ARP, 此类包肯定不应该出NAT网关, 直接丢弃 */ return 1; } }else if(memcmp(device_handle->io_para.local_mac_addr, eth_hdr->h_dest, ETH_ALEN) != 0){ /* 不是广播, 目标MAC也不是本机, 丢弃 */ return 1; } #if 0 /* IPv6的邻居发现包使用组播地址实现, 类似IPv4的ARP, 需要处理, 不能丢弃 */ else if((eth_hdr->h_dest[0] & 0x01) == 0x01){ /* 组播MAC地址, 通常为局域网内控制类数据包, 如LLMNR, SPT等协议, 一般无需处理 */ return 1; } #endif if(ETH_P_IP == eth_pro_type){ ip4hdr = (flwd_ipv4_hdr_t *)((char *)eth_hdr + sizeof(flwd_eth_hdr_t)); if(FLWD_IPV4_MULTICAST_ADDR(ntohl(ip4hdr->ip_dst.s_addr)) != 0){ return 1; /* 组播IP地址, 不处理 */ } if(check_dip_expect_cmp_res != (ip4hdr->ip_dst.s_addr == device_handle->io_para.device_ip_net_order)){ return 1; } } } return 0; } static int flwd_protocol_stack_icmp_layer_process( flwd_device_handle_t *io_handle, int tid, flwd_raw_pkt_t *raw_pkt, flwd_ipv4_hdr_t *raw_ip_hdr, const flwd_simple_icmp_hdr_t *raw_icmp_hdr) { int ret; void *io_mbuff; char *send_user_buf; int raw_ip_tot_len = ntohs(raw_ip_hdr->ip_len); int icmp_payload_len = raw_ip_tot_len - raw_ip_hdr->ip_hl * 4 - sizeof(flwd_simple_icmp_hdr_t); ///flwd_eth_hdr_t *snd_eth_hdr; const flwd_eth_hdr_t *raw_eth_hdr = (const flwd_eth_hdr_t *)raw_pkt->outer_pkt_data; /* 为适应不同底层驱动, 新申请内存, 构造ICMP_REPLY包再发送, 而不是直接修改原始报文 */ io_mbuff = io_handle->low_level_mbuff_malloc(io_handle, tid, raw_pkt->outer_pkt_len); assert(io_mbuff != NULL); send_user_buf = (char *)io_handle->low_level_mbuff_mtod(io_mbuff); flwd_sendpacket_build_icmpv4_echo(ICMP_ECHOREPLY, 0, 0, raw_icmp_hdr->icd_id, raw_icmp_hdr->icd_seq, (char *)raw_icmp_hdr + sizeof(flwd_simple_icmp_hdr_t), icmp_payload_len, send_user_buf + sizeof(flwd_eth_hdr_t) + sizeof(flwd_ipv4_hdr_t)); flwd_sendpacket_build_ipv4(raw_ip_tot_len - sizeof(flwd_ipv4_hdr_t), 0, 0x1234, 0, 64, IPPROTO_ICMP, raw_ip_hdr->ip_dst.s_addr, /* 地址取反 */ raw_ip_hdr->ip_src.s_addr, /* 地址取反 */ NULL, 0, send_user_buf + sizeof(flwd_eth_hdr_t)); /* 计算校验和 */ flwd_sendpacket_do_checksum(send_user_buf + sizeof(flwd_eth_hdr_t), IPPROTO_IP, sizeof(flwd_ipv4_hdr_t)); flwd_sendpacket_do_checksum(send_user_buf + sizeof(flwd_eth_hdr_t), IPPROTO_ICMP, icmp_payload_len + sizeof(flwd_simple_icmp_hdr_t)); flwd_sendpacket_build_ethernet(ETH_P_IP, raw_eth_hdr->h_dest, raw_eth_hdr->h_source, send_user_buf); io_handle->low_level_mbuff_set_pkt_len(io_mbuff, raw_pkt->outer_pkt_len); ret = io_handle->low_level_send(io_handle, tid, io_mbuff); if(ret < 0){ flwd_log(RLOG_LV_FATAL, "send icmp reply error!"); }else{ char icmp_dip_str[16]; inet_ntop(AF_INET, &raw_ip_hdr->ip_dst.s_addr, icmp_dip_str, 16); flwd_log(RLOG_LV_DEBUG, "ICMP: recv icmp request to %s, send icmp reply!\n", icmp_dip_str); } io_handle->low_level_mbuff_free_after_send(io_handle, tid, io_mbuff); return ret; } static int flwd_protocol_stack_ipv4_layer_process( flwd_device_handle_t *io_handle, int tid, flwd_raw_pkt_t *raw_pkt, flwd_ipv4_hdr_t *iphdr) { const flwd_simple_icmp_hdr_t *flwd_simple_icmp_hdr; if(io_handle->io_para.device_ip_net_order != iphdr->ip_dst.s_addr){ return 0; } if(iphdr->ip_p != IPPROTO_ICMP){ return 0; } flwd_simple_icmp_hdr = (flwd_simple_icmp_hdr_t *)((char *)iphdr + iphdr->ip_hl * 4); if(flwd_simple_icmp_hdr->icmp_type != ICMP_ECHO){ return 0; } flwd_protocol_stack_icmp_layer_process(io_handle, tid, raw_pkt, iphdr, flwd_simple_icmp_hdr); return 1; } static int flwd_protocol_stack_ipv6_layer_process( flwd_device_handle_t *io_handle, int tid, flwd_raw_pkt_t *raw_pkt, flwd_ipv6_hdr_t *ip6hdr) { if(IPPROTO_ICMPV6 == ip6hdr->ip6_nxt_hdr){ /* TODO, 从ICMPv6包中看类型是否是Netighbor 发现协议, 取出IP地址, 查看是否是本机, 然后回复应答包. */ } return 0; } static int flwd_protocol_stack_arp_layer_process( flwd_device_handle_t *io_handle, int tid, flwd_raw_pkt_t *raw_pkt, flwd_arp_hdr_t *arp_hdr) { int ret; void *io_mbuff; char *send_user_buf; const flwd_eth_hdr_t *raw_eth_hdr; if(memcmp(arp_hdr->ar_tpa, &io_handle->io_para.device_ip_net_order, sizeof(int)) != 0){ return 1; /* 非本机ARP, 但是也返回1, 让外部调用者不再处理本数据包, 但本函数也不回复ARP应答 */ } if(arp_hdr->ar_op == htons(ARPOP_REPLY)){ flwd_arp_response_update(arp_hdr); return 1; } if(arp_hdr->ar_op != htons(ARPOP_REQUEST)){ /* 只处理REQUEST和REPLY, 其他类型不处理 */ return 1; } /* 为适应不同底层驱动, 新申请内存, 构造ICMP_REPLY包再发送, 而不是直接修改原始报文 */ io_mbuff = io_handle->low_level_mbuff_malloc(io_handle, tid, raw_pkt->outer_pkt_len); assert(io_mbuff != NULL); send_user_buf = (char *)io_handle->low_level_mbuff_mtod(io_mbuff); flwd_sendpacket_build_arp(ARPHRD_ETHER, ETH_P_IP, arp_hdr->ar_hln, arp_hdr->ar_pln, ARPOP_REPLY, io_handle->io_para.local_mac_addr, arp_hdr->ar_tpa, arp_hdr->ar_sha, arp_hdr->ar_spa, send_user_buf + sizeof(flwd_eth_hdr_t)); raw_eth_hdr = (const flwd_eth_hdr_t *)raw_pkt->outer_pkt_data; flwd_sendpacket_build_ethernet(ETH_P_ARP, io_handle->io_para.local_mac_addr, raw_eth_hdr->h_source, send_user_buf); io_handle->low_level_mbuff_set_pkt_len(io_mbuff, raw_pkt->outer_pkt_len); ret = io_handle->low_level_send(io_handle, tid, io_mbuff); if(ret < 0){ flwd_log(RLOG_LV_FATAL, "send arp reply error!"); }else{ char arp_dip_str[16]; inet_ntop(AF_INET, arp_hdr->ar_tpa, arp_dip_str, 16); flwd_log(RLOG_LV_DEBUG, "ARP: recv arp request to %s, send arp reply!\n", arp_dip_str); } io_handle->low_level_mbuff_free_after_send(io_handle, tid, io_mbuff); return 1; /* ARP协议肯定不是flowood模块要的数据, 不管是不是本机的, 都固定返回1 */ } /* IP层协议栈相关流量处理, 比如arp请求, icmp请求等. return value: 1: 是协议栈流量, 无需flowood模块继续处理; 0: 不是协议栈流量, 是IP复用流量. */ int flwd_protocol_stack_process(flwd_device_handle_t *io_handle, int tid, flwd_raw_pkt_t *raw_pkt) { int is_stack = 0; const flwd_eth_hdr_t *flwd_ethhdr = (const flwd_eth_hdr_t *)raw_pkt->outer_pkt_data; switch(ntohs(flwd_ethhdr->h_proto)){ case ETH_P_IP: is_stack = flwd_protocol_stack_ipv4_layer_process(io_handle, tid, raw_pkt, (flwd_ipv4_hdr_t *)((char *)flwd_ethhdr + sizeof(flwd_eth_hdr_t))); flwd_thread_val[tid].pkt_stat.ip_pkt_num++; flwd_thread_val[tid].pkt_stat.ip_pkt_byte += raw_pkt->outer_pkt_len; break; case ETH_P_IPV6: is_stack = flwd_protocol_stack_ipv6_layer_process(io_handle, tid, raw_pkt, (flwd_ipv6_hdr_t *)((char *)flwd_ethhdr + sizeof(flwd_eth_hdr_t))); break; case ETH_P_ARP: is_stack = flwd_protocol_stack_arp_layer_process(io_handle, tid, raw_pkt, (flwd_arp_hdr_t *)((char *)flwd_ethhdr + sizeof(flwd_eth_hdr_t))); break; default: break; } return is_stack; }