Analysis of actions processing flow of openvswitch datapath path

The previous chapter analyzes the flow table matching in datapatch. According to the matching results, there are two processes. If the matching is successful, the actions action will be executed. If there is no matching flow, it will be sent to the user status ovs through the upcall process. This chapter focuses on the ovs actions processing process when the flow table matching is successful.

void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key)
{
	const struct vport *p = OVS_CB(skb)->input_vport;
	struct datapath *dp = p->dp;
	struct sw_flow *flow;
	struct sw_flow_actions *sf_acts;
	struct dp_stats_percpu *stats;
	u64 *stats_counter;
	u32 n_mask_hit;

	stats = this_cpu_ptr(dp->stats_percpu);

	/* Look up flow. */
	flow = ovs_flow_tbl_lookup_stats(&dp->table, key, &n_mask_hit);
	if (unlikely(!flow)) {
		struct dp_upcall_info upcall;
		int error;

		memset(&upcall, 0, sizeof(upcall));
		upcall.cmd = OVS_PACKET_CMD_MISS;
		upcall.portid = ovs_vport_find_upcall_portid(p, skb);
		upcall.mru = OVS_CB(skb)->mru;
		error = ovs_dp_upcall(dp, skb, key, &upcall, 0);
		if (unlikely(error))
			kfree_skb(skb);
		else
			consume_skb(skb);
		stats_counter = &stats->n_missed;
		goto out;
	}

	ovs_flow_stats_update(flow, key->tp.flags, skb);
	sf_acts = rcu_dereference(flow->sf_acts);
	ovs_execute_actions(dp, skb, sf_acts, key);

	stats_counter = &stats->n_hit;

out:
	/* Update datapath statistics. */
	u64_stats_update_begin(&stats->syncp);
	(*stats_counter)++;
	stats->n_mask_hit += n_mask_hit;
	u64_stats_update_end(&stats->syncp);
}

The action types of ovs are as follows, using NLA_ The type() function gets NL_ The value of type, and the entry processing function is do_execute_actions()

 

  • OVS_ACTION_ATTR_OUTPUT: get the port number and call do_output() sends a message to the port;
  • OVS_ACTION_ATTR_USERSPACE: call output_userspace() is sent to user status;
  • OVS_ACTION_ATTR_HASH: call execute_hash() gets the hash of skb and assigns it to ovs_flow_hash
  • OVS_ACTION_ATTR_PUSH_VLAN: call push_vlan() add vlan header
int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb,
			const struct sw_flow_actions *acts,
			struct sw_flow_key *key)
{
	int err, level;

	level = __this_cpu_inc_return(exec_actions_level);
	if (unlikely(level > OVS_RECURSION_LIMIT)) {
		net_crit_ratelimited("ovs: recursion limit reached on datapath %s, probable configuration error\n",
				     ovs_dp_name(dp));
		kfree_skb(skb);
		err = -ENETDOWN;
		goto out;
	}

	OVS_CB(skb)->acts_origlen = acts->orig_len;
	err = do_execute_actions(dp, skb, key,
				 acts->actions, acts->actions_len);

	if (level == 1)
		process_deferred_actions(dp);

out:
	__this_cpu_dec(exec_actions_level);
	return err;
}

do_ execute_ The actions function processes actions according to each action. Before analyzing specific actions, first look at the nlattr type definition. All actions in ovs are defined through the nlattr type.

one   nlattr type definition

struct nlattr {
	__u16           nla_len;
	__u16           nla_type;
};

  Several common operation functions:

1.nla_len function

static inline int nla_len(const struct nlattr *nla)   // Length of returned data
{
     return nla->nla_ len - NLA_ HDRLEN;     // Total data length minus header length
}
2.nla_data function
static inline void *nla_data(const struct nlattr *nla)   // Returns the first address of the data
{
     return (char *) nla + NLA_HDRLEN;   // Current pointer plus header length
}
3.nla_next function
static inline struct nlattr *nla_next(const struct nlattr *nla, int *remaining)    // Returns the first address of the next data
{
    int totlen = NLA_ALIGN(nla->nla_len);
 
    * remaining -= totlen;// When remaining is 0, it indicates that the data has been fetched and the return value cannot be used
    return (struct nlattr *) ((char *) nla + totlen);
}
 

2. OVS_ACTION_ATTR_OUTPUT

Send the message from the specified vport port. If it is not the last action in the processing process, you need to copy a message and send it. The skb message continues to match the subsequent actions, which is well understood. For example, if the message needs to be sent from multiple ports, there will be multiple actions, Each action item has a specific vport port.

		case OVS_ACTION_ATTR_OUTPUT: {
			int port = nla_get_u32(a);
			struct sk_buff *clone;

			/* Every output action needs a separate clone
			 * of 'skb', In case the output action is the
			 * last action, cloning can be avoided.
			 */
			if (nla_is_last(a, rem)) {
				do_output(dp, skb, port, key);
				/* 'skb' has been used for output.
				 */
				return 0;
			}

			clone = skb_clone(skb, GFP_ATOMIC);
			if (clone)
				do_output(dp, clone, port, key);
			OVS_CB(skb)->cutlen = 0;
			break;
		}

When sending, directly call do_output function

do_output

|---ovs_vport_send    // skb - > dev = vport - > dev modify the dev of skb to the dev in the kernel corresponding to vport

     |--- Vport - > ops - > send (SKB) / / ops is the ops structure registered when vport is created

netdev type port:

static struct vport_ops ovs_netdev_vport_ops = {
    .type        = OVS_VPORT_TYPE_NETDEV,
    .create        = netdev_create,
    .destroy    = netdev_destroy,
    .send        = dev_queue_xmit,
};
 

internal type port

static struct vport_ops ovs_internal_vport_ops = {
    .type        = OVS_VPORT_TYPE_INTERNAL,
    .create        = internal_dev_create,
    .destroy    = internal_dev_destroy,
    .send        = internal_dev_recv,
};
internal_ dev_ The recv function will send the message directly to the kernel.

3. OVS_ACTION_ATTR_USERSPACE

OVS_ ACTION_ ATTR_ The processing function of userspace is output_ The userspace function is used to remove the message from the user state space.

output_userspace

|---ovs_dp_upcall

     |---queue_userspace_packet

          |--- genlmsg_unicast     Send user status via generic netlink message

Tags: Linux Network Protocol Cloud Native Network Communications ovs

Posted on Thu, 02 Sep 2021 15:23:42 -0400 by bouwob