This patch adds set-field operations for nd_target, nd_sll, and nd_tll fields
using Nicira extensions and OpenFlow 1.2 protocol. Unit tests are included.
This version performs all ND set-field processing in userspace. All ND
set-field functions pass functional testing, including ICMPv6 checksum
validation and regression test with IPv4/ARP traffic.
Patch was developed against remotes/origin/branch-2.3.
Signed-off-by: Randall A Sharo <randall.sharo at navy.mil>
---
diff --git a/lib/csum.c b/lib/csum.c
index a9334fe..6743651 100644
--- a/lib/csum.c
+++ b/lib/csum.c
@@ -113,6 +113,26 @@ recalc_csum32(ovs_be16 old_csum, ovs_be32 old_u32,
ovs_be32 new_u32)
}
/* Returns the new checksum for a packet in which the checksum field previously
+ * contained 'old_csum' and in which a field that contained the 6 bytes at
+ * 'old_bytes' was changed to contain the 6 bytes at 'new_bytes'. */
+ovs_be16
+recalc_csum48(ovs_be16 old_csum, const void *old_bytes,
+ const void *new_bytes)
+{
+ ovs_be16 new_csum = old_csum;
+ const uint16_t *p16_old = (const uint16_t *)old_bytes,
+ *p16_new = (const uint16_t *)new_bytes;
+ int i;
+
+ for (i = 0; i < 3; ++i) {
+ new_csum = recalc_csum16(new_csum, get_unaligned_be16(&p16_old[i]),
+ get_unaligned_be16(&p16_new[i]));
+ }
+
+ return new_csum;
+}
+
+/* Returns the new checksum for a packet in which the checksum field previously
* contained 'old_csum' and in which a field that contained 'old_u32[4]' was
* changed to contain 'new_u32[4]'. */
ovs_be16
diff --git a/lib/csum.h b/lib/csum.h
index df4b19d..ceff001 100644
--- a/lib/csum.h
+++ b/lib/csum.h
@@ -28,6 +28,8 @@ uint32_t csum_continue(uint32_t partial, const void *,
size_t);
ovs_be16 csum_finish(uint32_t partial);
ovs_be16 recalc_csum16(ovs_be16 old_csum, ovs_be16 old_u16, ovs_be16 new_u16);
ovs_be16 recalc_csum32(ovs_be16 old_csum, ovs_be32 old_u32, ovs_be32 new_u32);
+ovs_be16 recalc_csum48(ovs_be16 old_csum, const void *old_bytes,
+ const void *new_bytes);
ovs_be16 recalc_csum128(ovs_be16 old_csum, ovs_16aligned_be32 old_u32[4],
const ovs_be32 new_u32[4]);
diff --git a/lib/meta-flow.c b/lib/meta-flow.c
index c94e0c5..111027c 100644
--- a/lib/meta-flow.c
+++ b/lib/meta-flow.c
@@ -758,7 +758,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
MFM_FULLY,
MFS_IPV6,
MFP_ND,
- false,
+ true,
NXM_NX_ND_TARGET, "NXM_NX_ND_TARGET",
OXM_OF_IPV6_ND_TARGET, "OXM_OF_IPV6_ND_TARGET", OFP12_VERSION,
OFPUTIL_P_NXM_OXM_ANY,
@@ -770,7 +770,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
MFM_FULLY,
MFS_ETHERNET,
MFP_ND_SOLICIT,
- false,
+ true,
NXM_NX_ND_SLL, "NXM_NX_ND_SLL",
OXM_OF_IPV6_ND_SLL, "OXM_OF_IPV6_ND_SLL", OFP12_VERSION,
OFPUTIL_P_NXM_OXM_ANY,
@@ -782,7 +782,7 @@ const struct mf_field mf_fields[MFF_N_IDS] = {
MFM_FULLY,
MFS_ETHERNET,
MFP_ND_ADVERT,
- false,
+ true,
NXM_NX_ND_TLL, "NXM_NX_ND_TLL",
OXM_OF_IPV6_ND_TLL, "OXM_OF_IPV6_ND_TLL", OFP12_VERSION,
OFPUTIL_P_NXM_OXM_ANY,
diff --git a/lib/odp-execute.c b/lib/odp-execute.c
index cc18536..6c86d13 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -73,6 +73,7 @@ odp_execute_set_action(struct ofpbuf *packet, const struct
nlattr *a,
const struct ovs_key_tcp *tcp_key;
const struct ovs_key_udp *udp_key;
const struct ovs_key_sctp *sctp_key;
+ const struct ovs_key_nd *nd_key;
switch (type) {
case OVS_KEY_ATTR_PRIORITY:
@@ -136,6 +137,12 @@ odp_execute_set_action(struct ofpbuf *packet, const struct
nlattr *a,
md->recirc_id = nl_attr_get_u32(a);
break;
+ case OVS_KEY_ATTR_ND:
+ nd_key = nl_attr_get_unspec(a, sizeof(struct ovs_key_nd));
+ packet_set_nd(packet, nd_key->nd_target, nd_key->nd_sll,
+ nd_key->nd_tll);
+ break;
+
case OVS_KEY_ATTR_UNSPEC:
case OVS_KEY_ATTR_ENCAP:
case OVS_KEY_ATTR_ETHERTYPE:
@@ -143,7 +150,6 @@ odp_execute_set_action(struct ofpbuf *packet, const struct
nlattr *a,
case OVS_KEY_ATTR_VLAN:
case OVS_KEY_ATTR_ICMP:
case OVS_KEY_ATTR_ICMPV6:
- case OVS_KEY_ATTR_ND:
case OVS_KEY_ATTR_TCP_FLAGS:
case __OVS_KEY_ATTR_MAX:
default:
diff --git a/lib/odp-util.c b/lib/odp-util.c
index 8b33ec8..ea4eeb1 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -3754,6 +3754,38 @@ commit_set_arp_action(const struct flow *flow, struct
flow *base,
}
static enum slow_path_reason
+commit_set_nd_action(const struct flow *flow, struct flow *base,
+ struct ofpbuf *odp_actions,
+ struct flow_wildcards *wc)
+{
+ struct ovs_key_nd nd_key;
+
+ /* nd_sll and nd_tll are stored in arp_sha and arp_tha, respectively */
+ if (ipv6_addr_equals(&base->nd_target, &flow->nd_target)
+ && eth_addr_equals(base->arp_sha, flow->arp_sha)
+ && eth_addr_equals(base->arp_tha, flow->arp_tha)) {
+ return 0;
+ }
+
+ memset(&wc->masks.nd_target, 0xff, sizeof wc->masks.nd_target);
+ memset(&wc->masks.arp_sha, 0xff, sizeof wc->masks.arp_sha);
+ memset(&wc->masks.arp_tha, 0xff, sizeof wc->masks.arp_tha);
+
+ base->nd_target = flow->nd_target;
+ memcpy(base->arp_sha, flow->arp_sha, ETH_ADDR_LEN);
+ memcpy(base->arp_tha, flow->arp_tha, ETH_ADDR_LEN);
+
+ memcpy(&nd_key.nd_target, &base->nd_target, sizeof(nd_key.nd_target));
+ memcpy(nd_key.nd_sll, flow->arp_sha, ETH_ADDR_LEN);
+ memcpy(nd_key.nd_tll, flow->arp_tha, ETH_ADDR_LEN);
+
+ commit_set_action(odp_actions, OVS_KEY_ATTR_ND,
+ &nd_key, sizeof(nd_key));
+
+ return SLOW_ACTION;
+}
+
+static enum slow_path_reason
commit_set_nw_action(const struct flow *flow, struct flow *base,
struct ofpbuf *odp_actions, struct flow_wildcards *wc)
{
@@ -3769,7 +3801,7 @@ commit_set_nw_action(const struct flow *flow, struct flow
*base,
case ETH_TYPE_IPV6:
commit_set_ipv6_action(flow, base, odp_actions, wc);
- break;
+ return commit_set_nd_action(flow, base, odp_actions, wc);
case ETH_TYPE_ARP:
return commit_set_arp_action(flow, base, odp_actions, wc);
diff --git a/lib/packets.c b/lib/packets.c
index 65d8109..ee4cb5b 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -875,6 +875,62 @@ packet_set_sctp_port(struct ofpbuf *packet, ovs_be16 src,
ovs_be16 dst)
put_16aligned_be32(&sh->sctp_csum, old_csum ^ old_correct_csum ^ new_csum);
}
+void
+packet_set_nd(struct ofpbuf *packet, const ovs_be32 target[4],
+ const uint8_t sll[ETH_ADDR_LEN],
+ const uint8_t tll[ETH_ADDR_LEN]) {
+ uint8_t *option = ofpbuf_l4(packet);
+ int bytes_remain = (int)ofpbuf_l4_size(packet);
+ struct nd_neighbor_solicit *ns = (struct nd_neighbor_solicit *)option;
+
+ if (bytes_remain < sizeof(*ns)) {
+ return;
+ }
+
+ if (memcmp(&ns->nd_ns_target, target, sizeof(ovs_be32[4]))) {
+ packet_set_ipv6_addr(packet, IPPROTO_ICMPV6,
+ (ovs_16aligned_be32 *)&ns->nd_ns_target,
+ target, true);
+ }
+
+ option += sizeof(*ns);
+ bytes_remain -= sizeof(*ns);
+
+ /* ND sll and tll fields are 8 bytes, including option header */
+ while (bytes_remain >= 8) {
+ struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *) option;
+ int opt_len_bytes = nd_opt->nd_opt_len * 8 - sizeof(*nd_opt);
+
+ option += sizeof(*nd_opt);
+ bytes_remain -= sizeof(*nd_opt);
+
+ if (nd_opt->nd_opt_type == ND_OPT_SOURCE_LINKADDR
+ && opt_len_bytes == ETH_ADDR_LEN) {
+ if (memcmp(option, sll, ETH_ADDR_LEN)) {
+ ovs_be16 *csum = &(ns->nd_ns_hdr.icmp6_cksum);
+ *csum = recalc_csum48(*csum, option, sll);
+ memcpy(option, sll, ETH_ADDR_LEN);
+
+ /* A packet can only contain one SLL or TLL option */
+ break;
+ }
+ } else if (nd_opt->nd_opt_type == ND_OPT_TARGET_LINKADDR
+ && opt_len_bytes == ETH_ADDR_LEN) {
+ if (memcmp(option, tll, ETH_ADDR_LEN)) {
+ ovs_be16 *csum = &(ns->nd_ns_hdr.icmp6_cksum);
+ *csum = recalc_csum48(*csum, option, tll);
+ memcpy(option, tll, ETH_ADDR_LEN);
+
+ /* A packet can only contain one SLL or TLL option */
+ break;
+ }
+ }
+
+ option += opt_len_bytes;
+ bytes_remain -= opt_len_bytes;
+ }
+}
+
const char *
packet_tcp_flag_to_string(uint32_t flag)
{
diff --git a/lib/packets.h b/lib/packets.h
index 9ebf4c1..91d3904 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -687,6 +687,8 @@ void packet_set_ipv6(struct ofpbuf *, uint8_t proto, const
ovs_be32 src[4],
void packet_set_tcp_port(struct ofpbuf *, ovs_be16 src, ovs_be16 dst);
void packet_set_udp_port(struct ofpbuf *, ovs_be16 src, ovs_be16 dst);
void packet_set_sctp_port(struct ofpbuf *, ovs_be16 src, ovs_be16 dst);
+void packet_set_nd(struct ofpbuf *, const ovs_be32 target[4],
+ const uint8_t sll[6], const uint8_t tll[6]);
void packet_format_tcp_flags(struct ds *, uint16_t);
const char *packet_tcp_flag_to_string(uint32_t flag);
diff --git a/tests/ofp-print.at b/tests/ofp-print.at
index c25da53..a467181 100644
--- a/tests/ofp-print.at
+++ b/tests/ofp-print.at
@@ -890,6 +890,23 @@ OFPT_FLOW_MOD (OF1.2) (xid=0x52334507): ADD
priority=255,sctp actions=set_field:
])
AT_CLEANUP
+AT_SETUP([OFPT_FLOW_MOD - OF1.2 - set-field nd_target, nd_sll])
+AT_KEYWORDS([ofp-print])
+AT_CHECK([ovs-ofctl '-vPATTERN:console:%c|%p|%m' ofp-print "\
+03 0e 00 78 00 00 00 02 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff \
+ff ff ff ff ff ff ff ff ff ff ff ff 00 00 00 00 \
+00 01 00 14 80 00 0a 02 86 dd 80 00 14 01 3a 80 \
+00 3a 01 87 00 00 00 00 00 04 00 30 00 00 00 00 \
+00 19 00 18 80 00 3e 10 00 00 00 00 00 00 00 00 \
+00 00 00 00 00 00 00 01 00 19 00 10 80 00 40 06 \
+aa aa aa aa aa aa 00 00
+" 2], [0], [dnl
+OFPT_FLOW_MOD (OF1.2) (xid=0x2): ADD priority=255,icmp6,icmp_type=135
actions=set_field:::1->nd_target,set_field:aa:aa:aa:aa:aa:aa->nd_sll
+], [dnl
+])
+AT_CLEANUP
+
dnl This triggered a buggy "instructions out of order" message earlier.
AT_SETUP([OFPT_FLOW_MOD - OF1.3 - meter])
AT_KEYWORDS([ofp-print])
diff --git a/tests/ofproto-dpif.at b/tests/ofproto-dpif.at
index 1fcd937..dfabc83 100644
--- a/tests/ofproto-dpif.at
+++ b/tests/ofproto-dpif.at
@@ -249,6 +249,25 @@ Datapath actions:
10,set(ipv4(src=192.168.3.91,dst=192.168.0.2,proto=1,tos=0,ttl
OVS_VSWITCHD_STOP
AT_CLEANUP
+AT_SETUP([ofproto-dpif - modify IPv6 Neighbor Solitication (ND)])
+OVS_VSWITCHD_START
+ADD_OF_PORTS([br0], [1], [10], [11], [12], [13])
+AT_DATA([flows.txt], [dnl
+table=0 in_port=1,icmp6,icmpv6_type=135
actions=output(10),write_actions(set_field:fe80::3->nd_target,set_field:aa:aa:aa:aa:aa:aa->nd_sll,output(12)),goto_table(1)
+table=1 icmp6 actions=write_actions(output(13)),goto_table(2)
+table=2 in_port=1,icmp6,icmpv6_type=135
actions=set_field:fe80::4->nd_target,set_field:cc:cc:cc:cc:cc:cc->nd_sll,output(11)
+])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flows br0 flows.txt])
+AT_CHECK([ovs-appctl ofproto/trace br0
'in_port=1,dl_src=50:54:00:00:00:05,dl_dst=50:54:00:00:00:07,icmp6,ipv6_src=fe80::1,ipv6_dst=fe80::2,nw_tos=0,nw_ttl=128,icmpv6_type=135,nd_target=fe80::2020,nd_sll=66:55:44:33:22:11'],
[0], [stdout])
+AT_CHECK([tail -4 stdout], [0],
+ [Megaflow:
recirc_id=0,skb_priority=0,icmp6,in_port=1,nw_frag=no,icmp_type=135,icmp_code=0x0/0xff,nd_target=fe80::2020,nd_sll=66:55:44:33:22:11,nd_tll=00:00:00:00:00:00
+Datapath actions:
10,set(nd(target=fe80::4,sll=cc:cc:cc:cc:cc:cc)),11,set(nd(target=fe80::3,sll=aa:aa:aa:aa:aa:aa)),13
+This flow is handled by the userspace slow path because it:
+ - Uses action(s) not supported by datapath.
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
AT_SETUP([ofproto-dpif - clear actions])
OVS_VSWITCHD_START
ADD_OF_PORTS([br0], [1], [10], [11], [12])
diff --git a/tests/ofproto.at b/tests/ofproto.at
index 5162938..9e5c142 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -617,7 +617,9 @@ AT_SETUP([ofproto - set-field flow_mod commands (NXM)])
OVS_VSWITCHD_START
AT_CHECK([ovs-ofctl add-flow br0 ipv6,table=1,in_port=3,actions=drop])
AT_CHECK([ovs-ofctl add-flow br0
ipv6,table=1,in_port=3,actions=set_field:fe80:0123:4567:890a:a6ba:dbff:fefe:59fa-\>ipv6_src])
+AT_CHECK([ovs-ofctl add-flow br0
icmp6,icmp_type=136,table=1,in_port=3,actions=set_field:fe80:8675:3097:890a:a6ba:dbff:f00d:59fa-\>nd_target,set_field:cc:dd:ee:ff:00:11-\>nd_tll])
AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
+ table=1, icmp6,in_port=3,icmp_type=136
actions=load:0xa6badbfff00d59fa->NXM_NX_ND_TARGET[[0..63]],load:0xfe8086753097890a->NXM_NX_ND_TARGET[[64..127]],load:0xccddeeff0011->NXM_NX_ND_TLL[[]]
table=1, ipv6,in_port=3
actions=load:0xa6badbfffefe59fa->NXM_NX_IPV6_SRC[[0..63]],load:0xfe8001234567890a->NXM_NX_IPV6_SRC[[64..127]]
NXST_FLOW reply:
])
@@ -643,6 +645,19 @@ AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 |
ofctl_strip], [0], [OFPST_FLO
OVS_VSWITCHD_STOP
AT_CLEANUP
+AT_SETUP([ofproto - set-field flow_mod commands (OF1.2)])
+OVS_VSWITCHD_START
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0
ipv6,table=1,in_port=3,actions=drop])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0
ipv6,table=1,in_port=3,actions=set_field:fe80:0123:4567:890a:a6ba:dbff:fefe:59fa-\>ipv6_src])
+AT_CHECK([ovs-ofctl -O OpenFlow12 add-flow br0
icmp6,icmp_type=136,table=1,in_port=3,actions=set_field:fe80:8675:3097:890a:a6ba:dbff:f00d:59fa-\>nd_target,set_field:cc:dd:ee:ff:00:11-\>nd_tll])
+AT_CHECK([ovs-ofctl -O OpenFlow12 dump-flows br0 | ofctl_strip | sort], [0],
[dnl
+ table=1, icmp6,in_port=3,icmp_type=136
actions=set_field:fe80:8675:3097:890a:a6ba:dbff:f00d:59fa->nd_target,set_field:cc:dd:ee:ff:00:11->nd_tll
+ table=1, ipv6,in_port=3
actions=set_field:fe80:123:4567:890a:a6ba:dbff:fefe:59fa->ipv6_src
+OFPST_FLOW reply (OF1.2):
+])
+OVS_VSWITCHD_STOP
+AT_CLEANUP
+
AT_SETUP([ofproto - dump flows with cookie])
OVS_VSWITCHD_START
AT_CHECK([ovs-ofctl add-flow br0 cookie=0x1,in_port=1,actions=1])
_______________________________________________
dev mailing list
[email protected]
http://openvswitch.org/mailman/listinfo/dev