示例#1
0
static void
test_packet_output_gre_no_nexthop(void)
{
	odp_packet_t pkt = ODP_PACKET_INVALID;
	odp_event_t ev;
	int res;

	if (create_odp_packet_ip4(&pkt, test_frame, sizeof(test_frame),
				  tun_p2p + 1)) {
		CU_FAIL("Fail to create packet");
		return;
	}

	/*
	 * Packet's destination is GRE tunnel's p2p address, no next hop
	 * is found for tunnel destination address, packet is dropped.
	 */
	res = ofp_ip_output(pkt, NULL);
	CU_ASSERT_EQUAL(res, OFP_PKT_DROP);

	res = ofp_send_pending_pkt();
	CU_ASSERT_EQUAL(res, OFP_PKT_PROCESSED);

	ev = odp_queue_deq(dev->outq_def);
	CU_ASSERT_EQUAL_FATAL(ev, ODP_EVENT_INVALID);
}
示例#2
0
/*
 * Tests
 */
static void
test_packet_output_gre(void)
{
	odp_packet_t pkt = ODP_PACKET_INVALID;
	odp_event_t ev;
	int res;
	struct ofp_ether_header *eth;
	struct ofp_ip *ip;
	struct ofp_ip *ip_orig;
	struct ofp_greip *greip;

	if (create_odp_packet_ip4(&pkt, test_frame, sizeof(test_frame),
				  tun_p2p)) {
		CU_FAIL("Fail to create packet");
		return;
	}

	/*
	 * Packet's destination is GRE tunnel's p2p address, next hop is GRE
	 * interface. GRE+IP header is prepended. Packet's new destination is
	 * link local. Packet is put into output queue.
	 */
	res = ofp_ip_output(pkt, NULL);
	CU_ASSERT_EQUAL(res, OFP_PKT_PROCESSED);

	res = ofp_send_pending_pkt();
	CU_ASSERT_EQUAL(res, OFP_PKT_PROCESSED);

	ev = odp_queue_deq(dev->outq_def);
	CU_ASSERT_NOT_EQUAL_FATAL(ev, ODP_EVENT_INVALID);

	pkt = odp_packet_from_event(ev);
	CU_ASSERT_EQUAL_FATAL(odp_packet_len(pkt),
			      sizeof(test_frame) + 20 + 4);

	eth = odp_packet_l2_ptr(pkt, NULL);
	if (memcmp(eth->ether_dhost, tun_rem_mac, OFP_ETHER_ADDR_LEN))
		CU_FAIL("Bad destination mac address.");
	if (memcmp(eth->ether_shost, dev->mac, OFP_ETHER_ADDR_LEN))
		CU_FAIL("Bad source mac address.");

	ip = odp_packet_l3_ptr(pkt, NULL);
	CU_ASSERT_EQUAL(ip->ip_src.s_addr, dev_ip);
	CU_ASSERT_EQUAL(ip->ip_dst.s_addr, tun_rem_ip);
	CU_ASSERT_EQUAL(ip->ip_p, OFP_IPPROTO_GRE);

	greip = (struct ofp_greip *)ip;
	CU_ASSERT_EQUAL(greip->gi_g.flags, 0);
	CU_ASSERT_EQUAL(greip->gi_g.ptype,
			odp_cpu_to_be_16(OFP_ETHERTYPE_IP));

	/* inner ip */
	ip = (struct ofp_ip *)(greip + 1);
	ip_orig = (struct ofp_ip *)(&orig_pkt_data[OFP_ETHER_HDR_LEN]);
	if (memcmp(ip, ip_orig, odp_be_to_cpu_16(ip_orig->ip_len)))
		CU_FAIL("Inner IP packet error.");
}
示例#3
0
文件: app_main.c 项目: chyyuu/ofp
static void *pkt_io_recv(void *arg)
{
	odp_pktin_queue_t pktin;
	odp_packet_t pkt, pkt_tbl[PKT_BURST_SIZE];
	int pkt_idx, pkt_cnt;
	struct pktio_thr_arg *thr_args;
	ofp_pkt_processing_func pkt_func;

	thr_args = arg;
	pkt_func = thr_args->pkt_func;
	pktin = thr_args->pktin;

	if (ofp_init_local()) {
		OFP_ERR("Error: OFP local init failed.\n");
		return NULL;
	}

	OFP_DBG("PKT-IO receive starting on cpu: %d", odp_cpu_id());

	while (1) {
		pkt_cnt = odp_pktin_recv(pktin, pkt_tbl, PKT_BURST_SIZE);

		for (pkt_idx = 0; pkt_idx < pkt_cnt; pkt_idx++) {
			pkt = pkt_tbl[pkt_idx];

			if (odp_unlikely(odp_packet_has_error(pkt))) {
				OFP_DBG("Packet with error dropped.\n");
				odp_packet_free(pkt);
				continue;
			}

			ofp_packet_input(pkt, ODP_QUEUE_INVALID, pkt_func);
		}
		ofp_send_pending_pkt();
	}

	/* Never reached */
	return NULL;
}
示例#4
0
static void test_packet_size_is_less_then_mtu(void)
{
    odp_packet_t pkt_orig, pkt_sent;
    odp_event_t ev;
    int res;
    struct ofp_ether_header *eth;

    if (create_odp_packet_ip4(&pkt_orig, pkt1_frag1,
                              sizeof(pkt1_frag1), 0)) {
        CU_FAIL("Fail to create packet");
        return;
    }

    res = ofp_ip_output(pkt_orig, &nexthop);
    CU_ASSERT_EQUAL(res, OFP_PKT_PROCESSED);
    res = ofp_send_pending_pkt();
    CU_ASSERT_EQUAL(res, OFP_PKT_PROCESSED);

    ev = odp_queue_deq(dev->outq_def);
    CU_ASSERT_NOT_EQUAL_FATAL(ev, ODP_EVENT_INVALID);
    CU_ASSERT_EQUAL_FATAL(odp_queue_deq(dev->outq_def), ODP_EVENT_INVALID);

    pkt_sent = odp_packet_from_event(ev);
    CU_ASSERT_EQUAL_FATAL(odp_packet_len(pkt_sent), sizeof(pkt1_frag1));

    eth = odp_packet_l2_ptr(pkt_sent, NULL);
    if (memcmp(eth->ether_dhost, dst_mac, OFP_ETHER_ADDR_LEN))
        CU_FAIL("Bad destination mac address.");
    if (memcmp(eth->ether_shost, dev->mac, OFP_ETHER_ADDR_LEN))
        CU_FAIL("Bad source mac address.");
    if (memcmp(odp_packet_l3_ptr(pkt_sent, NULL),
               &orig_pkt_data[OFP_ETHER_HDR_LEN],
               sizeof(pkt1_frag1) - OFP_ETHER_HDR_LEN))
        CU_FAIL("corrupt l3 + data forwarded");
    CU_PASS("Correct packet");

    odp_packet_free(pkt_sent);
}
示例#5
0
enum ofp_return_code ofp_send_frame(struct ofp_ifnet *dev, odp_packet_t pkt)
{
	struct ofp_ether_header *eth, eth_tmp;
	struct ofp_ether_vlan_header *eth_vlan, eth_vlan_tmp;
	uint32_t pkt_len, eth_hdr_len;
	enum ofp_return_code rc;

