Skip to content

Commit

Permalink
openvswitch: add ipv6 'set' action
Browse files Browse the repository at this point in the history
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@nicira.com>
Signed-off-by: Jesse Gross <jesse@nicira.com>
  • Loading branch information
Ansis Atteka authored and Jesse Gross committed Nov 13, 2012
1 parent 9195bb8 commit 3fdbd1c
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 0 deletions.
93 changes: 93 additions & 0 deletions net/openvswitch/actions.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <linux/if_arp.h>
#include <linux/if_vlan.h>
#include <net/ip.h>
#include <net/ipv6.h>
#include <net/checksum.h>
#include <net/dsfield.h>

Expand Down Expand Up @@ -162,6 +163,53 @@ static void set_ip_addr(struct sk_buff *skb, struct iphdr *nh,
*addr = new_addr;
}

static void update_ipv6_checksum(struct sk_buff *skb, u8 l4_proto,
__be32 addr[4], const __be32 new_addr[4])
{
int transport_len = skb->len - skb_transport_offset(skb);

if (l4_proto == IPPROTO_TCP) {
if (likely(transport_len >= sizeof(struct tcphdr)))
inet_proto_csum_replace16(&tcp_hdr(skb)->check, skb,
addr, new_addr, 1);
} else if (l4_proto == IPPROTO_UDP) {
if (likely(transport_len >= sizeof(struct udphdr))) {
struct udphdr *uh = udp_hdr(skb);

if (uh->check || skb->ip_summed == CHECKSUM_PARTIAL) {
inet_proto_csum_replace16(&uh->check, skb,
addr, new_addr, 1);
if (!uh->check)
uh->check = CSUM_MANGLED_0;
}
}
}
}

static void set_ipv6_addr(struct sk_buff *skb, u8 l4_proto,
__be32 addr[4], const __be32 new_addr[4],
bool recalculate_csum)
{
if (recalculate_csum)
update_ipv6_checksum(skb, l4_proto, addr, new_addr);

skb->rxhash = 0;
memcpy(addr, new_addr, sizeof(__be32[4]));
}

static void set_ipv6_tc(struct ipv6hdr *nh, u8 tc)
{
nh->priority = 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 & 0x000F0000) >> 16;
nh->flow_lbl[1] = (fl & 0x0000FF00) >> 8;
nh->flow_lbl[2] = fl & 0x000000FF;
}

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));
Expand Down Expand Up @@ -195,6 +243,47 @@ 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;
__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, ipv6_key->ipv6_proto, saddr,
ipv6_key->ipv6_src, true);

if (memcmp(ipv6_key->ipv6_dst, daddr, sizeof(ipv6_key->ipv6_dst))) {
unsigned int offset = 0;
int flags = IP6_FH_F_SKIP_RH;
bool recalc_csum = true;

if (ipv6_ext_hdr(nh->nexthdr))
recalc_csum = ipv6_find_hdr(skb, &offset,
NEXTHDR_ROUTING, NULL,
&flags) != NEXTHDR_ROUTING;

set_ipv6_addr(skb, ipv6_key->ipv6_proto, daddr,
ipv6_key->ipv6_dst, recalc_csum);
}

set_ipv6_tc(nh, ipv6_key->ipv6_tclass);
set_ipv6_fl(nh, ntohl(ipv6_key->ipv6_label));
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)
Expand Down Expand Up @@ -347,6 +436,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;
Expand Down
20 changes: 20 additions & 0 deletions net/openvswitch/datapath.c
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,7 @@ static int validate_set(const struct nlattr *a,

switch (key_type) {
const struct ovs_key_ipv4 *ipv4_key;
const struct ovs_key_ipv6 *ipv6_key;

case OVS_KEY_ATTR_PRIORITY:
case OVS_KEY_ATTR_ETHERNET:
Expand All @@ -500,6 +501,25 @@ static int validate_set(const struct nlattr *a,

break;

case OVS_KEY_ATTR_IPV6:
if (flow_key->eth.type != htons(ETH_P_IPV6))
return -EINVAL;

if (!flow_key->ip.proto)
return -EINVAL;

ipv6_key = nla_data(ovs_key);
if (ipv6_key->ipv6_proto != flow_key->ip.proto)
return -EINVAL;

if (ipv6_key->ipv6_frag != flow_key->ip.frag)
return -EINVAL;

if (ntohl(ipv6_key->ipv6_label) & 0xFFF00000)
return -EINVAL;

break;

case OVS_KEY_ATTR_TCP:
if (flow_key->ip.proto != IPPROTO_TCP)
return -EINVAL;
Expand Down

0 comments on commit 3fdbd1c

Please sign in to comment.