DPDK IP reassembly example

Page content
TAILQ_HEAD(ip_pkt_list, ip_frag_pkt); /**< @internal fragments tailq */

자료 구조체

Fragment 관리용 table
struct rte_ip_frag_tbl *frag_tbl;  

locking 없이 IP reassembly를 수행할 단위(통상 core)로 한 개씩 만든다. 즉 하나의 core가 여러 rx queue를 처리하더라도 하나의 frag_tbl만 가지면 된다.
아래 rte_ip_frag_table_create()함수를 이용해서 생성한다.

struct rte_ip_frag_death_row death_row

core별로 갖는 death_row. IP reassembly를 호출한 후 해당 함수내에서 free할 mbuf를 이 리스트에 담아줌.
main loop에서 reassembly작업 후 rte_ip_frag_free_death_row()함수를 호출해 reassembly에 실패한 mbuf를 free함

IP_MAX_FRAG_NUM defines the maximum fragments of one reassembly. Defined same as RTE_LIBRTE_IP_FRAG_MAX_FRAG aka 4.

주요 함수

rte_ipv4_frag_reassemble_packet

실제 reassembly를 수행하는 함수.

  • 첫번째 인자는 ip fragment들이 저장된 ip_frag_tbl의 pointer
  • 두번째 인자는 death_row를 저장할 pointer.
  • 세번째 인자는 현재 수신한 mbuf의 pointer
  • 네번째 인자는 패킷이 수신한 timestamp. rdtsc_tsc()의 결과
  • 다섯번째 인자는 pointer to ip header(struct ipv4_hdr *)
rte_ipv6_frag_reassemble_packet()

IPv6용 reassembly 함수. 함수의 인자는 rte_ipv4_frag_reassemble_packet()와 유사하지만, 마지막에 struct ipv6_extension_fragment *가 추가된다.

rte_ipv4_frag_pkt_is_fragmented()

Return 1 if packet is fragmented.

rte_ipv6_frag_get_ipv6_fragment_header()

IPv6 경우 위 함수를 통해 frag_hdr 위치를 돌려받는다. struct ipv6_extension_fragment * 단순히 IPv6 base header 바로 뒷 헤더만 확인함.(표준 확인 필요)

rte_ip_frag_table_create()
struct rte_ip_frag_tbl * rte_ip_frag_table_create(
       uint32_t bucket_num, 
       uint32_t bucket_entries,  
       uint32_t max_entries, 
       uint64_t max_cycles,
       int socket_id);                                                       
  • bucket_num : # of bucket(Flow 개수). Should be power of 2
  • bucket_entries : bucket당 개수(hash associativity)
  • max_entries : # of flows to be stored in table. less or equal to max_flow_num * bucket_entries
  • max_cycles : Maximum TTL in cycles for each fragmented packet. frag_cycles = (rte_get_tsc_hz() + MS_PER_S - 1) / MS_PER_S * max_flow_ttl; 예. 1ms에 해당하는 TSC value
  • socket : 메모리를 할당할 socket ID. core와 함께 있는 socket 정보를 넣으면 됨.

defined in lib/librte_ip_frag/rte_ip_frag.h

/** fragmentation table */
struct rte_ip_frag_tbl {
    uint64_t             max_cycles;      /**< ttl for table entries. */
    uint32_t             entry_mask;      /**< hash value mask. */
    uint32_t             max_entries;     /**< max entries allowed. */
    uint32_t             use_entries;     /**< entries in use. */
    uint32_t             bucket_entries;  /**< hash assocaitivity. */
    uint32_t             nb_entries;      /**< total size of the table. */
    uint32_t             nb_buckets;      /**< num of associativity lines. */
    struct ip_frag_pkt *last;         /**< last used entry. */
    struct ip_pkt_list lru;           /**< LRU list for table entries. */
    struct ip_frag_tbl_stat stat;     /**< statistics counters. */
    struct ip_frag_pkt pkt[0];        /**< hash table. */
};
rte_ip_frag_free_death_row()

prefetch가 왜 필요한 지??(FIXME)

/*
 * Free mbufs on a given death row.
 *
 * @param dr
 *   Death row to free mbufs in.
 * @param prefetch
 *   How many buffers to prefetch before freeing.
 */
void rte_ip_frag_free_death_row(struct rte_ip_frag_death_row *dr,
        uint32_t prefetch);

예) if death_row has 10 entries and prefetch is 5

<< Step 1 >>
prefetch 0
prefetch 1
prefetch 2
prefetch 3
prefetch 4

<< Step 2 >>
prefetch 5
free 0
prefetch 6
free 1
prefetch 7
free 2
prefetch 8
free 3
prefetch 9
free 4

<< Step 3 >>
free 5
free 6
free 7
free 8
free 9

동작

rte_ipv4_frag_reassemble_packet() 혹은 rte_ipv6_frag_reassemble_packet()는 reassembly에 성공한 경우 해당 struct mbuf *를 리터한다. 그러므로 해당 mbuf를 다음 작업에 처리하면 됨.

struct rte_ip_frag_death_row *dr = &qconf->death_row;
if (rte_ipv4_frag_pkt_is_fragmented(ip_hdr)) {
    struct rte_mbuf *mo;

    tbl = rxq->frag_tbl;
    dr = &qconf->death_row;

    /* prepare mbuf: setup l2_len/l3_len. */
    m->l2_len = sizeof(*eth_hdr);
    m->l3_len = sizeof(*ip_hdr);

    /* process this fragment. */
    mo = rte_ipv4_frag_reassemble_packet(tbl, dr, m, tms, ip_hdr);
    if (mo == NULL)
        /* no packet to send out. */
        return;
    else
        /* mo를 이용해서 post processing */
} 
rte_ip_frag_free_death_row(dr, PREFETCH_OFFSET);  

IPv6 경우

frag_hdr = rte_ipv6_frag_get_ipv6_fragment_header(ip_hdr);

if (frag_hdr != NULL) {
    struct rte_mbuf *mo;

    tbl = rxq->frag_tbl;
    dr  = &qconf->death_row;

    /* prepare mbuf: setup l2_len/l3_len. */
    m->l2_len = sizeof(*eth_hdr);
    m->l3_len = sizeof(*ip_hdr) + sizeof(*frag_hdr);

    mo = rte_ipv6_frag_reassemble_packet(tbl, dr, m, tms, ip_hdr, frag_hdr);
    if (mo == NULL)
        return;

    if (mo != m) {
        m = mo;
        eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *);
        ip_hdr = (struct ipv6_hdr *)(eth_hdr + 1);
    }
}

rte_ip_frag_free_death_row(dr, PREFETCH_OFFSET);