	if (ofp_if_type(dev) == OFP_IFT_GRE) {
		OFP_ERR("Send frame on GRE port");
		return OFP_PKT_DROP;
	}

	ofp_packet_user_area_reset(pkt);

	/* Contsruct ethernet header */
	eth = odp_packet_l2_ptr(pkt, NULL);
	eth_vlan = odp_packet_l2_ptr(pkt, NULL);

	if (odp_be_to_cpu_16(eth->ether_type) == OFP_ETHERTYPE_VLAN) {
		if (dev->vlan) {
			/* change vlan */
			eth_vlan->evl_tag = odp_cpu_to_be_16(dev->vlan);
		} else {
			/* remove existing vlan */
			eth_vlan_tmp = *eth_vlan;
			eth = odp_packet_pull_head(pkt, 4);
			if (!eth) {
				OFP_ERR("odp_packet_pull_head failed");
				return OFP_PKT_DROP;
			}

			odp_packet_l3_offset_set(pkt,
						 odp_packet_l3_offset(pkt) - 4);
			ofp_copy_mac(eth->ether_dhost, eth_vlan_tmp.evl_dhost);
			ofp_copy_mac(eth->ether_shost, eth_vlan_tmp.evl_shost);
			eth->ether_type = eth_vlan_tmp.evl_proto;
		}
	} else {
		if (dev->vlan) {
			/* insert vlan */
			eth_tmp = *eth;
			eth_vlan = odp_packet_push_head(pkt, 4);
			if (!eth_vlan) {
				OFP_ERR("odp_packet_push_head failed");
				return OFP_PKT_DROP;
			}

			odp_packet_l3_offset_set(pkt,
						 odp_packet_l3_offset(pkt) + 4);
			ofp_copy_mac(eth_vlan->evl_dhost, eth_tmp.ether_dhost);
			ofp_copy_mac(eth_vlan->evl_shost, eth_tmp.ether_shost);
			eth_vlan->evl_encap_proto =
				odp_cpu_to_be_16(OFP_ETHERTYPE_VLAN);
			eth_vlan->evl_tag = odp_cpu_to_be_16(dev->vlan);
			eth_vlan->evl_proto = eth_tmp.ether_type;
		}
	}

