[ovs-dev] [PATCH] datapath: add ipv6 'set' action

Ansis Atteka aatteka at nicira.com
Sun Oct 28 21:21:23 UTC 2012


This patch adds ipv6 set action functionality. It allows to change
traffic class, flow label, hop-limit, ipv6 source and destination
address fields.

Signed-off-by: Ansis Atteka <aatteka at nicira.com>
---
 datapath/actions.c |   83 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 datapath/compat.h  |   23 +++++++++++++++
 2 files changed, 106 insertions(+)

diff --git a/datapath/actions.c b/datapath/actions.c
index ec9b595..7e60b0f 100644
--- a/datapath/actions.c
+++ b/datapath/actions.c
@@ -165,6 +165,46 @@ static void set_ip_addr(struct sk_buff *skb, struct iphdr *nh,
 	*addr = new_addr;
 }
 
+static void set_ipv6_addr(struct sk_buff *skb, struct ipv6hdr *nh,
+			  __be32 addr[4], const __be32 new_addr[4])
+{
+	int transport_len = skb->len - skb_transport_offset(skb);
+
+	if (nh->nexthdr == IPPROTO_TCP) {
+		if (likely(transport_len >= sizeof(struct tcphdr)))
+			inet_proto_csum_replace16(&tcp_hdr(skb)->check, skb,
+						  addr, new_addr, 1);
+	} else if (nh->nexthdr == IPPROTO_UDP) {
+		if (likely(transport_len >= sizeof(struct udphdr))) {
+			struct udphdr *uh = udp_hdr(skb);
+
+			if (uh->check ||
+			    get_ip_summed(skb) == OVS_CSUM_PARTIAL) {
+				inet_proto_csum_replace16(&uh->check, skb,
+							  addr, new_addr, 1);
+				if (!uh->check)
+					uh->check = CSUM_MANGLED_0;
+			}
+		}
+	}
+
+	skb_clear_rxhash(skb);
+	memcpy(addr, new_addr, sizeof(addr));
+}
+
+static void set_ipv6_tc(struct ipv6hdr *nh, __u8 tc)
+{
+	nh->priority = (nh->priority & 0xf0) | (tc >> 4);
+	nh->flow_lbl[0] = (nh->flow_lbl[0] & 0x0f) | ((tc & 0x0f) << 4);
+}
+
+static void set_ipv6_fl(struct ipv6hdr *nh, __u32 fl)
+{
+	nh->flow_lbl[0] = (nh->flow_lbl[0] & 0xf0) | ((fl & 0x0f0000) >> 16);
+	nh->flow_lbl[1] = (fl & 0x00ff00) >> 8;
+	nh->flow_lbl[2] = fl & 0x0000ff;
+}
+
 static void set_ip_ttl(struct sk_buff *skb, struct iphdr *nh, u8 new_ttl)
 {
 	csum_replace2(&nh->check, htons(nh->ttl << 8), htons(new_ttl << 8));
@@ -198,6 +238,45 @@ static int set_ipv4(struct sk_buff *skb, const struct ovs_key_ipv4 *ipv4_key)
 	return 0;
 }
 
+static int set_ipv6(struct sk_buff *skb, const struct ovs_key_ipv6 *ipv6_key)
+{
+	struct ipv6hdr *nh;
+	int err;
+	__u8 tc;
+	__be32 fl;
+	__be32 *saddr;
+	__be32 *daddr;
+
+	err = make_writable(skb, skb_network_offset(skb) +
+				 sizeof(struct ipv6hdr));
+	if (unlikely(err))
+		return err;
+
+	nh = ipv6_hdr(skb);
+	saddr = (__be32 *)&nh->saddr;
+	daddr = (__be32 *)&nh->daddr;
+
+	if (memcmp(ipv6_key->ipv6_src, saddr, sizeof(ipv6_key->ipv6_src)))
+		set_ipv6_addr(skb, nh, saddr, ipv6_key->ipv6_src);
+
+	if (memcmp(ipv6_key->ipv6_dst, daddr, sizeof(ipv6_key->ipv6_dst)))
+		set_ipv6_addr(skb, nh, daddr, ipv6_key->ipv6_dst);
+
+	tc = (nh->priority << 4) | (nh->flow_lbl[0] >> 4);
+	if (ipv6_key->ipv6_tclass != tc)
+		set_ipv6_tc(nh, ipv6_key->ipv6_tclass);
+
+	fl = (nh->flow_lbl[0] & 0x0f) << 16 | nh->flow_lbl[1] << 8 |
+	      nh->flow_lbl[2];
+	if (ipv6_key->ipv6_label != fl)
+		set_ipv6_fl(nh, ipv6_key->ipv6_label);
+
+	if (ipv6_key->ipv6_hlimit != nh->hop_limit)
+		nh->hop_limit = ipv6_key->ipv6_hlimit;
+
+	return 0;
+}
+
 /* Must follow make_writable() since that can move the skb data. */
 static void set_tp_port(struct sk_buff *skb, __be16 *port,
 			 __be16 new_port, __sum16 *check)
@@ -354,6 +433,10 @@ static int execute_set_action(struct sk_buff *skb,
 		err = set_ipv4(skb, nla_data(nested_attr));
 		break;
 
+	case OVS_KEY_ATTR_IPV6:
+		err = set_ipv6(skb, nla_data(nested_attr));
+		break;
+
 	case OVS_KEY_ATTR_TCP:
 		err = set_tcp(skb, nla_data(nested_attr));
 		break;
diff --git a/datapath/compat.h b/datapath/compat.h
index 3113b96..fb660d2 100644
--- a/datapath/compat.h
+++ b/datapath/compat.h
@@ -81,4 +81,27 @@ static inline void skb_clear_rxhash(struct sk_buff *skb)
 #define SET_NETNSOK    .netnsok = true,
 #endif
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3,6,0)
+static inline void inet_proto_csum_replace16(__sum16 *sum,
+					     struct sk_buff *skb,
+					     const __be32 *from,
+					     const __be32 *to,
+					     int pseudohdr)
+{
+	__be32 diff[] = {
+		~from[0], ~from[1], ~from[2], ~from[3],
+		to[0], to[1], to[2], to[3],
+	};
+	if (skb->ip_summed != CHECKSUM_PARTIAL) {
+		*sum = csum_fold(csum_partial(diff, sizeof(diff),
+				 ~csum_unfold(*sum)));
+		if (skb->ip_summed == CHECKSUM_COMPLETE && pseudohdr)
+			skb->csum = ~csum_partial(diff, sizeof(diff),
+						  ~skb->csum);
+	} else if (pseudohdr)
+		*sum = ~csum_fold(csum_partial(diff, sizeof(diff),
+				  csum_unfold(*sum)));
+}
+#endif
+
 #endif /* compat.h */
-- 
1.7.9.5




More information about the dev mailing list