Skip to content

Commit

Permalink
Support for match & set ICMPv6 reserved and options type fields
Browse files Browse the repository at this point in the history
Currently OVS supports all ARP protocol fields as OXM match fields to
implement the relevant ARP procedures for IPv4. This includes support
for matching copying and setting ARP fields. In IPv6 ARP has been
replaced by ICMPv6 neighbor discovery (ND) procedures, neighbor
advertisement and neighbor solicitation.

The support for ICMPv6 fields in OVS is not complete for the use cases
equivalent to ARP in IPv4. OVS lacks support for matching, copying and
setting the “ND option type” and “ND reserved” fields. Without these user
cannot implement all ICMPv6 ND procedures for IPv6 support.

This commit adds additional OXM fields to OVS for ICMPv6 “ND option type“
and ICMPv6 “ND reserved” using the OXM extension mechanism. This allows
support for parsing these fields from an ICMPv6 packet header and extending
the OpenFlow protocol with specifications for these new OXM fields for
matching, copying and setting.

Signed-off-by: Vishal Deep Ajmera <[email protected]>
Co-authored-by: Ashvin Lakshmikantha <[email protected]>
Signed-off-by: Ashvin Lakshmikantha <[email protected]>
Signed-off-by: Ben Pfaff <[email protected]>
  • Loading branch information
2 people authored and blp committed Feb 4, 2019
1 parent 401eacf commit 9b2b849
Show file tree
Hide file tree
Showing 18 changed files with 366 additions and 14 deletions.
3 changes: 3 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ Post-v2.11.0
- DPDK:
* New option 'other_config:dpdk-socket-limit' to limit amount of
hugepage memory that can be used by DPDK.
- Userspace datapath:
* ICMPv6 ND enhancements: support for match and set ND options type
and reserved fields.


