用户态协议栈02-arp reply实现

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 用户态协议栈02-arp reply实现

在上一节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代理ARPNDP用于在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


相关文章
|
6天前
|
网络协议 安全 NoSQL
网络空间安全之一个WH的超前沿全栈技术深入学习之路(8-2):scapy 定制 ARP 协议 、使用 nmap 进行僵尸扫描-实战演练、就怕你学成黑客啦!
scapy 定制 ARP 协议 、使用 nmap 进行僵尸扫描-实战演练等具体操作详解步骤;精典图示举例说明、注意点及常见报错问题所对应的解决方法IKUN和I原们你这要是学不会我直接退出江湖;好吧!!!
网络空间安全之一个WH的超前沿全栈技术深入学习之路(8-2):scapy 定制 ARP 协议 、使用 nmap 进行僵尸扫描-实战演练、就怕你学成黑客啦!
|
1月前
|
缓存 网络协议 Linux
Python渗透测试之ARP毒化和协议应用
Python渗透测试之ARP毒化和协议应用
|
3月前
|
网络协议
用户态协议栈04-定时arp-table的实现
用户态协议栈04-定时arp-table的实现
|
3月前
|
网络协议 安全 网络安全
ARP协议详解及其工作原理
【8月更文挑战第31天】
88 0
|
3月前
|
存储 缓存 监控
|
3月前
|
存储 缓存 网络协议
MAC协议原理与ARP协议
总结一下,MAC协议是控制同一网络媒介上多个设备的数据访问的规范,而ARP是解决局域网络中的IP地址到MAC地址的转换问题,以确保IP包能在本地网络上传输到正确的设备。尽管这两种协议服务于网络通信中的不同层面,但它们都是网络正常操作的基本要素,保证了数据能从一个设备准确无误地传递到另一个设备。
40 0
|
4月前
|
存储 缓存 网络协议
ARP 地址解析协议
ARP 地址解析协议
69 0
|
4月前
|
人工智能 缓存 网络协议
网络层之三层交换、icmp协议、arp协议
网络层之三层交换、icmp协议、arp协议
|
5月前
|
存储 缓存 网络协议
|
6月前
|
缓存 网络协议 安全
【网络工程师】<软考中级>解析协议ARP&路由协议RIP/OSPF/BGP
【1月更文挑战第27天】【网络工程师】<软考中级>解析协议ARP&路由协议RIP/OSPF/BGP