積雲が映像制作したMV『RANGEFINDER』公開中
専門88IO

【C言語】DPDKのTCPヘッダとビットフィールド

専門

IPv4ヘッダとTCPヘッダの構造は以下のようになっている。

IPv4ヘッダ

TCPヘッダ

Linuxのヘッダ構造体では、ビット単位でメンバの領域を指定するビットフィールドを用い、ヘッダ長やフラグといった1B未満のメンバを定義している。

LinuxのIPv4ヘッダ

struct iphdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
    __u8    ihl:4,
        version:4;
#elif defined (__BIG_ENDIAN_BITFIELD)
    __u8    version:4,
        ihl:4;
#else
#error "Please fix <asm/byteorder.h>"
#endif
    __u8    tos;
    __be16  tot_len;
    __be16  id;
    __be16  frag_off;
    __u8    ttl;
    __u8    protocol;
    __sum16 check;
    __struct_group(/* no tag */, addrs, /* no attrs */,
        __be32  saddr;
        __be32  daddr;
    );
    /*The options start here. */
};

LinuxのTCPヘッダ

struct tcphdr {
    __be16  source;
    __be16  dest;
    __be32  seq;
    __be32  ack_seq;
#if defined(__LITTLE_ENDIAN_BITFIELD)
    __u16   res1:4,
        doff:4,
        fin:1,
        syn:1,
        rst:1,
        psh:1,
        ack:1,
        urg:1,
        ece:1,
        cwr:1;
#elif defined(__BIG_ENDIAN_BITFIELD)
    __u16   doff:4,
        res1:4,
        cwr:1,
        ece:1,
        urg:1,
        ack:1,
        psh:1,
        rst:1,
        syn:1,
        fin:1;
#else
#error "Adjust your <asm/byteorder.h> defines"
#endif
    __be16  window;
    __sum16 check;
    __be16  urg_ptr;
};

同様にDPDKのIPv4ヘッダもビットフィールドを用いて定義しているが、DPDKのTCPヘッダはビットフィールドを使用していない。

DPDKのTCPヘッダ

struct rte_tcp_hdr {
    rte_be16_t src_port; /**< TCP source port. */
    rte_be16_t dst_port; /**< TCP destination port. */
    rte_be32_t sent_seq; /**< TX data sequence number. */
    rte_be32_t recv_ack; /**< RX data acknowledgment sequence number. */
    uint8_t  data_off;   /**< Data offset. */
    uint8_t  tcp_flags;  /**< TCP flags */
    rte_be16_t rx_win;   /**< RX flow control window. */
    rte_be16_t cksum;    /**< TCP checksum. */
    rte_be16_t tcp_urp;  /**< TCP urgent pointer, if any. */
} __rte_packed;

フラグを確認する際はビットマスクで論理積を取る。

struct rte_tcp_hdr *tcph;
if (tcph->tcp_flags & RTE_TCP_SYN_FLAG) {
    printf("syn packet\n");
}

また、TCPヘッダ長を示すデータオフセットは4bitのフィールドだが、data_offは予約領域を含めた8bitで定義されているためリトルエンディアン環境では24=16倍の値となる。IPv4ヘッダ長はipv4h->ihl << 2と2bit左シフトで計算し、TCPヘッダ長はtcph->data_off >> 2と2bit右シフトで計算する必要がある。

ビットフィールドは互換性を維持して導入できるものの、データオフセットを表さないdata_offが既にフィールド名として使用されているのがややこしい。

コメント

タイトルとURLをコピーしました