Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8f0aad6f authored by Wenyu Zhang's avatar Wenyu Zhang Committed by Pravin B Shelar
Browse files

openvswitch: Extend packet attribute for egress tunnel info



OVS vswitch has extended IPFIX exporter to export tunnel headers
to improve network visibility.
To export this information userspace needs to know egress tunnel
for given packet. By extending packet attributes datapath can
export egress tunnel info for given packet. So that userspace
can ask for egress tunnel info in userspace action. This
information is used to build IPFIX data for given flow.

Signed-off-by: default avatarWenyu Zhang <wenyuz@vmware.com>
Acked-by: default avatarRomain Lenglet <rlenglet@vmware.com>
Acked-by: default avatarBen Pfaff <blp@nicira.com>
Signed-off-by: default avatarPravin B Shelar <pshelar@nicira.com>
parent 9ba559d9
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -157,6 +157,11 @@ enum ovs_packet_cmd {
 * notification if the %OVS_ACTION_ATTR_USERSPACE action specified an
 * %OVS_USERSPACE_ATTR_USERDATA attribute, with the same length and content
 * specified there.
 * @OVS_PACKET_ATTR_EGRESS_TUN_KEY: Present for an %OVS_PACKET_CMD_ACTION
 * notification if the %OVS_ACTION_ATTR_USERSPACE action specified an
 * %OVS_USERSPACE_ATTR_EGRESS_TUN_PORT attribute, which is sent only if the
 * output port is actually a tunnel port. Contains the output tunnel key
 * extracted from the packet as nested %OVS_TUNNEL_KEY_ATTR_* attributes.
 *
 * These attributes follow the &struct ovs_header within the Generic Netlink
 * payload for %OVS_PACKET_* commands.
@@ -167,6 +172,8 @@ enum ovs_packet_attr {
	OVS_PACKET_ATTR_KEY,         /* Nested OVS_KEY_ATTR_* attributes. */
	OVS_PACKET_ATTR_ACTIONS,     /* Nested OVS_ACTION_ATTR_* attributes. */
	OVS_PACKET_ATTR_USERDATA,    /* OVS_ACTION_ATTR_USERSPACE arg. */
	OVS_PACKET_ATTR_EGRESS_TUN_KEY,  /* Nested OVS_TUNNEL_KEY_ATTR_*
					    attributes. */
	__OVS_PACKET_ATTR_MAX
};

@@ -315,6 +322,8 @@ enum ovs_tunnel_key_attr {
	OVS_TUNNEL_KEY_ATTR_CSUM,               /* No argument. CSUM packet. */
	OVS_TUNNEL_KEY_ATTR_OAM,                /* No argument. OAM frame.  */
	OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS,        /* Array of Geneve options. */
	OVS_TUNNEL_KEY_ATTR_TP_SRC,		/* be16 src Transport Port. */
	OVS_TUNNEL_KEY_ATTR_TP_DST,		/* be16 dst Transport Port. */
	__OVS_TUNNEL_KEY_ATTR_MAX
};

@@ -480,11 +489,15 @@ enum ovs_sample_attr {
 * message should be sent.  Required.
 * @OVS_USERSPACE_ATTR_USERDATA: If present, its variable-length argument is
 * copied to the %OVS_PACKET_CMD_ACTION message as %OVS_PACKET_ATTR_USERDATA.
 * @OVS_USERSPACE_ATTR_EGRESS_TUN_PORT: If present, u32 output port to get
 * tunnel info.
 */
enum ovs_userspace_attr {
	OVS_USERSPACE_ATTR_UNSPEC,
	OVS_USERSPACE_ATTR_PID,	      /* u32 Netlink PID to receive upcalls. */
	OVS_USERSPACE_ATTR_USERDATA,  /* Optional user-specified cookie. */
	OVS_USERSPACE_ATTR_EGRESS_TUN_PORT,  /* Optional, u32 output port
					      * to get tunnel info. */
	__OVS_USERSPACE_ATTR_MAX
};

+19 −0
Original line number Diff line number Diff line
@@ -564,6 +564,7 @@ static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port)
static int output_userspace(struct datapath *dp, struct sk_buff *skb,
			    struct sw_flow_key *key, const struct nlattr *attr)
{
	struct ovs_tunnel_info info;
	struct dp_upcall_info upcall;
	const struct nlattr *a;
	int rem;
@@ -572,6 +573,7 @@ static int output_userspace(struct datapath *dp, struct sk_buff *skb,
	upcall.key = key;
	upcall.userdata = NULL;
	upcall.portid = 0;
	upcall.egress_tun_info = NULL;

	for (a = nla_data(attr), rem = nla_len(attr); rem > 0;
		 a = nla_next(a, &rem)) {
@@ -583,7 +585,24 @@ static int output_userspace(struct datapath *dp, struct sk_buff *skb,
		case OVS_USERSPACE_ATTR_PID:
			upcall.portid = nla_get_u32(a);
			break;

		case OVS_USERSPACE_ATTR_EGRESS_TUN_PORT: {
			/* Get out tunnel info. */
			struct vport *vport;

			vport = ovs_vport_rcu(dp, nla_get_u32(a));
			if (vport) {
				int err;

				err = ovs_vport_get_egress_tun_info(vport, skb,
								    &info);
				if (!err)
					upcall.egress_tun_info = &info;
			}
			break;
		}

		} /* End of switch. */
	}

	return ovs_dp_upcall(dp, skb, &upcall);
+17 −4
Original line number Diff line number Diff line
@@ -274,6 +274,7 @@ void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key)
		upcall.key = key;
		upcall.userdata = NULL;
		upcall.portid = ovs_vport_find_upcall_portid(p, skb);
		upcall.egress_tun_info = NULL;
		error = ovs_dp_upcall(dp, skb, &upcall);
		if (unlikely(error))
			kfree_skb(skb);
@@ -375,7 +376,7 @@ static int queue_gso_packets(struct datapath *dp, struct sk_buff *skb,
	return err;
}

static size_t upcall_msg_size(const struct nlattr *userdata,
static size_t upcall_msg_size(const struct dp_upcall_info *upcall_info,
			      unsigned int hdrlen)
{
	size_t size = NLMSG_ALIGN(sizeof(struct ovs_header))
@@ -383,8 +384,12 @@ static size_t upcall_msg_size(const struct nlattr *userdata,
		+ nla_total_size(ovs_key_attr_size()); /* OVS_PACKET_ATTR_KEY */

	/* OVS_PACKET_ATTR_USERDATA */
	if (userdata)
		size += NLA_ALIGN(userdata->nla_len);
	if (upcall_info->userdata)
		size += NLA_ALIGN(upcall_info->userdata->nla_len);

	/* OVS_PACKET_ATTR_EGRESS_TUN_KEY */
	if (upcall_info->egress_tun_info)
		size += nla_total_size(ovs_tun_key_attr_size());

	return size;
}
@@ -440,7 +445,7 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
	else
		hlen = skb->len;

	len = upcall_msg_size(upcall_info->userdata, hlen);
	len = upcall_msg_size(upcall_info, hlen);
	user_skb = genlmsg_new_unicast(len, &info, GFP_ATOMIC);
	if (!user_skb) {
		err = -ENOMEM;
@@ -461,6 +466,14 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
			  nla_len(upcall_info->userdata),
			  nla_data(upcall_info->userdata));

	if (upcall_info->egress_tun_info) {
		nla = nla_nest_start(user_skb, OVS_PACKET_ATTR_EGRESS_TUN_KEY);
		err = ovs_nla_put_egress_tunnel_key(user_skb,
						    upcall_info->egress_tun_info);
		BUG_ON(err);
		nla_nest_end(user_skb, nla);
	}

	/* Only reserve room for attribute header, packet data is added
	 * in skb_zerocopy() */
	if (!(nla = nla_reserve(user_skb, OVS_PACKET_ATTR_PACKET, 0))) {
+2 −0
Original line number Diff line number Diff line
@@ -114,12 +114,14 @@ struct ovs_skb_cb {
 * @pid: Netlink PID to which packet should be sent.  If @pid is 0 then no
 * packet is sent and the packet is accounted in the datapath's @n_lost
 * counter.
 * @egress_tun_info: If nonnull, becomes %OVS_PACKET_ATTR_EGRESS_TUN_KEY.
 */
struct dp_upcall_info {
	u8 cmd;
	const struct sw_flow_key *key;
	const struct nlattr *userdata;
	u32 portid;
	const struct ovs_tunnel_info *egress_tun_info;
};

/**
+48 −14
Original line number Diff line number Diff line
@@ -37,8 +37,8 @@ struct sk_buff;

/* Used to memset ovs_key_ipv4_tunnel padding. */
#define OVS_TUNNEL_KEY_SIZE					\
	(offsetof(struct ovs_key_ipv4_tunnel, ipv4_ttl) +	\
	FIELD_SIZEOF(struct ovs_key_ipv4_tunnel, ipv4_ttl))
	(offsetof(struct ovs_key_ipv4_tunnel, tp_dst) +		\
	 FIELD_SIZEOF(struct ovs_key_ipv4_tunnel, tp_dst))

struct ovs_key_ipv4_tunnel {
	__be64 tun_id;
@@ -47,6 +47,8 @@ struct ovs_key_ipv4_tunnel {
	__be16 tun_flags;
	u8   ipv4_tos;
	u8   ipv4_ttl;
	__be16 tp_src;
	__be16 tp_dst;
} __packed __aligned(4); /* Minimize padding. */

struct ovs_tunnel_info {
@@ -64,27 +66,59 @@ struct ovs_tunnel_info {
			       FIELD_SIZEOF(struct sw_flow_key, tun_opts) - \
			       opt_len))

static inline void ovs_flow_tun_info_init(struct ovs_tunnel_info *tun_info,
					  const struct iphdr *iph,
					  __be64 tun_id, __be16 tun_flags,
static inline void __ovs_flow_tun_info_init(struct ovs_tunnel_info *tun_info,
					    __be32 saddr, __be32 daddr,
					    u8 tos, u8 ttl,
					    __be16 tp_src,
					    __be16 tp_dst,
					    __be64 tun_id,
					    __be16 tun_flags,
					    struct geneve_opt *opts,
					    u8 opts_len)
{
	tun_info->tunnel.tun_id = tun_id;
	tun_info->tunnel.ipv4_src = iph->saddr;
	tun_info->tunnel.ipv4_dst = iph->daddr;
	tun_info->tunnel.ipv4_tos = iph->tos;
	tun_info->tunnel.ipv4_ttl = iph->ttl;
	tun_info->tunnel.ipv4_src = saddr;
	tun_info->tunnel.ipv4_dst = daddr;
	tun_info->tunnel.ipv4_tos = tos;
	tun_info->tunnel.ipv4_ttl = ttl;
	tun_info->tunnel.tun_flags = tun_flags;

	/* clear struct padding. */
	memset((unsigned char *)&tun_info->tunnel + OVS_TUNNEL_KEY_SIZE, 0,
	       sizeof(tun_info->tunnel) - OVS_TUNNEL_KEY_SIZE);
	/* For the tunnel types on the top of IPsec, the tp_src and tp_dst of
	 * the upper tunnel are used.
	 * E.g: GRE over IPSEC, the tp_src and tp_port are zero.
	 */
	tun_info->tunnel.tp_src = tp_src;
	tun_info->tunnel.tp_dst = tp_dst;

	/* Clear struct padding. */
	if (sizeof(tun_info->tunnel) != OVS_TUNNEL_KEY_SIZE)
		memset((unsigned char *)&tun_info->tunnel + OVS_TUNNEL_KEY_SIZE,
		       0, sizeof(tun_info->tunnel) - OVS_TUNNEL_KEY_SIZE);

	tun_info->options = opts;
	tun_info->options_len = opts_len;
}

static inline void ovs_flow_tun_info_init(struct ovs_tunnel_info *tun_info,
					  const struct iphdr *iph,
					  __be16 tp_src,
					  __be16 tp_dst,
					  __be64 tun_id,
					  __be16 tun_flags,
					  struct geneve_opt *opts,
					  u8 opts_len)
{
	__ovs_flow_tun_info_init(tun_info, iph->saddr, iph->daddr,
				 iph->tos, iph->ttl,
				 tp_src, tp_dst,
				 tun_id, tun_flags,
				 opts, opts_len);
}

#define OVS_SW_FLOW_KEY_METADATA_SIZE			\
	(offsetof(struct sw_flow_key, recirc_id) +	\
	FIELD_SIZEOF(struct sw_flow_key, recirc_id))

struct sw_flow_key {
	u8 tun_opts[255];
	u8 tun_opts_len;
Loading