在上一节DODK的UDP收发中发送udp包的时候,需要向物理机的arp表中添加一个静态的arp记录。这在生产环境中显然是不可以的。在内核的协议栈中,会将自己的ip和mac在局域网中进行广播,并且记录其他电脑的ip和mac。在需要发送数据包的时候,查询arp表来获取目标的地址构建发送数据包。在实现arp reply之后,dpdk可以回复收到的arp数据包,让对方在arp表中添加一条动态的记录,这样就不需要添加静态的arp记录了。
arp协议
地址解析协议,即ARP(Address Resolution Protocol),是根据IP地址获取物理地址的一个TCP/IP协议。主机发送信息时将包含目标IP地址的ARP请求广播到局域网络上的所有主机,并接收返回消息,以此确定目标的物理地址;收到返回消息后将该IP地址和物理地址存入本机ARP缓存中并保留一定时间,下次请求时直接查询ARP缓存以节约资源。地址解析协议是建立在网络中各个主机互相信任的基础上的,局域网络上的主机可以自主发送ARP应答消息,其他主机收到应答报文时不会检测该报文的真实性就会将其记入本机ARP缓存;由此攻击者就可以向某一主机发送伪ARP应答报文,使其发送的信息无法到达预期的主机或到达错误的主机,这就构成了一个ARP欺骗。ARP命令可用于查询本机ARP缓存中IP地址和MAC地址的对应关系、添加或删除静态对应关系等。相关协议有RARP、代理ARP。NDP用于在IPv6中代替地址解析协议。
arp报文结构
协议解析
- 硬件类型:指明了发送方想知道的硬件接口类型,以太网的值为1
- 协议类型:指明了发送方提供的高层协议类型,IP为0800(16进制)
- 硬件地址长度和协议长度:指明了硬件地址和高层协议地址的长度,这样ARP报文就可以在任意硬件和任意协议的网络中使用
- 操作类型:用来表示这个报文的类型,ARP请求为1,ARP响应为2,RARP请求为3,RARP响应为4
- 发送方硬件地址(0-3字节):源主机硬件地址的前3个字节
- 发送方硬件地址(4-5字节):源主机硬件地址的后3个字节
- 发送方IP地址(0-1字节):源主机硬件地址的前2个字节
- 发送方IP地址(2-3字节):源主机硬件地址的后2个字节
- 目标硬件地址(0-1字节):目的主机硬件地址的前2个字节
- 目标硬件地址(2-5字节):目的主机硬件地址的后4个字节
- 目标IP地址(0-3字节):目的主机的IP地址
DPDK实现
在UDP的基础上加上ARP的实现,和UDP一样,都分为报文解析和报文组织两个部分。
报文解析
if(ethhdr->ether_type == rte_cpu_to_be_16(RTE_ETHER_TYPE_ARP)) { struct rte_arp_hdr* arphdr = rte_pktmbuf_mtod_offset(mbufs[i], struct rte_arp_hdr*, sizeof(struct rte_ether_hdr)); struct in_addr addr; addr.s_addr = arphdr->arp_data.arp_tip; printf("arp --> src: %s ", inet_ntoa(addr)); addr.s_addr = gLocalIp; printf("local: %s\n", inet_ntoa(addr)); if(arphdr->arp_data.arp_tip == gLocalIp) { struct rte_mbuf* arpbuf = ng_send_arp(mbuf_pool, arphdr->arp_data.arp_sha.addr_bytes, gLocalIp, arphdr->arp_data.arp_sip); rte_eth_tx_burst(gDpdkPortId, 0, &arpbuf, 1); rte_pktmbuf_free(arpbuf); rte_pktmbuf_free(mbufs[i]); arpbuf = NULL; mbufs[i] = NULL; } continue; }
ARP协议工作在数据链路层,主要传递的信息是IP和MAC。在解析完以太网之后,如果他的数据包是ARP数据包,那就对数据包进行解析。针对数据包内容,组织一个新的ARP包进行回发。
报文组织
static int ng_encode_arp_pkt(uint8_t *msg, uint8_t *dst_mac, uint32_t sip, uint32_t dip) { // 1 ethhdr struct rte_ether_hdr *eth = (struct rte_ether_hdr *)msg; rte_memcpy(eth->s_addr.addr_bytes, gSrcMac, RTE_ETHER_ADDR_LEN); rte_memcpy(eth->d_addr.addr_bytes, dst_mac, RTE_ETHER_ADDR_LEN); eth->ether_type = htons(RTE_ETHER_TYPE_ARP); // 2 arp struct rte_arp_hdr *arp = (struct rte_arp_hdr *)(eth + 1); arp->arp_hardware = htons(1); arp->arp_protocol = htons(RTE_ETHER_TYPE_IPV4); arp->arp_hlen = RTE_ETHER_ADDR_LEN; arp->arp_plen = sizeof(uint32_t); arp->arp_opcode = htons(2); rte_memcpy(arp->arp_data.arp_sha.addr_bytes, gSrcMac, RTE_ETHER_ADDR_LEN); rte_memcpy( arp->arp_data.arp_tha.addr_bytes, dst_mac, RTE_ETHER_ADDR_LEN); arp->arp_data.arp_sip = sip; arp->arp_data.arp_tip = dip; return 0; } static struct rte_mbuf *ng_send_arp(struct rte_mempool *mbuf_pool, uint8_t *dst_mac, uint32_t sip, uint32_t dip) { const unsigned total_length = sizeof(struct rte_ether_hdr) + sizeof(struct rte_arp_hdr); struct rte_mbuf *mbuf = rte_pktmbuf_alloc(mbuf_pool); if (!mbuf) { rte_exit(EXIT_FAILURE, "rte_pktmbuf_alloc\n"); } mbuf->pkt_len = total_length; mbuf->data_len = total_length; uint8_t *pkt_data = rte_pktmbuf_mtod(mbuf, uint8_t *); ng_encode_arp_pkt(pkt_data, dst_mac, sip, dip); return mbuf; }
这里的步骤和UDP一样,作为对ARP协议的实现,后面的arp_table会有详细的实现,这里是回复arp request使得不需要手动添加静态ARP。
完整代码
#if 1 #include <rte_eal.h> #include <rte_ethdev.h> #include <stdio.h> #include <arpa/inet.h> static int gDpdkPortId = 0; #define MAKE_IPV4_ADDR(a, b, c, d) (a + (b<<8) + (c<<16) + (d<<24)) static uint32_t gLocalIp = MAKE_IPV4_ADDR(192, 168, 1, 111); uint32_t gSrcIp; uint32_t gDstIp; uint16_t gSrcPort; uint16_t gDstPort; uint8_t gSrcMac[RTE_ETHER_ADDR_LEN]; uint8_t gDstMac[RTE_ETHER_ADDR_LEN]; #define MBUF_LEN (4096-1) #define BURST_LEN 64 static const struct rte_eth_conf port_conf_default = { .rxmode = {.max_rx_pkt_len = RTE_ETHER_MAX_LEN} }; static void ng_encode_udp_pkt(uint8_t* msg, uint8_t* data, unsigned total_len) { struct rte_ether_hdr* ethhdr = (struct rte_ether_hdr*)(msg); rte_memcpy(ethhdr->s_addr.addr_bytes, gSrcMac, RTE_ETHER_ADDR_LEN); rte_memcpy(ethhdr->d_addr.addr_bytes, gDstMac, RTE_ETHER_ADDR_LEN); ethhdr->ether_type = htons(RTE_ETHER_TYPE_IPV4); struct rte_ipv4_hdr* iphdr = (struct rte_ipv4_hdr*)(ethhdr + 1); iphdr->version_ihl = 0x45; iphdr->type_of_service = 0; iphdr->total_length = htons(total_len - sizeof(struct rte_ether_hdr)); iphdr->packet_id = 0; iphdr->fragment_offset = 0; iphdr->time_to_live = 64; iphdr->next_proto_id = IPPROTO_UDP; iphdr->src_addr = gSrcIp; iphdr->dst_addr = gDstIp; iphdr->hdr_checksum = 0; iphdr->hdr_checksum = rte_ipv4_cksum(iphdr); struct rte_udp_hdr* udphdr = (struct rte_udp_hdr*)(iphdr + 1); udphdr->src_port = gSrcPort; udphdr->dst_port = gDstPort; uint16_t len = total_len - sizeof(struct rte_ether_hdr) - sizeof(struct rte_ipv4_hdr); udphdr->dgram_len = htons(len); rte_memcpy((uint8_t*)(udphdr + 1), data, len); udphdr->dgram_cksum = 0; udphdr->dgram_cksum = rte_ipv4_udptcp_cksum(iphdr, udphdr); struct in_addr addr; addr.s_addr = gSrcIp; printf("-->Src: %s:%d ", inet_ntoa(addr), ntohs(gSrcPort)); addr.s_addr = gDstIp; printf("Dst:%s:%d %s\n", inet_ntoa(addr), ntohs(gDstPort), data); } static struct rte_mbuf* ng_udp_send(struct rte_mempool* mbuf_pool, uint8_t* data, uint16_t length) { struct rte_mbuf* mbuf = rte_pktmbuf_alloc(mbuf_pool); if(!mbuf) { rte_exit(EXIT_FAILURE, "rte_pktmbuf_alloc\n"); } const unsigned total_len = length + 42; mbuf->pkt_len = total_len; mbuf->data_len = total_len; uint8_t* pkt = rte_pktmbuf_mtod(mbuf, uint8_t*); ng_encode_udp_pkt(pkt, data, total_len); return mbuf; } static int ng_encode_arp_pkt(uint8_t *msg, uint8_t *dst_mac, uint32_t sip, uint32_t dip) { // 1 ethhdr struct rte_ether_hdr *eth = (struct rte_ether_hdr *)msg; rte_memcpy(eth->s_addr.addr_bytes, gSrcMac, RTE_ETHER_ADDR_LEN); rte_memcpy(eth->d_addr.addr_bytes, dst_mac, RTE_ETHER_ADDR_LEN); eth->ether_type = htons(RTE_ETHER_TYPE_ARP); // 2 arp struct rte_arp_hdr *arp = (struct rte_arp_hdr *)(eth + 1); arp->arp_hardware = htons(1); arp->arp_protocol = htons(RTE_ETHER_TYPE_IPV4); arp->arp_hlen = RTE_ETHER_ADDR_LEN; arp->arp_plen = sizeof(uint32_t); arp->arp_opcode = htons(2); rte_memcpy(arp->arp_data.arp_sha.addr_bytes, gSrcMac, RTE_ETHER_ADDR_LEN); rte_memcpy( arp->arp_data.arp_tha.addr_bytes, dst_mac, RTE_ETHER_ADDR_LEN); arp->arp_data.arp_sip = sip; arp->arp_data.arp_tip = dip; return 0; } static struct rte_mbuf *ng_send_arp(struct rte_mempool *mbuf_pool, uint8_t *dst_mac, uint32_t sip, uint32_t dip) { const unsigned total_length = sizeof(struct rte_ether_hdr) + sizeof(struct rte_arp_hdr); struct rte_mbuf *mbuf = rte_pktmbuf_alloc(mbuf_pool); if (!mbuf) { rte_exit(EXIT_FAILURE, "rte_pktmbuf_alloc\n"); } mbuf->pkt_len = total_length; mbuf->data_len = total_length; uint8_t *pkt_data = rte_pktmbuf_mtod(mbuf, uint8_t *); ng_encode_arp_pkt(pkt_data, dst_mac, sip, dip); return mbuf; } static void ng_init_port(struct rte_mempool* mbuf_pool) { int dev_sys_ports = rte_eth_dev_count_avail(); if(dev_sys_ports == 0) { rte_exit(EXIT_FAILURE, "Could not support dpdk\n"); } struct rte_eth_dev_info dev_info; rte_eth_dev_info_get(gDpdkPortId, &dev_info); const int nb_rx_queue = 1; const int nb_tx_queue = 1; struct rte_eth_conf port_conf = port_conf_default; rte_eth_dev_configure(gDpdkPortId, nb_rx_queue, nb_tx_queue, &port_conf); if(rte_eth_rx_queue_setup(gDpdkPortId, 0, 1024, rte_eth_dev_socket_id(gDpdkPortId), NULL, mbuf_pool) < 0) { rte_exit(EXIT_FAILURE, "Could not setup RX queue\n"); } struct rte_eth_txconf txconf = dev_info.default_txconf; txconf.offloads = port_conf_default.rxmode.offloads; if(rte_eth_tx_queue_setup(gDpdkPortId, 0, 1024, rte_eth_dev_socket_id(gDpdkPortId), &txconf) < 0) { rte_exit(EXIT_FAILURE, "Could not setup TX queue\n"); } if(rte_eth_dev_start(gDpdkPortId) < 0) { rte_exit(EXIT_FAILURE, "Could not start\n"); } } int main(int argc, char* argv[]) { if(rte_eal_init(argc, argv) < 0) { rte_exit(EXIT_FAILURE, "Error with eal init\n"); } struct rte_mempool* mbuf_pool = rte_pktmbuf_pool_create("mbuf_pool", MBUF_LEN, 0, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id()); if(!mbuf_pool) { rte_exit(EXIT_FAILURE, "Error with mempool create\n"); } ng_init_port(mbuf_pool); rte_eth_macaddr_get(gDpdkPortId, (struct rte_ether_addr*)gSrcMac); //printf("local mac: %s\n", gSrcMac); while (1) { struct rte_mbuf* mbufs[BURST_LEN]; unsigned nb_pkt = rte_eth_rx_burst(gDpdkPortId, 0, mbufs, BURST_LEN); if (nb_pkt > BURST_LEN) { rte_exit(EXIT_FAILURE, "Error receiving from eth\n"); } for(size_t i = 0; i < nb_pkt; i++) { struct rte_ether_hdr* ethhdr = rte_pktmbuf_mtod(mbufs[i], struct rte_ether_hdr*); if(ethhdr->ether_type == rte_cpu_to_be_16(RTE_ETHER_TYPE_ARP)) { struct rte_arp_hdr* arphdr = rte_pktmbuf_mtod_offset(mbufs[i], struct rte_arp_hdr*, sizeof(struct rte_ether_hdr)); struct in_addr addr; addr.s_addr = arphdr->arp_data.arp_tip; printf("arp --> src: %s ", inet_ntoa(addr)); addr.s_addr = gLocalIp; printf("local: %s\n", inet_ntoa(addr)); if(arphdr->arp_data.arp_tip == gLocalIp) { struct rte_mbuf* arpbuf = ng_send_arp(mbuf_pool, arphdr->arp_data.arp_sha.addr_bytes, gLocalIp, arphdr->arp_data.arp_sip); rte_eth_tx_burst(gDpdkPortId, 0, &arpbuf, 1); rte_pktmbuf_free(arpbuf); rte_pktmbuf_free(mbufs[i]); arpbuf = NULL; mbufs[i] = NULL; } continue; } if(ethhdr->ether_type != rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4)) { rte_pktmbuf_free(mbufs[i]); mbufs[i] = NULL; continue; } struct rte_ipv4_hdr* iphdr = rte_pktmbuf_mtod_offset(mbufs[i], struct rte_ipv4_hdr*, sizeof(struct rte_ether_hdr)); if(iphdr->next_proto_id == IPPROTO_UDP) { struct rte_udp_hdr* udphdr = (struct rte_udp_hdr*)(iphdr + 1); // rte_memcpy(gSrcMac, ethhdr->d_addr.addr_bytes, RTE_ETHER_ADDR_LEN); rte_memcpy(gDstMac, ethhdr->s_addr.addr_bytes, RTE_ETHER_ADDR_LEN); rte_memcpy(&gDstIp, &iphdr->src_addr, sizeof(uint32_t)); rte_memcpy(&gSrcIp, &iphdr->dst_addr, sizeof(uint32_t)); rte_memcpy(&gSrcPort, &udphdr->dst_port, sizeof(uint16_t)); rte_memcpy(&gDstPort, &udphdr->src_port, sizeof(uint16_t)); uint16_t data_len = ntohs(udphdr->dgram_len); *((char*)udphdr + data_len) = '\0'; struct in_addr addr; addr.s_addr = iphdr->src_addr; printf("<--Src: %s:%d ", inet_ntoa(addr), htons(udphdr->src_port)); addr.s_addr = iphdr->dst_addr; printf("Dst: %s:%d %s\n", inet_ntoa(addr), htons(udphdr->dst_port), (char*)(udphdr+1)); struct rte_mbuf* txbuf = ng_udp_send(mbuf_pool, (uint8_t*)(udphdr + 1), data_len); rte_eth_tx_burst(gDpdkPortId, 0, &txbuf, 1); rte_pktmbuf_free(txbuf); rte_pktmbuf_free(mbufs[i]); mbufs[i] = NULL; txbuf = NULL; } } } } #endif