组织tcp数据包

/*
 * 函数名称: ln_tcp_encode
 * 作     者: 黄彦杰 Lenn
 * 设计日期: 2024-08-02
 * 功能描述: 组织tcp数据包
 * 返 回 值: 0
*/
static int ln_tcp_encode(uint8_t* pktbuf, uint32_t sip, uint32_t dip, uint8_t* smac, uint8_t* dmac, ln_tcp_fragment* fragment) {

    unsigned int total_len = fragment->length + sizeof(struct rte_ether_hdr) + sizeof(struct rte_ipv4_hdr) + 
        sizeof(struct rte_tcp_hdr) + fragment->optlen * sizeof(uint32_t);

    struct rte_ether_hdr* ehdr = (struct rte_ether_hdr*)pktbuf;

    rte_memcpy(ehdr->s_addr.addr_bytes, smac, RTE_ETHER_ADDR_LEN);
    rte_memcpy(ehdr->d_addr.addr_bytes, dmac, RTE_ETHER_ADDR_LEN);

    ehdr->ether_type = htons(RTE_ETHER_TYPE_IPV4);

    struct rte_ipv4_hdr* iphdr = (struct rte_ipv4_hdr*)(ehdr + 1);

    iphdr->version_ihl = 0x45;
    iphdr->type_of_service = 0;
    iphdr->total_length = htons(total_len - sizeof(struct rte_ether_hdr));
    iphdr->packet_id = htons(0);
    iphdr->fragment_offset = htons(0);
    iphdr->time_to_live = 64;
    iphdr->next_proto_id = IPPROTO_TCP;
    iphdr->src_addr = sip;
    iphdr->dst_addr = dip;
    iphdr->hdr_checksum = 0;
    iphdr->hdr_checksum = rte_ipv4_cksum(iphdr);

    struct rte_tcp_hdr* tcphdr = (struct rte_tcp_hdr*)(iphdr + 1);
    tcphdr->src_port = fragment->sport;
    tcphdr->dst_port = fragment->dport;
    tcphdr->sent_seq = htonl(fragment->seqnum);
    tcphdr->recv_ack = htonl(fragment->acknum);
    tcphdr->data_off = fragment->hdr_off;
    tcphdr->rx_win = fragment->windows;
    tcphdr->tcp_urp = fragment->urp;



    if (fragment->data != 0) {

        uint8_t* payload = (uint8_t*)(tcphdr + 1) + fragment->optlen * sizeof(uint32_t);
        rte_memcpy(payload, fragment->data, fragment->length);
    }

    tcphdr->cksum = 0;
    tcphdr->cksum = rte_ipv4_udptcp_cksum(iphdr, tcphdr);

    return 0;
}

/*
 * 函数名称: ln_tcp_send
 * 作     者: 黄彦杰 Lenn
 * 设计日期: 2024-08-02
 * 功能描述: tcp数据包发送
 * 返 回 值: 存储tcp数据包的内存
*/
struct rte_mbuf* ln_tcp_send(struct rte_mempool* mbuf_pool, uint32_t sip, uint32_t dip, uint8_t* smac, uint8_t* dmac, ln_tcp_fragment* fragment) {

    int total_len = fragment->length + sizeof(struct rte_ether_hdr) + sizeof(struct rte_ipv4_hdr) + sizeof(struct rte_tcp_hdr) + fragment->optlen * sizeof(uint32_t);

    struct rte_mbuf* mbuf = rte_pktmbuf_alloc(mbuf_pool);

    if (!mbuf) {

        rte_exit(EXIT_FAILED, "Alloc tcp buf failed");
    }
    mbuf->pkt_len = total_len;
    mbuf->data_len = total_len;

    uint8_t* data = rte_pktmbuf_mtod(mbuf, uint8_t*);

    ln_tcp_encode(data, sip, dip, smac, dmac, fragment);

    return mbuf;
}

tcp状态控制