	if (dev->vlan)
		eth_hdr_len = OFP_ETHER_HDR_LEN + OFP_ETHER_VLAN_ENCAP_LEN;
	else
		eth_hdr_len = OFP_ETHER_HDR_LEN;

	pkt_len = odp_packet_len(pkt) - eth_hdr_len;

	if (pkt_len > dev->if_mtu) {
		OFP_ERR("Packet size bigger than MTU: %d %d", pkt_len,
			dev->if_mtu);
		return OFP_PKT_DROP;
	}

	rc = send_pkt_out(dev, pkt);
	if (rc != OFP_PKT_PROCESSED)
		return rc;

	return ofp_send_pending_pkt();
}
示例#6
0
int default_event_dispatcher(void *arg)
{
	odp_event_t ev;
	odp_packet_t pkt;
	odp_queue_t in_queue;
	int event_idx = 0;
	int event_cnt = 0;
	ofp_pkt_processing_func pkt_func = (ofp_pkt_processing_func)arg;
	odp_bool_t *is_running = NULL;

	if (ofp_init_local()) {
		OFP_ERR("ofp_init_local failed");
		return -1;
	}

	int rx_burst = global_param->evt_rx_burst_size;
	odp_event_t events[rx_burst];

	is_running = ofp_get_processing_state();
	if (is_running == NULL) {
		OFP_ERR("ofp_get_processing_state failed");
		ofp_term_local();
		return -1;
	}

	/* PER CORE DISPATCHER */
	while (*is_running) {
		event_cnt = odp_schedule_multi(&in_queue, ODP_SCHED_WAIT,
					 events, rx_burst);
		for (event_idx = 0; event_idx < event_cnt; event_idx++) {
			odp_event_type_t ev_type;

			ev = events[event_idx];

			if (ev == ODP_EVENT_INVALID)
				continue;
			ev_type = odp_event_type(ev);

			if (odp_likely(ev_type == ODP_EVENT_PACKET)) {
				pkt = odp_packet_from_event(ev);
#if 0
				if (odp_unlikely(odp_packet_has_error(pkt))) {
					OFP_DBG("Dropping packet with error");
					odp_packet_free(pkt);
					continue;
				}
#endif
				ofp_packet_input(pkt, in_queue, pkt_func);
				continue;
			}
			if (ev_type == ODP_EVENT_TIMEOUT) {
				ofp_timer_handle(ev);
				continue;
			}

			OFP_ERR("Unexpected event type: %u", ev_type);
			odp_event_free(ev);
		}
		ofp_send_pending_pkt();
	}

	if (ofp_term_local())
		OFP_ERR("ofp_term_local failed");

	return 0;
}
示例#7
0
static void
test_packet_output_ipv6_to_gre(void)
{
	odp_packet_t pkt = ODP_PACKET_INVALID;
	odp_event_t ev;
	int res;
	struct ofp_ether_header *eth;
	struct ofp_ip6_hdr *ip6, *ip6_orig;
	struct ofp_ip *ip;
	struct ofp_greip *greip;

	(void)tcp_frame;
	(void)icmp_frame;
	(void)arp_frame;
	(void)icmp6_frame;

	if (create_odp_packet_ip6(&pkt, ip6udp_frame, sizeof(ip6udp_frame))) {
		CU_FAIL("Fail to create packet");
		return;
	}

	ip6 = odp_packet_l3_ptr(pkt, NULL);

	ofp_set_route6_params(OFP_ROUTE6_ADD, 0 /*vrf*/, 100 /*vlan*/, GRE_PORTS,
			      ip6->ip6_dst.__u6_addr.__u6_addr8, 64 /*masklen*/,
			      0 /*gw*/, OFP_RTF_NET /* flags */);

	res = ofp_ip6_output(pkt, NULL);
	CU_ASSERT_EQUAL(res, OFP_PKT_PROCESSED);

	res = ofp_send_pending_pkt();
	CU_ASSERT_EQUAL(res, OFP_PKT_PROCESSED);

	ev = odp_queue_deq(dev->outq_def);
	CU_ASSERT_NOT_EQUAL_FATAL(ev, ODP_EVENT_INVALID);

	pkt = odp_packet_from_event(ev);
	CU_ASSERT_EQUAL_FATAL(odp_packet_len(pkt),
			      sizeof(ip6udp_frame) + 20 + 4);

	eth = odp_packet_l2_ptr(pkt, NULL);
	if (memcmp(eth->ether_dhost, tun_rem_mac, OFP_ETHER_ADDR_LEN))
		CU_FAIL("Bad destination mac address.");
	if (memcmp(eth->ether_shost, dev->mac, OFP_ETHER_ADDR_LEN))
		CU_FAIL("Bad source mac address.");

	ip = odp_packet_l3_ptr(pkt, NULL);
	CU_ASSERT_EQUAL(ip->ip_src.s_addr, dev_ip);
	CU_ASSERT_EQUAL(ip->ip_dst.s_addr, tun_rem_ip);
	CU_ASSERT_EQUAL(ip->ip_p, OFP_IPPROTO_GRE);

	greip = (struct ofp_greip *)ip;
	CU_ASSERT_EQUAL(greip->gi_g.flags, 0);
	CU_ASSERT_EQUAL(greip->gi_g.ptype,
			odp_cpu_to_be_16(OFP_ETHERTYPE_IPV6));

	/* inner ip */
	ip6 = (struct ofp_ip6_hdr *)(greip + 1);
	ip6_orig = (struct ofp_ip6_hdr *)
		(&orig_pkt_data[OFP_ETHER_HDR_LEN]);
	if (memcmp(ip6, ip6_orig,
		   odp_be_to_cpu_16(ip6_orig->ofp_ip6_plen) + sizeof(*ip6)))
		CU_FAIL("Inner IP packet error.");
}
示例#8
0
static void test_fragment_fragmented_to_two(void)
{
    odp_packet_t pkt_orig, pkt_sent;
    odp_event_t ev;
    int res;
    struct ofp_ether_header *eth;
    struct ofp_ip *ip;
    struct ofp_ip *ip_orig;
    uint16_t pl_pos, pl_len, orig_pl_len, pktlen, start_offset;

    dev->if_mtu = 620;

    if (create_odp_packet_ip4(&pkt_orig, pkt1_frag2,
                              sizeof(pkt1_frag2), 0)) {
        CU_FAIL("Fail to create packet");
        return;
    }

    res = ofp_ip_output(pkt_orig, &nexthop);
    CU_ASSERT_EQUAL(res, OFP_PKT_PROCESSED);

    res = ofp_send_pending_pkt();
    CU_ASSERT_EQUAL(res, OFP_PKT_PROCESSED);

    /* ASSERT 1st fragment */
    ev = odp_queue_deq(dev->outq_def);
    CU_ASSERT_NOT_EQUAL_FATAL(ev, ODP_EVENT_INVALID);

    pkt_sent = odp_packet_from_event(ev);
    CU_ASSERT_EQUAL_FATAL(odp_packet_len(pkt_sent),
                          dev->if_mtu + OFP_ETHER_HDR_LEN);

    eth = odp_packet_l2_ptr(pkt_sent, NULL);
    if (memcmp(eth->ether_dhost, dst_mac, OFP_ETHER_ADDR_LEN))
        CU_FAIL("Bad destination mac address.");
    if (memcmp(eth->ether_shost, dev->mac, OFP_ETHER_ADDR_LEN))
        CU_FAIL("Bad source mac address.");

    ip = odp_packet_l3_ptr(pkt_sent, NULL);
    ip_orig = (struct ofp_ip *)(&orig_pkt_data[OFP_ETHER_HDR_LEN]);
    orig_pl_len = odp_be_to_cpu_16(ip_orig->ip_len) - (ip_orig->ip_hl<<2);
    start_offset = odp_be_to_cpu_16(ip_orig->ip_off) & OFP_IP_OFFMASK;

    assert_ip_header(ip, ip_orig, dev->if_mtu, 1, start_offset);

    pl_len = dev->if_mtu - (ip->ip_hl<<2);
    if (memcmp((uint8_t *)ip + (ip->ip_hl<<2),
               (uint8_t *)ip_orig + (ip_orig->ip_hl<<2),
               pl_len))
        CU_FAIL("corrupt l3 + data forwarded");
    pl_pos = pl_len;
    CU_PASS("Correct packet");

    odp_packet_free(pkt_sent);

    /* ASSERT 2nd fragment */
    ev = odp_queue_deq(dev->outq_def);
    CU_ASSERT_NOT_EQUAL_FATAL(ev, ODP_EVENT_INVALID);

    pkt_sent = odp_packet_from_event(ev);
    pl_len = orig_pl_len - pl_pos;
    pktlen = pl_len + OFP_ETHER_HDR_LEN + sizeof(struct ofp_ip);
    CU_ASSERT_EQUAL_FATAL(odp_packet_len(pkt_sent), pktlen);

    eth = odp_packet_l2_ptr(pkt_sent, NULL);
    if (memcmp(eth->ether_dhost, dst_mac, OFP_ETHER_ADDR_LEN))
        CU_FAIL("Bad destination mac address.");
    if (memcmp(eth->ether_shost, dev->mac, OFP_ETHER_ADDR_LEN))
        CU_FAIL("Bad source mac address.");

    ip = odp_packet_l3_ptr(pkt_sent, NULL);

    assert_ip_header(ip, ip_orig, pl_len + sizeof(struct ofp_ip),
                     0, start_offset + pl_pos/8);

    if (memcmp((uint8_t *)ip + (ip->ip_hl<<2),
               (uint8_t *)ip_orig + (ip_orig->ip_hl<<2) + pl_pos,
               pl_len))
        CU_FAIL("corrupt l3 + data forwarded");
    CU_PASS("Correct packet");

    odp_packet_free(pkt_sent);

    /* no more fragments */
    ev = odp_queue_deq(dev->outq_def);
    CU_ASSERT_EQUAL(ev, ODP_EVENT_INVALID);

    dev->if_mtu = def_mtu;
}
static ngx_int_t
ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,
    ngx_uint_t flags)
{
	odp_packet_t pkts[OFP_PKT_RX_BURST_SIZE];
        odp_packet_t odp_pkt;
        int pkt_cnt = 0;
        int pkt_idx = 0;

	pkt_cnt = odp_pktin_recv(in_queue, pkts, OFP_PKT_RX_BURST_SIZE);

        for (pkt_idx = 0; pkt_idx < pkt_cnt; pkt_idx++) {
                odp_pkt = pkts[pkt_idx];
                ofp_packet_input(odp_pkt, ODP_QUEUE_INVALID, ofp_eth_vlan_processing);
        }

	if (pkt_cnt < 1) ofp_send_pending_pkt();

	static uint32_t count = 0;
	/*odp_queue_deq has a lock that impacts performance*/
	if (count ++ > 50) {
		count = 0;

		odp_queue_t timer_queue = ofp_timer_queue_cpu(-1);
		odp_event_t event = odp_queue_deq(timer_queue);

		if (event != ODP_EVENT_INVALID) {
			if (odp_event_type(event) == ODP_EVENT_TIMEOUT) {
				ofp_timer_handle(event);
			} else {
				odp_buffer_free(odp_buffer_from_event(event));
			}
		}
	}

#if OFP_NOTIFY
    return NGX_OK;
#endif
    int                ready, nready;
    ngx_err_t          err;
    ngx_uint_t         i, found;
    ngx_event_t       *ev;
    ngx_queue_t       *queue;
    struct timeval     tv, *tp;
    ngx_connection_t  *c;

    if (max_fd == -1) {
        for (i = 0; i < nevents; i++) {
            c = event_index[i]->data;
        ngx_log_debug(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                       "change max_fd: %i, c->fd : %d", max_fd, c->fd);
            if (max_fd < c->fd) {
                max_fd = c->fd;
            }
        }

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                       "change max_fd: %i", max_fd);
    }

