/* called by execute_plums() to execute BPF program
 * or send it out of vport if destination plum_id is zero
 * It's called with rcu_read_lock.
 */
static void __bpf_forward(struct bpf_dp_context *ctx, u32 dest)
{
	struct datapath *dp = ctx->dp;
	u32 plum_id = dest >> 16;
	u32 port_id = dest & 0xffff;
	struct plum *plum;
	struct vport *vport;
	struct ovs_key_ipv4_tunnel tun_key;

	plum = rcu_dereference(dp->plums[plum_id]);
	if (unlikely(!plum)) {
		kfree_skb(ctx->skb);
		return;
	}
	if (plum_id == 0) {
		if (ctx->context.tun_key.dst_ip) {
			tun_key.tun_id =
				cpu_to_be64(ctx->context.tun_key.tun_id);
			tun_key.ipv4_src =
				cpu_to_be32(ctx->context.tun_key.src_ip);
			tun_key.ipv4_dst =
				cpu_to_be32(ctx->context.tun_key.dst_ip);
			tun_key.ipv4_tos = ctx->context.tun_key.tos;
			tun_key.ipv4_ttl = ctx->context.tun_key.ttl;
			tun_key.tun_flags = TUNNEL_KEY;
			OVS_CB(ctx->skb)->tun_key = &tun_key;
		} else {
			OVS_CB(ctx->skb)->tun_key = NULL;
		}

		plum_update_stats(plum, port_id, ctx->skb, false);

		vport = ovs_vport_rcu(dp, port_id);
		if (unlikely(!vport)) {
			kfree_skb(ctx->skb);
			return;
		}

/** begin_fixme **/
#if LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0)
		offload_send(vport, ctx->skb);
#else
		ovs_vport_send(vport, ctx->skb);
#endif
/** end_fixme **/
	} else {
		ctx->context.port_id = port_id;
		ctx->context.plum_id = plum_id;
		BUG_ON(plum->run == NULL);
		plum_update_stats(plum, port_id, ctx->skb, true);
		/* execute BPF program */
		plum->run(ctx);
		consume_skb(ctx->skb);
	}
}
static int do_output(struct datapath *dp, struct sk_buff *skb, int out_port)
{
	struct vport *vport;

	if (unlikely(!skb))
		return -ENOMEM;

	vport = rcu_dereference(dp->ports[out_port]);
	if (unlikely(!vport)) {
		kfree_skb(skb);
		return -ENODEV;
	}

	ovs_vport_send(vport, skb);
	return 0;
}