int ln_tcp_process(struct rte_mbuf* mbuf) {

    struct rte_ipv4_hdr* iphdr = rte_pktmbuf_mtod_offset(mbuf, struct rte_ipv4_hdr*, struct rte_ether_hdr);

    struct rte_tcp_hdr* tcphdr = (struct rte_tcp_hdr*)(iphdr + 1);

    uint16_t cksum_cur = tcphdr->cksum;
    tcphdr->cksum = 0;
    tcphdr->cksum = rte_ipv4_udptcp_cksum(iphdr, tcphdr);

    if (tcphdr->cksum != cksum_cur) {

        printf("[check] cur sksum : %d, now cksum %d\n", cksum_cur, tcphdr->cksum);
        return -1;
    }

    ln_tcp_stream* stream = ln_tcp_stream_search(iphdr->src_addr, iphdr->dst_addr, tcphdr->src_port, tcphdr->dst_port);

    if (stream == NULL) {

        stream = ln_tcp_stream_create(iphdr->src_addr, iphdr->dst_addr, tcphdr->src_port, tcphdr->dst_port);

        if (stream == NULL) {

            return -1;
        }
    }

    switch (stream->status) {

        case LN_TCP_STATUS_CLOSED: // client
            break;
        case LN_TCP_STATUS_LISTEN:
            ln_tcp_handle_listen(stream, tcphdr);
            break;
        case LN_TCP_STATUS_SYN_RCVD:
            ln_tcp_handle_syn_revd(stream, tcphdr);
            break;
        case LN_TCP_STATUS_SYN_SENT: // client
            break;
        case LN_TCP_STATUS_ESTABLISHED: // client | server
            uint8_t hdrlen = tcphdr->data_off & 0xF0;

            hdrlen = (hdrlen >> 4);
            uint8_t* payload = (uint8_t*)(tcphdr + 1) + hdrlen * 4;
            printf("--> tcp payload %s\n", padload);
            break;
        case LN_TCP_STATUS_FIN_WAIT_1: // client
            break;
        case LN_TCP_STATUS_FIN_WAIT_2: // client
            break;
        case LN_TCP_STATUS_CLOSING: // client
            break;
        case LN_TCP_STATUS_TIME_WAIT: // client
            break;
        case LN_TCP_STATUS_CLOSE_WAIT: // server
            break;
        case LN_TCP_STATUS_LAST_ACK: // server
            break;
        case default:
            break;
    }
}
  1. 首先,从接收到的mbuf中提取出IP头(rte_ipv4_hdr* iphdr)和TCP头(rte_tcp_hdr* tcphdr)。这里使用了DPDK库函数rte_pktmbuf_mtod_offset()来获取指向特定位置的指针。

  2. 接下来,保存当前的TCP校验和值,并将校验和字段置为0。然后通过调用rte_ipv4_udptcp_cksum()计算新的TCP校验和,并与之前保存的值进行比较,以检查是否有校验和错误。

  3. 然后,通过源IP地址、目标IP地址、源端口号和目标端口号在已建立的TCP流列表中搜索相应的流对象(ln_tcp_stream* stream)。如果找不到匹配的流对象,则创建一个新的流对象。

  4. 根据流对象当前状态,执行相应操作:

  5. LN_TCP_STATUS_CLOSED:客户端状态。

  6. LN_TCP_STATUS_LISTEN:监听状态,执行相关处理。

  7. LN_TCP_STATUS_SYN_RCVD:同步收到状态,执行相关处理。

  8. LN_TCP_STATUS_SYN_SENT:客户端状态。

  9. LN_TCP_STATUS_ESTABLISHED:连接已建立状态(客户端或服务器),打印TCP负载信息。

  10. 其他状态暂未给出具体处理逻辑,可自行补充。

tcp数据包控制

int ln_tcp_out(struct rte_mempool* mbuf_pool) {

    ln_tcp_table* table = get_tcp_table_instance();

    ln_tcp_stream* stream;
    for(stream = table->streams; stream != NULL; stream = stream->next) {

        struct ln_tcp_fragment* fragment = NULL;
        int nb_send = rte_ring_mc_dequeue(stream->sendbuf, (void**)&fragment);

        if (nb_send < 0) {

            continue;
        }

        uint8_t* dst_mac = ln_get_mac_from_arp(stream->sip);

        if (dst_mac == NULL) {

            struct rte_mbuf* arpbuf = ln_arp_send(mbuf_pool, RTE_ARP_OP_REQUEST, nDefaultArpMac, stream->dip, stream->sip);

            struct inout_ring* ring = get_ioring_instance();
            rte_ring_mp_enqueue_burst(ring->out, (void**)&arpbuf);

            rte_ring_mp_enqueue(stream->sendbuf, (void*)fragment);
        }
        else {

            struct rte_mbuf* tcpbuf = ln_tcp_send(mbuf_pool, stream->dip, stream->sip, stream->localmac, dst_mac, fragment);

            struct inout_ring* ring = get_ioring_instance();
            rte_ring_mp_enqueue_burst(ring->out, (void**)&tcpbuf);

            rte_free(fragment);
        }
    }

    return 0;
}
  1. 首先,获取全局的TCP连接表实例(ln_tcp_table* table = get_tcp_table_instance();)。

  2. 然后,遍历所有的TCP流对象(stream),对每个流对象执行以下操作:

  3. 从发送缓冲区(sendbuf)中取出待发送的TCP片段(通过调用rte_ring_mc_dequeue()),存储在指针变量 fragment 中。

  4. 如果没有可发送的数据包,则继续下一次循环。

  5. 获取目标MAC地址(dst_mac):通过源IP地址查询ARP缓存,获取目标MAC地址。如果找不到目标MAC地址,则需要发送ARP请求,并将待发送的ARP请求包加入输出队列。

  6. 如果成功获取到目标MAC地址,则构建要发送的TCP数据包(通过调用 ln_tcp_send()),并将其加入输出队列。

  7. 最后,释放已经处理过的TCP片段内存(通过调用 rte_free(fragment))。

  8. 整个函数遍历完所有流对象后返回。

项目地址

项目地址