#if (NGX_DEBUG)
    if (cycle->log->log_level & NGX_LOG_DEBUG_ALL) {
        for (i = 0; i < nevents; i++) {
            ev = event_index[i];
            c = ev->data;
            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                           "select event: fd:%d wr:%d", c->fd, ev->write);
        }

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                       "max_fd: %i", max_fd);
    }
#endif

    if (timer == NGX_TIMER_INFINITE) {
        tp = NULL;

    } else {
        tv.tv_sec = (long) (timer / 1000);
        tv.tv_usec = (long) ((timer % 1000) * 1000);
        tp = &tv;
    }

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                   "select timer: %M", timer);

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                   "select max_fd %d, tp %p ", max_fd, tp);
    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                   " master %p : work %p", (ofp_fd_set *)&master_read_fd_set,
		   (ofp_fd_set *)&work_read_fd_set);
    ready = select(max_fd + 1, &master_read_fd_set, &master_write_fd_set, NULL, tp);

    err = (ready == -1) ? ngx_errno : 0;

    if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {
        ngx_time_update();
    }

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                   "select ready %d", ready);

    if (err) {
        ngx_uint_t  level;

        if (err == NGX_EINTR) {

            if (ngx_event_timer_alarm) {
                ngx_event_timer_alarm = 0;
                return NGX_OK;
            }

            level = NGX_LOG_INFO;

        } else {
            level = NGX_LOG_ALERT;
        }

        ngx_log_error(level, cycle->log, err, "select() failed");

        if (err == NGX_EBADF) {
            /*ngx_select_repair_fd_sets(cycle);*/
        }

        return NGX_ERROR;
    }

    if (ready == 0) {
return NGX_OK;
        if (timer != NGX_TIMER_INFINITE) {
            return NGX_OK;
        }

        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
                      "select() returned no events without timeout");
        return NGX_ERROR;
    }

    nready = 0;

    for (i = 0; i < nevents; i++) {
        ev = event_index[i];
        c = ev->data;
        found = 0;

        if (ev->write) {
            if (FD_ISSET(c->fd, &master_write_fd_set)) {
                found = 1;
                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                               "select write %d", c->fd);
            }

        } else {
            if (FD_ISSET(c->fd, &master_read_fd_set)) {
                found = 1;
                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                               "select read %d", c->fd);
            }
        }

        if (found) {
            ev->ready = 1;

            queue = ev->accept ? &ngx_posted_accept_events
                               : &ngx_posted_events;

            ngx_post_event(ev, queue);

            nready++;
        }
    }

    if (ready != nready) {
        /*ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
                      "select ready != events: %d:%d", ready, nready);

        ngx_select_repair_fd_sets(cycle);*/
    }

    return NGX_OK;
}
示例#10
0
void *default_event_dispatcher(void *arg)
{
	odp_event_t ev;
	odp_packet_t pkt;
	odp_queue_t in_queue;
	odp_event_t events[OFP_EVT_RX_BURST_SIZE];
	int event_idx = 0;
	int event_cnt = 0;
	ofp_pkt_processing_func pkt_func = (ofp_pkt_processing_func)arg;
	odp_bool_t *is_running = NULL;

#if ODP_VERSION < 106
	if (odp_init_local(ODP_THREAD_WORKER)) {
		OFP_ERR("odp_init_local failed");
		return NULL;
	}
#endif

	if (ofp_init_local()) {
		OFP_ERR("ofp_init_local failed");
		return NULL;
	}

	is_running = ofp_get_processing_state();
	if (is_running == NULL) {
		OFP_ERR("ofp_get_processing_state failed");
		ofp_term_local();
		return NULL;
	}

	/* PER CORE DISPATCHER */
	while (*is_running) {
		event_cnt = odp_schedule_multi(&in_queue, ODP_SCHED_WAIT,
					 events, OFP_EVT_RX_BURST_SIZE);
		for (event_idx = 0; event_idx < event_cnt; event_idx++) {
			ev = events[event_idx];

			if (ev == ODP_EVENT_INVALID)
				continue;

			if (odp_event_type(ev) == ODP_EVENT_TIMEOUT) {
				ofp_timer_handle(ev);
				continue;
			}

			if (odp_event_type(ev) == ODP_EVENT_PACKET) {
				pkt = odp_packet_from_event(ev);
#if 0
				if (odp_unlikely(odp_packet_has_error(pkt))) {
					OFP_DBG("Dropping packet with error");
					odp_packet_free(pkt);
					continue;
				}
#endif
				ofp_packet_input(pkt, in_queue, pkt_func);
				continue;
			}

			OFP_ERR("Unexpected event type: %u", odp_event_type(ev));

			/* Free events by type */
			if (odp_event_type(ev) == ODP_EVENT_BUFFER) {
				odp_buffer_free(odp_buffer_from_event(ev));
				continue;
			}

			if (odp_event_type(ev) == ODP_EVENT_CRYPTO_COMPL) {
				odp_crypto_compl_free(
					odp_crypto_compl_from_event(ev));
				continue;
			}

		}
		ofp_send_pending_pkt();
	}

	if (ofp_term_local())
		OFP_ERR("ofp_term_local failed");

	return NULL;
}