v2.11.0 - xx xxx xxxx
Expand Down
1 change: 1 addition & 0 deletions build-aux/extract-ofp-fields
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ OXM_CLASSES = {"NXM_OF_": (0, 0x0000, 'extension'),
"OXM_OF_": (0, 0x8000, 'standard'),
"OXM_OF_PKT_REG": (0, 0x8001, 'standard'),
"ONFOXM_ET_": (0x4f4e4600, 0xffff, 'standard'),
"ERICOXM_OF_": (0, 0x1000, 'extension'),

# This is the experimenter OXM class for Nicira, which is the
# one that OVS would be using instead of NXM_OF_ and NXM_NX_
Expand Down
8 changes: 8 additions & 0 deletions datapath/linux/compat/include/linux/openvswitch.h
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ enum ovs_key_attr {
#ifndef __KERNEL__
/* Only used within userspace data path. */
OVS_KEY_ATTR_PACKET_TYPE, /* be32 packet type */
OVS_KEY_ATTR_ND_EXTENSIONS, /* struct ovs_key_nd_extensions */
#endif

__OVS_KEY_ATTR_MAX
Expand Down Expand Up @@ -489,6 +490,13 @@ struct ovs_key_nd {
__u8 nd_tll[ETH_ALEN];
};

#ifndef __KERNEL__
struct ovs_key_nd_extensions {
__be32 nd_reserved;
__u8 nd_options_type;
};
#endif

#define OVS_CT_LABELS_LEN_32 4
#define OVS_CT_LABELS_LEN (OVS_CT_LABELS_LEN_32 * sizeof(__u32))
struct ovs_key_ct_labels {
Expand Down
6 changes: 4 additions & 2 deletions include/openvswitch/flow.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ struct flow {
struct in6_addr nd_target; /* IPv6 neighbor discovery (ND) target. */
struct eth_addr arp_sha; /* ARP/ND source hardware address. */
struct eth_addr arp_tha; /* ARP/ND target hardware address. */
ovs_be16 tcp_flags; /* TCP flags. With L3 to avoid matching L4. */
ovs_be16 tcp_flags; /* TCP flags/ICMPv6 ND options type.
* With L3 to avoid matching L4. */
ovs_be16 pad2; /* Pad to 64 bits. */
struct ovs_key_nsh nsh; /* Network Service Header keys */

Expand All @@ -153,7 +154,8 @@ struct flow {
ovs_be16 tp_dst; /* TCP/UDP/SCTP destination port/ICMP code. */
ovs_be16 ct_tp_src; /* CT original tuple source port/ICMP type. */
ovs_be16 ct_tp_dst; /* CT original tuple dst port/ICMP code. */
ovs_be32 igmp_group_ip4; /* IGMP group IPv4 address.
ovs_be32 igmp_group_ip4; /* IGMP group IPv4 address/ICMPv6 ND reserved
* field.
* Keep last for BUILD_ASSERT_DECL below. */
ovs_be32 pad3; /* Pad to 64 bits. */
};
Expand Down
3 changes: 3 additions & 0 deletions include/openvswitch/match.h
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@ void match_set_nd_target(struct match *, const struct in6_addr *);
void match_set_nd_target_masked(struct match *, const struct in6_addr *,
const struct in6_addr *);

void match_set_nd_reserved(struct match *, ovs_be32);
void match_set_nd_options_type(struct match *, uint8_t);

bool match_equal(const struct match *, const struct match *);
uint32_t match_hash(const struct match *, uint32_t basis);

Expand Down
28 changes: 28 additions & 0 deletions include/openvswitch/meta-flow.h
Original file line number Diff line number Diff line change
Expand Up @@ -1796,6 +1796,34 @@ enum OVS_PACKED_ENUM mf_field_id {
*/
MFF_ND_TLL,

/* "nd_reserved".
*
* The reserved field in IPv6 Neighbor Discovery message.
*
* Type: be32.
* Maskable: no.
* Formatting: decimal.
* Prerequisites: ND.
* Access: read/write.
* NXM: none.
* OXM: ERICOXM_OF_ICMPV6_ND_RESERVED(1) since v2.11.
*/
MFF_ND_RESERVED,

/* "nd_options_type".
*
* The type of the option in IPv6 Neighbor Discovery message.
*
* Type: u8.
* Maskable: no.
* Formatting: decimal.
* Prerequisites: ND.
* Access: read/write.
* NXM: none.
* OXM: ERICOXM_OF_ICMPV6_ND_OPTIONS_TYPE(2) since v2.11.
*/
MFF_ND_OPTIONS_TYPE,

/* ## ---- ## */
/* ## NSH ## */
/* ## ---- ## */
Expand Down
58 changes: 50 additions & 8 deletions lib/flow.c
Original file line number Diff line number Diff line change
Expand Up @@ -396,13 +396,14 @@ parse_ethertype(const void **datap, size_t *sizep)
}

/* Returns 'true' if the packet is an ND packet. In that case the '*nd_target'
* and 'arp_buf[]' are filled in. If the packet is not an ND pacet, 'false' is
* returned and no values are filled in on '*nd_target' or 'arp_buf[]'. */
* and 'arp_buf[]' are filled in. If the packet is not an ND packet, 'false'
* is returned and no values are filled in on '*nd_target' or 'arp_buf[]'. */
static inline bool
parse_icmpv6(const void **datap, size_t *sizep, const struct icmp6_hdr *icmp,
const struct in6_addr **nd_target,
struct eth_addr arp_buf[2])
uint32_t *rso_flags, const struct in6_addr **nd_target,
struct eth_addr arp_buf[2], uint8_t *opt_type)
{
const uint32_t *reserved;
if (icmp->icmp6_code != 0 ||
(icmp->icmp6_type != ND_NEIGHBOR_SOLICIT &&
icmp->icmp6_type != ND_NEIGHBOR_ADVERT)) {
Expand All @@ -411,6 +412,15 @@ parse_icmpv6(const void **datap, size_t *sizep, const struct icmp6_hdr *icmp,

arp_buf[0] = eth_addr_zero;
arp_buf[1] = eth_addr_zero;
*opt_type = 0;

reserved = data_try_pull(datap, sizep, sizeof(uint32_t));
if (OVS_UNLIKELY(!reserved)) {
/* Invalid ND packet. */
return false;
}
*rso_flags = *reserved;

*nd_target = data_try_pull(datap, sizep, sizeof **nd_target);
if (OVS_UNLIKELY(!*nd_target)) {
return true;
Expand All @@ -432,12 +442,20 @@ parse_icmpv6(const void **datap, size_t *sizep, const struct icmp6_hdr *icmp,
if (lla_opt->type == ND_OPT_SOURCE_LINKADDR && opt_len == 8) {
if (OVS_LIKELY(eth_addr_is_zero(arp_buf[0]))) {
arp_buf[0] = lla_opt->mac;
/* We use only first option type present in ND packet. */
if (*opt_type == 0) {
*opt_type = lla_opt->type;
}
} else {
goto invalid;
}
} else if (lla_opt->type == ND_OPT_TARGET_LINKADDR && opt_len == 8) {
if (OVS_LIKELY(eth_addr_is_zero(arp_buf[1]))) {
arp_buf[1] = lla_opt->mac;
/* We use only first option type present in ND packet. */
if (*opt_type == 0) {
*opt_type = lla_opt->type;
}
} else {
goto invalid;
}
Expand Down Expand Up @@ -987,18 +1005,38 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst)
if (OVS_LIKELY(size >= sizeof(struct icmp6_hdr))) {
const struct in6_addr *nd_target;
struct eth_addr arp_buf[2];
const struct icmp6_hdr *icmp = data_pull(&data, &size,
sizeof *icmp);
if (parse_icmpv6(&data, &size, icmp, &nd_target, arp_buf)) {
/* This will populate whether we received Option 1
* or Option 2. */
uint8_t opt_type;
/* This holds the ND Reserved field. */
uint32_t rso_flags;
const struct icmp6_hdr *icmp = data_pull(&data,
&size,ICMP6_HEADER_LEN);
if (parse_icmpv6(&data, &size, icmp,
&rso_flags, &nd_target, arp_buf, &opt_type)) {
if (nd_target) {
miniflow_push_words(mf, nd_target, nd_target,
sizeof *nd_target / sizeof(uint64_t));
}
miniflow_push_macs(mf, arp_sha, arp_buf);
miniflow_pad_to_64(mf, arp_tha);
/* Populate options field and set the padding
* accordingly. */
if (opt_type != 0) {
miniflow_push_be16(mf, tcp_flags, htons(opt_type));
/* Pad to align with 64 bits.
* This will zero out the pad3 field. */
miniflow_pad_to_64(mf, tcp_flags);
} else {
/* Pad to align with 64 bits.
* This will zero out the tcp_flags & pad3 field. */
miniflow_pad_to_64(mf, arp_tha);
}
miniflow_push_be16(mf, tp_src, htons(icmp->icmp6_type));
miniflow_push_be16(mf, tp_dst, htons(icmp->icmp6_code));
miniflow_pad_to_64(mf, tp_dst);
/* Fill ND reserved field. */
miniflow_push_be32(mf, igmp_group_ip4, htonl(rso_flags));
miniflow_pad_to_64(mf, igmp_group_ip4);
} else {
/* ICMPv6 but not ND. */
miniflow_push_be16(mf, tp_src, htons(icmp->icmp6_type));
Expand Down Expand Up @@ -1927,6 +1965,8 @@ flow_wc_map(const struct flow *flow, struct flowmap *map)
FLOWMAP_SET(map, nd_target);
FLOWMAP_SET(map, arp_sha);
FLOWMAP_SET(map, arp_tha);
FLOWMAP_SET(map, tcp_flags);
FLOWMAP_SET(map, igmp_group_ip4);
} else {
FLOWMAP_SET(map, ct_nw_proto);
FLOWMAP_SET(map, ct_ipv6_src);
Expand Down Expand Up @@ -2968,6 +3008,8 @@ flow_compose_l4(struct dp_packet *p, const struct flow *flow,
struct icmp6_hdr *icmp = dp_packet_put_zeros(p, sizeof *icmp);
icmp->icmp6_type = ntohs(flow->tp_src);
icmp->icmp6_code = ntohs(flow->tp_dst);
uint32_t *reserved = &icmp->icmp6_dataun.icmp6_un_data32[0];
*reserved = ntohl(flow->igmp_group_ip4);

if (icmp->icmp6_code == 0 &&
(icmp->icmp6_type == ND_NEIGHBOR_SOLICIT ||
Expand Down
21 changes: 21 additions & 0 deletions lib/match.c
Original file line number Diff line number Diff line change
Expand Up @@ -1080,6 +1080,19 @@ match_set_nd_target_masked(struct match *match,
match->wc.masks.nd_target = *mask;
}

void
match_set_nd_reserved (struct match *match, ovs_be32 value)
{
match->flow.igmp_group_ip4 = value;
match->wc.masks.igmp_group_ip4 = OVS_BE32_MAX;
}

void
match_set_nd_options_type(struct match *match, uint8_t option)
{
match_set_tcp_flags(match, htons(option));
}

/* Returns true if 'a' and 'b' wildcard the same fields and have the same
* values for fixed fields, otherwise false. */
bool
Expand Down Expand Up @@ -1688,6 +1701,14 @@ match_format(const struct match *match,
&wc->masks.nd_target);
format_eth_masked(s, "nd_sll", f->arp_sha, wc->masks.arp_sha);
format_eth_masked(s, "nd_tll", f->arp_tha, wc->masks.arp_tha);
if (wc->masks.igmp_group_ip4) {
format_be32_masked(s,"nd_reserved", f->igmp_group_ip4,
wc->masks.igmp_group_ip4);
}
if (wc->masks.tcp_flags) {
format_be16_masked(s,"nd_options_type", f->tcp_flags,
wc->masks.tcp_flags);
}
} else {
format_be16_masked(s, "tp_src", f->tp_src, wc->masks.tp_src);
format_be16_masked(s, "tp_dst", f->tp_dst, wc->masks.tp_dst);
Expand Down
38 changes: 38 additions & 0 deletions lib/meta-flow.c
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,11 @@ mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
case MFF_ND_TARGET:
return ipv6_mask_is_any(&wc->masks.nd_target);

case MFF_ND_RESERVED:
return !wc->masks.igmp_group_ip4;
case MFF_ND_OPTIONS_TYPE:
return !wc->masks.tcp_flags;

case MFF_IP_FRAG:
return !(wc->masks.nw_frag & FLOW_NW_FRAG_MASK);

Expand Down Expand Up @@ -571,6 +576,8 @@ mf_is_value_valid(const struct mf_field *mf, const union mf_value *value)
case MFF_ND_TARGET:
case MFF_ND_SLL:
case MFF_ND_TLL:
case MFF_ND_RESERVED:
case MFF_ND_OPTIONS_TYPE:
return true;

case MFF_IN_PORT_OXM:
Expand Down Expand Up @@ -909,9 +916,14 @@ mf_get_value(const struct mf_field *mf, const struct flow *flow,
break;

case MFF_TCP_FLAGS:
case MFF_ND_OPTIONS_TYPE:
value->be16 = flow->tcp_flags;
break;

case MFF_ND_RESERVED:
value->be32 = flow->igmp_group_ip4;
break;

case MFF_ICMPV4_TYPE:
case MFF_ICMPV6_TYPE:
value->u8 = ntohs(flow->tp_src);
Expand Down Expand Up @@ -1259,6 +1271,14 @@ mf_set_value(const struct mf_field *mf,
match_set_nd_target(match, &value->ipv6);
break;

case MFF_ND_RESERVED:
match_set_nd_reserved(match, value->be32);
break;

case MFF_ND_OPTIONS_TYPE:
match_set_nd_options_type(match, value->u8);
break;

case MFF_NSH_FLAGS:
MATCH_SET_FIELD_UINT8(match, nsh.flags, value->u8);
break;
Expand Down Expand Up @@ -1668,6 +1688,14 @@ mf_set_flow_value(const struct mf_field *mf,
flow->nd_target = value->ipv6;
break;

case MFF_ND_RESERVED:
flow->igmp_group_ip4 = value->be32;
break;

case MFF_ND_OPTIONS_TYPE:
flow->tcp_flags = htons(value->u8);
break;

case MFF_NSH_FLAGS:
flow->nsh.flags = value->u8;
break;
Expand Down Expand Up @@ -1823,6 +1851,8 @@ mf_is_pipeline_field(const struct mf_field *mf)
case MFF_ND_TARGET:
case MFF_ND_SLL:
case MFF_ND_TLL:
case MFF_ND_RESERVED:
case MFF_ND_OPTIONS_TYPE:
case MFF_NSH_FLAGS:
case MFF_NSH_TTL:
case MFF_NSH_MDTYPE:
Expand Down Expand Up @@ -2150,6 +2180,11 @@ mf_set_wild(const struct mf_field *mf, struct match *match, char **err_str)
match->wc.masks.arp_tha = eth_addr_zero;
break;

case MFF_ND_RESERVED:
match->wc.masks.igmp_group_ip4 = htonl(0);
match->flow.igmp_group_ip4 = htonl(0);
break;

case MFF_TCP_SRC:
case MFF_UDP_SRC:
case MFF_SCTP_SRC:
Expand All @@ -2169,6 +2204,7 @@ mf_set_wild(const struct mf_field *mf, struct match *match, char **err_str)
break;

case MFF_TCP_FLAGS:
case MFF_ND_OPTIONS_TYPE:
match->wc.masks.tcp_flags = htons(0);
match->flow.tcp_flags = htons(0);
break;
Expand Down Expand Up @@ -2285,6 +2321,8 @@ mf_set(const struct mf_field *mf,
case MFF_ICMPV4_CODE:
case MFF_ICMPV6_TYPE:
case MFF_ICMPV6_CODE:
case MFF_ND_RESERVED:
case MFF_ND_OPTIONS_TYPE:
return OFPUTIL_P_NONE;

case MFF_DP_HASH:
Expand Down
12 changes: 12 additions & 0 deletions lib/meta-flow.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4586,6 +4586,18 @@ r r c c c.
title="ICMPv6 Neighbor Discovery Source Ethernet Address"/>
<field id="MFF_ND_TLL"
title="ICMPv6 Neighbor Discovery Target Ethernet Address"/>
<field id="MFF_ND_RESERVED"
title="ICMPv6 Neighbor Discovery Reserved Field"/>
<p>
This is used to set the R,S,O bits in Neighbor Advertisement Messages
</p>
<field id="MFF_ND_OPTIONS_TYPE"
title="ICMPv6 Neighbor Discovery Options Type Field"/>
<p>
A value of 1 indicates that the option is Source Link Layer.
A value of 2 indicates that the options is Target Link Layer.
See RFC 4861 for further details.
</p>
</group>

<h1>References</h1>
Expand Down
8 changes: 8 additions & 0 deletions lib/nx-match.c
Original file line number Diff line number Diff line change
Expand Up @@ -990,8 +990,16 @@ nxm_put_ip(struct nxm_put_ctx *ctx,
ntohs(flow->tp_dst));
}
if (is_nd(flow, NULL)) {
if (match->wc.masks.igmp_group_ip4) {
nxm_put_32(ctx, MFF_ND_RESERVED, oxm,
flow->igmp_group_ip4);
}
nxm_put_ipv6(ctx, MFF_ND_TARGET, oxm,
&flow->nd_target, &match->wc.masks.nd_target);
if (match->wc.masks.tcp_flags) {
nxm_put_8(ctx, MFF_ND_OPTIONS_TYPE, oxm,
ntohs(flow->tcp_flags));
}
if (flow->tp_src == htons(ND_NEIGHBOR_SOLICIT)) {
nxm_put_eth_masked(ctx, MFF_ND_SLL, oxm,
flow->arp_sha, match->wc.masks.arp_sha);
Expand Down
Loading

0 comments on commit 9b2b849

Please sign in to comment.