DPDK IP reassembly example
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 2bucket_entries
: bucket당 개수(hash associativity)max_entries
: # of flows to be stored in table. less or equal tomax_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);