diff mbox

[ovs-dev] match: Add support for matching IGMP fields.

Message ID 1449783761-63312-1-git-send-email-jarno@ovn.org
State Changes Requested
Headers show

Commit Message

Jarno Rajahalme Dec. 10, 2015, 9:42 p.m. UTC
Complete the IGMP protocol support by making IGMP fields (type, code,
and group) matchable via OpenFlow by the way of new Nicira extensions.

The new fields are: 8-bit NXM_NX_IGMP_TYPE (111), 8-bit
NXM_NX_IGMP_CODE (112), and 32-bit NXM_NX_IGMP_GROUP (113).

VMware-BZ: #1558992
Signed-off-by: Jarno Rajahalme <jarno@ovn.org>
---
 build-aux/extract-ofp-fields |  1 +
 lib/match.c                  | 15 ++++++++++++++
 lib/match.h                  |  3 +++
 lib/meta-flow.c              | 45 +++++++++++++++++++++++++++++++++++++++++-
 lib/meta-flow.h              | 47 ++++++++++++++++++++++++++++++++++++++++++++
 lib/nx-match.c               |  9 +++++++++
 tests/ofproto.at             |  3 +++
 tests/ovs-ofctl.at           | 38 +++++++++++++++++++++++++++++++++++
 8 files changed, 160 insertions(+), 1 deletion(-)

Comments

Ben Pfaff Dec. 14, 2015, 11:12 a.m. UTC | #1
On Thu, Dec 10, 2015 at 01:42:41PM -0800, Jarno Rajahalme wrote:
> Complete the IGMP protocol support by making IGMP fields (type, code,
> and group) matchable via OpenFlow by the way of new Nicira extensions.
> 
> The new fields are: 8-bit NXM_NX_IGMP_TYPE (111), 8-bit
> NXM_NX_IGMP_CODE (112), and 32-bit NXM_NX_IGMP_GROUP (113).
> 
> VMware-BZ: #1558992
> Signed-off-by: Jarno Rajahalme <jarno@ovn.org>

Is this something we're targeting to backport to OVS 2.5?  If not, then
the meta-flow.h headers should say "since v2.6" instead of "since
v2.5".

This needs to add an item to NEWS and documentation to ovs-ofctl(8).

What's here looks good, and I trust you to do a good job on the above,
so:
Acked-by: Ben Pfaff <blp@ovn.org>
Jarno Rajahalme Dec. 15, 2015, 12:36 a.m. UTC | #2
> On Dec 14, 2015, at 3:12 AM, Ben Pfaff <blp@ovn.org> wrote:
> 
> On Thu, Dec 10, 2015 at 01:42:41PM -0800, Jarno Rajahalme wrote:
>> Complete the IGMP protocol support by making IGMP fields (type, code,
>> and group) matchable via OpenFlow by the way of new Nicira extensions.
>> 
>> The new fields are: 8-bit NXM_NX_IGMP_TYPE (111), 8-bit
>> NXM_NX_IGMP_CODE (112), and 32-bit NXM_NX_IGMP_GROUP (113).
>> 
>> VMware-BZ: #1558992
>> Signed-off-by: Jarno Rajahalme <jarno@ovn.org>
> 
> Is this something we're targeting to backport to OVS 2.5?  If not, then
> the meta-flow.h headers should say "since v2.6" instead of "since
> v2.5".
> 
> This needs to add an item to NEWS and documentation to ovs-ofctl(8).
> 
> What's here looks good, and I trust you to do a good job on the above,
> so:
> Acked-by: Ben Pfaff <blp@ovn.org>


Ben,

While doing this I just realized that this may require further work, as the kernel module does not seem to parse the IGMP fields!

  Jarno
Ben Pfaff Dec. 16, 2015, 1:47 a.m. UTC | #3
On Mon, Dec 14, 2015 at 04:36:08PM -0800, Jarno Rajahalme wrote:
> 
> > On Dec 14, 2015, at 3:12 AM, Ben Pfaff <blp@ovn.org> wrote:
> > 
> > On Thu, Dec 10, 2015 at 01:42:41PM -0800, Jarno Rajahalme wrote:
> >> Complete the IGMP protocol support by making IGMP fields (type, code,
> >> and group) matchable via OpenFlow by the way of new Nicira extensions.
> >> 
> >> The new fields are: 8-bit NXM_NX_IGMP_TYPE (111), 8-bit
> >> NXM_NX_IGMP_CODE (112), and 32-bit NXM_NX_IGMP_GROUP (113).
> >> 
> >> VMware-BZ: #1558992
> >> Signed-off-by: Jarno Rajahalme <jarno@ovn.org>
> > 
> > Is this something we're targeting to backport to OVS 2.5?  If not, then
> > the meta-flow.h headers should say "since v2.6" instead of "since
> > v2.5".
> > 
> > This needs to add an item to NEWS and documentation to ovs-ofctl(8).
> > 
> > What's here looks good, and I trust you to do a good job on the above,
> > so:
> > Acked-by: Ben Pfaff <blp@ovn.org>
> 
> While doing this I just realized that this may require further work,
> as the kernel module does not seem to parse the IGMP fields!

Userspace should be able to handle that.  It might require a little work
in odp-util.c to mark the received packet as ODP_FIT_TOO_LITTLE.
Jarno Rajahalme Dec. 16, 2015, 1:48 a.m. UTC | #4
> On Dec 15, 2015, at 5:47 PM, Ben Pfaff <blp@ovn.org> wrote:
> 
> On Mon, Dec 14, 2015 at 04:36:08PM -0800, Jarno Rajahalme wrote:
>> 
>>> On Dec 14, 2015, at 3:12 AM, Ben Pfaff <blp@ovn.org> wrote:
>>> 
>>> On Thu, Dec 10, 2015 at 01:42:41PM -0800, Jarno Rajahalme wrote:
>>>> Complete the IGMP protocol support by making IGMP fields (type, code,
>>>> and group) matchable via OpenFlow by the way of new Nicira extensions.
>>>> 
>>>> The new fields are: 8-bit NXM_NX_IGMP_TYPE (111), 8-bit
>>>> NXM_NX_IGMP_CODE (112), and 32-bit NXM_NX_IGMP_GROUP (113).
>>>> 
>>>> VMware-BZ: #1558992
>>>> Signed-off-by: Jarno Rajahalme <jarno@ovn.org>
>>> 
>>> Is this something we're targeting to backport to OVS 2.5?  If not, then
>>> the meta-flow.h headers should say "since v2.6" instead of "since
>>> v2.5".
>>> 
>>> This needs to add an item to NEWS and documentation to ovs-ofctl(8).
>>> 
>>> What's here looks good, and I trust you to do a good job on the above,
>>> so:
>>> Acked-by: Ben Pfaff <blp@ovn.org>
>> 
>> While doing this I just realized that this may require further work,
>> as the kernel module does not seem to parse the IGMP fields!
> 
> Userspace should be able to handle that.  It might require a little work
> in odp-util.c to mark the received packet as ODP_FIT_TOO_LITTLE.

Justin suggested the same offline. I’ll have a look at this tomorrow,

  Jarno
Jarno Rajahalme Dec. 16, 2015, 9:22 p.m. UTC | #5
> On Dec 15, 2015, at 5:48 PM, Jarno Rajahalme <jarno@ovn.org> wrote:
> 
> 
>> On Dec 15, 2015, at 5:47 PM, Ben Pfaff <blp@ovn.org <mailto:blp@ovn.org>> wrote:
>> 
>> On Mon, Dec 14, 2015 at 04:36:08PM -0800, Jarno Rajahalme wrote:
>>> 
>>>> On Dec 14, 2015, at 3:12 AM, Ben Pfaff <blp@ovn.org <mailto:blp@ovn.org>> wrote:
>>>> 
>>>> On Thu, Dec 10, 2015 at 01:42:41PM -0800, Jarno Rajahalme wrote:
>>>>> Complete the IGMP protocol support by making IGMP fields (type, code,
>>>>> and group) matchable via OpenFlow by the way of new Nicira extensions.
>>>>> 
>>>>> The new fields are: 8-bit NXM_NX_IGMP_TYPE (111), 8-bit
>>>>> NXM_NX_IGMP_CODE (112), and 32-bit NXM_NX_IGMP_GROUP (113).
>>>>> 
>>>>> VMware-BZ: #1558992
>>>>> Signed-off-by: Jarno Rajahalme <jarno@ovn.org <mailto:jarno@ovn.org>>
>>>> 
>>>> Is this something we're targeting to backport to OVS 2.5?  If not, then
>>>> the meta-flow.h headers should say "since v2.6" instead of "since
>>>> v2.5".
>>>> 
>>>> This needs to add an item to NEWS and documentation to ovs-ofctl(8).
>>>> 
>>>> What's here looks good, and I trust you to do a good job on the above,
>>>> so:
>>>> Acked-by: Ben Pfaff <blp@ovn.org <mailto:blp@ovn.org>>
>>> 
>>> While doing this I just realized that this may require further work,
>>> as the kernel module does not seem to parse the IGMP fields!
>> 
>> Userspace should be able to handle that.  It might require a little work
>> in odp-util.c to mark the received packet as ODP_FIT_TOO_LITTLE.
> 
> Justin suggested the same offline. I’ll have a look at this tomorrow,
> 

It seems to me that the users in upcall processing and revalidation only care about ODP_FIT_ERROR, i.e., ODP_FIT_TOO_LITTLE and ODP_FIT_TOO_MUCH get the same treatment as ODP_FIT_PERFECT. However, to work as intended, at least for IGMP matching, ODP_FIT_TOO_LITTLE flows should be slow-pathed (a new SLOW_MATCH is needed), and ODP_FIT_TOO_MUCH flows should be exact matched (i.e., non-mega-flow-ed). Making these changes and making sure we don’t introduce new revalidation bugs would take more time than I have now.

  Jarno
Jarno Rajahalme Dec. 16, 2015, 9:46 p.m. UTC | #6
> On Dec 16, 2015, at 1:22 PM, Jarno Rajahalme <jarno@ovn.org> wrote:
> 
> 
>> On Dec 15, 2015, at 5:48 PM, Jarno Rajahalme <jarno@ovn.org <mailto:jarno@ovn.org>> wrote:
>> 
>> 
>>> On Dec 15, 2015, at 5:47 PM, Ben Pfaff <blp@ovn.org <mailto:blp@ovn.org>> wrote:
>>> 
>>> On Mon, Dec 14, 2015 at 04:36:08PM -0800, Jarno Rajahalme wrote:
>>>> 
>>>>> On Dec 14, 2015, at 3:12 AM, Ben Pfaff <blp@ovn.org <mailto:blp@ovn.org>> wrote:
>>>>> 
>>>>> On Thu, Dec 10, 2015 at 01:42:41PM -0800, Jarno Rajahalme wrote:
>>>>>> Complete the IGMP protocol support by making IGMP fields (type, code,
>>>>>> and group) matchable via OpenFlow by the way of new Nicira extensions.
>>>>>> 
>>>>>> The new fields are: 8-bit NXM_NX_IGMP_TYPE (111), 8-bit
>>>>>> NXM_NX_IGMP_CODE (112), and 32-bit NXM_NX_IGMP_GROUP (113).
>>>>>> 
>>>>>> VMware-BZ: #1558992
>>>>>> Signed-off-by: Jarno Rajahalme <jarno@ovn.org <mailto:jarno@ovn.org>>
>>>>> 
>>>>> Is this something we're targeting to backport to OVS 2.5?  If not, then
>>>>> the meta-flow.h headers should say "since v2.6" instead of "since
>>>>> v2.5".
>>>>> 
>>>>> This needs to add an item to NEWS and documentation to ovs-ofctl(8).
>>>>> 
>>>>> What's here looks good, and I trust you to do a good job on the above,
>>>>> so:
>>>>> Acked-by: Ben Pfaff <blp@ovn.org <mailto:blp@ovn.org>>
>>>> 
>>>> While doing this I just realized that this may require further work,
>>>> as the kernel module does not seem to parse the IGMP fields!
>>> 
>>> Userspace should be able to handle that.  It might require a little work
>>> in odp-util.c to mark the received packet as ODP_FIT_TOO_LITTLE.
>> 
>> Justin suggested the same offline. I’ll have a look at this tomorrow,
>> 
> 
> It seems to me that the users in upcall processing and revalidation only care about ODP_FIT_ERROR, i.e., ODP_FIT_TOO_LITTLE and ODP_FIT_TOO_MUCH get the same treatment as ODP_FIT_PERFECT. However, to work as intended, at least for IGMP matching, ODP_FIT_TOO_LITTLE flows should be slow-pathed (a new SLOW_MATCH is needed), and ODP_FIT_TOO_MUCH flows should be exact matched (i.e., non-mega-flow-ed). Making these changes and making sure we don’t introduce new revalidation bugs would take more time than I have now.
> 

I was too fast here, the ODP_FIT_TOO_MUCH is already treated correctly, as the unknown-to-userspace fields are left wildcarded in the megaflow. However, slowpathing ODP_FIT_TOO_LITTLE flow keys needs to be added in the case where the fields not understood by the datapath are used in the flow translation. Daniele promised to take a stab at it.

  Jarno
diff mbox

Patch

diff --git a/build-aux/extract-ofp-fields b/build-aux/extract-ofp-fields
index 8d43e4b..d1f5825 100755
--- a/build-aux/extract-ofp-fields
+++ b/build-aux/extract-ofp-fields
@@ -46,6 +46,7 @@  PREREQS = {"none": "MFP_NONE",
            "SCTP": "MFP_SCTP",
            "ICMPv4": "MFP_ICMPV4",
            "ICMPv6": "MFP_ICMPV6",
+           "IGMP": "MFP_IGMP",
            "ND": "MFP_ND",
            "ND solicit": "MFP_ND_SOLICIT",
            "ND advert": "MFP_ND_ADVERT"}
diff --git a/lib/match.c b/lib/match.c
index 95d34bc..a725737 100644
--- a/lib/match.c
+++ b/lib/match.c
@@ -725,6 +725,21 @@  match_set_icmp_code(struct match *match, uint8_t icmp_code)
 }
 
 void
+match_set_igmp_group(struct match *match, ovs_be32 igmp_group)
+{
+    match->flow.igmp_group_ip4 = igmp_group;
+    match->wc.masks.igmp_group_ip4 = OVS_BE32_MAX;
+}
+
+void
+match_set_igmp_group_masked(struct match *match, ovs_be32 igmp_group,
+                            ovs_be32 mask)
+{
+    match->flow.igmp_group_ip4 = igmp_group & mask;
+    match->wc.masks.igmp_group_ip4 = mask;
+}
+
+void
 match_set_arp_sha(struct match *match, const struct eth_addr sha)
 {
     match->flow.arp_sha = sha;
diff --git a/lib/match.h b/lib/match.h
index 650a203..66a3664 100644
--- a/lib/match.h
+++ b/lib/match.h
@@ -125,6 +125,9 @@  void match_set_mpls_lse(struct match *, int idx, ovs_be32 lse);
 void match_set_tp_src_masked(struct match *, ovs_be16 port, ovs_be16 mask);
 void match_set_tp_dst(struct match *, ovs_be16);
 void match_set_tp_dst_masked(struct match *, ovs_be16 port, ovs_be16 mask);
+void match_set_igmp_group(struct match *, ovs_be32);
+void match_set_igmp_group_masked(struct match *, ovs_be32 igmp_group,
+                                 ovs_be32 mask);
 void match_set_tcp_flags(struct match *, ovs_be16);
 void match_set_tcp_flags_masked(struct match *, ovs_be16 flags, ovs_be16 mask);
 void match_set_nw_proto(struct match *, uint8_t);
diff --git a/lib/meta-flow.c b/lib/meta-flow.c
index 6bd0b99..04f75e2 100644
--- a/lib/meta-flow.c
+++ b/lib/meta-flow.c
@@ -306,15 +306,19 @@  mf_is_all_wild(const struct mf_field *mf, const struct flow_wildcards *wc)
     case MFF_SCTP_SRC:
     case MFF_ICMPV4_TYPE:
     case MFF_ICMPV6_TYPE:
+    case MFF_IGMP_TYPE:
         return !wc->masks.tp_src;
     case MFF_TCP_DST:
     case MFF_UDP_DST:
     case MFF_SCTP_DST:
     case MFF_ICMPV4_CODE:
     case MFF_ICMPV6_CODE:
+    case MFF_IGMP_CODE:
         return !wc->masks.tp_dst;
     case MFF_TCP_FLAGS:
         return !wc->masks.tcp_flags;
+    case MFF_IGMP_GROUP:
+        return !wc->masks.igmp_group_ip4;
 
     case MFF_N_IDS:
     default:
@@ -387,7 +391,9 @@  mf_are_prereqs_ok(const struct mf_field *mf, const struct flow *flow)
         return is_icmpv4(flow);
     case MFP_ICMPV6:
         return is_icmpv6(flow);
-
+    case MFP_IGMP:
+        return is_igmp(flow);
+        
     case MFP_ND:
         return (is_icmpv6(flow)
                 && flow->tp_dst == htons(0)
@@ -426,6 +432,7 @@  mf_mask_field_and_prereqs(const struct mf_field *mf, struct flow_wildcards *wc)
     case MFP_SCTP:
     case MFP_ICMPV4:
     case MFP_ICMPV6:
+    case MFP_IGMP:
         /* nw_frag always unwildcarded. */
         WC_MASK_FIELD(wc, nw_proto);
         /* Fall through. */
@@ -462,6 +469,7 @@  mf_bitmap_set_field_and_prereqs(const struct mf_field *mf, struct mf_bitmap *bm)
     case MFP_SCTP:
     case MFP_ICMPV4:
     case MFP_ICMPV6:
+    case MFP_IGMP:
         /* nw_frag always unwildcarded. */
         bitmap_set1(bm->bm, MFF_IP_PROTO);
         /* Fall through. */
@@ -540,6 +548,9 @@  mf_is_value_valid(const struct mf_field *mf, const union mf_value *value)
     case MFF_ICMPV4_CODE:
     case MFF_ICMPV6_TYPE:
     case MFF_ICMPV6_CODE:
+    case MFF_IGMP_TYPE:
+    case MFF_IGMP_CODE:
+    case MFF_IGMP_GROUP:
     case MFF_ND_TARGET:
     case MFF_ND_SLL:
     case MFF_ND_TLL:
@@ -818,14 +829,20 @@  mf_get_value(const struct mf_field *mf, const struct flow *flow,
 
     case MFF_ICMPV4_TYPE:
     case MFF_ICMPV6_TYPE:
+    case MFF_IGMP_TYPE:
         value->u8 = ntohs(flow->tp_src);
         break;
 
     case MFF_ICMPV4_CODE:
     case MFF_ICMPV6_CODE:
+    case MFF_IGMP_CODE:
         value->u8 = ntohs(flow->tp_dst);
         break;
 
+    case MFF_IGMP_GROUP:
+        value->be32 = flow->igmp_group_ip4;
+        break;
+
     case MFF_ND_TARGET:
         value->ipv6 = flow->nd_target;
         break;
@@ -1072,14 +1089,20 @@  mf_set_value(const struct mf_field *mf,
 
     case MFF_ICMPV4_TYPE:
     case MFF_ICMPV6_TYPE:
+    case MFF_IGMP_TYPE:
         match_set_icmp_type(match, value->u8);
         break;
 
     case MFF_ICMPV4_CODE:
     case MFF_ICMPV6_CODE:
+    case MFF_IGMP_CODE:
         match_set_icmp_code(match, value->u8);
         break;
 
+    case MFF_IGMP_GROUP:
+        match_set_igmp_group(match, value->be32);
+        break;
+
     case MFF_ND_TARGET:
         match_set_nd_target(match, &value->ipv6);
         break;
@@ -1381,14 +1404,20 @@  mf_set_flow_value(const struct mf_field *mf,
 
     case MFF_ICMPV4_TYPE:
     case MFF_ICMPV6_TYPE:
+    case MFF_IGMP_TYPE:
         flow->tp_src = htons(value->u8);
         break;
 
     case MFF_ICMPV4_CODE:
     case MFF_ICMPV6_CODE:
+    case MFF_IGMP_CODE:
         flow->tp_dst = htons(value->u8);
         break;
 
+    case MFF_IGMP_GROUP:
+        flow->igmp_group_ip4 = value->be32;
+        break;
+
     case MFF_ND_TARGET:
         flow->nd_target = value->ipv6;
         break;
@@ -1689,6 +1718,7 @@  mf_set_wild(const struct mf_field *mf, struct match *match, char **err_str)
     case MFF_SCTP_SRC:
     case MFF_ICMPV4_TYPE:
     case MFF_ICMPV6_TYPE:
+    case MFF_IGMP_TYPE:
         match->wc.masks.tp_src = htons(0);
         match->flow.tp_src = htons(0);
         break;
@@ -1698,10 +1728,15 @@  mf_set_wild(const struct mf_field *mf, struct match *match, char **err_str)
     case MFF_SCTP_DST:
     case MFF_ICMPV4_CODE:
     case MFF_ICMPV6_CODE:
+    case MFF_IGMP_CODE:
         match->wc.masks.tp_dst = htons(0);
         match->flow.tp_dst = htons(0);
         break;
 
+    case MFF_IGMP_GROUP:
+        match_set_igmp_group_masked(match, htonl(0), htonl(0));
+        break;
+
     case MFF_TCP_FLAGS:
         match->wc.masks.tcp_flags = htons(0);
         match->flow.tcp_flags = htons(0);
@@ -1782,6 +1817,10 @@  mf_set(const struct mf_field *mf,
     case MFF_ICMPV4_CODE:
     case MFF_ICMPV6_TYPE:
     case MFF_ICMPV6_CODE:
+    case MFF_IGMP_TYPE:
+    case MFF_IGMP_CODE:
+        /* All of these are non-maskable, so no protocol can support masked
+         * match on them. */
         return OFPUTIL_P_NONE;
 
     case MFF_DP_HASH:
@@ -1887,6 +1926,10 @@  mf_set(const struct mf_field *mf,
         match_set_nw_dst_masked(match, value->be32, mask->be32);
         break;
 
+    case MFF_IGMP_GROUP:
+        match_set_igmp_group_masked(match, value->be32, mask->be32);
+        break;
+
     case MFF_IPV6_SRC:
         match_set_ipv6_src_masked(match, &value->ipv6, &mask->ipv6);
         break;
diff --git a/lib/meta-flow.h b/lib/meta-flow.h
index 71c238d..40956db 100644
--- a/lib/meta-flow.h
+++ b/lib/meta-flow.h
@@ -1704,6 +1704,52 @@  enum OVS_PACKED_ENUM mf_field_id {
      */
     MFF_ND_TLL,
 
+/* ## ---- ## */
+/* ## IGMP ## */
+/* ## ---- ## */
+
+    /* "igmp_type".
+     *
+     * IGMP type.
+     *
+     * Type: u8.
+     * Maskable: no.
+     * Formatting: decimal.
+     * Prerequisites: IGMP.
+     * Access: read-only.
+     * NXM: NXM_NX_IGMP_TYPE(111) since v2.5.
+     * OXM: none.
+     */
+    MFF_IGMP_TYPE,
+
+    /* "igmp_code".
+     *
+     * IGMP code.
+     *
+     * Type: u8.
+     * Maskable: no.
+     * Formatting: decimal.
+     * Prerequisites: IGMP.
+     * Access: read-only.
+     * NXM: NXM_NX_IGMP_CODE(112) since v2.5.
+     * OXM: none.
+     */
+    MFF_IGMP_CODE,
+
+    /* "igmp_group".
+     *
+     * IGMP group.
+     *
+     * Type: be32.
+     * Maskable: bitwise.
+     * Formatting: IPv4.
+     * Prerequisites: IGMP.
+     * Access: read-only.
+     * NXM: NXM_NX_IGMP_GROUP(113) since v2.5.
+     * OXM: none.
+     */
+    MFF_IGMP_GROUP,
+
     MFF_N_IDS
 };
 
@@ -1792,6 +1838,7 @@  enum OVS_PACKED_ENUM mf_prereqs {
     MFP_SCTP,                   /* On IPv4 or IPv6. */
     MFP_ICMPV4,
     MFP_ICMPV6,
+    MFP_IGMP,                   /* An IPv4 protocol only. */
 
     /* L2+L3+L4 requirements. */
     MFP_ND,
diff --git a/lib/nx-match.c b/lib/nx-match.c
index 11bcd95..5198b07 100644
--- a/lib/nx-match.c
+++ b/lib/nx-match.c
@@ -883,6 +883,15 @@  nxm_put_ip(struct ofpbuf *b, const struct match *match, enum ofp_version oxm)
                                        flow->arp_tha, match->wc.masks.arp_tha);
                 }
             }
+        } else if (is_igmp(flow)) {
+            if (match->wc.masks.tp_src) {
+                nxm_put_8(b, MFF_IGMP_TYPE, oxm, ntohs(flow->tp_src));
+            }
+            if (match->wc.masks.tp_dst) {
+                nxm_put_8(b, MFF_IGMP_CODE, oxm, ntohs(flow->tp_dst));
+            }
+            nxm_put_32m(b, MFF_IGMP_GROUP, oxm, flow->igmp_group_ip4,
+                        match->wc.masks.igmp_group_ip4);
         }
     }
 }
diff --git a/tests/ofproto.at b/tests/ofproto.at
index c22d79f..d0425a9 100644
--- a/tests/ofproto.at
+++ b/tests/ofproto.at
@@ -1858,6 +1858,9 @@  metadata in_port in_port_oxm pkt_mark ct_mark ct_label reg0 reg1 reg2 reg3 reg4
       nd_target: arbitrary mask
       nd_sll: arbitrary mask
       nd_tll: arbitrary mask
+      igmp_type: exact match or wildcard
+      igmp_code: exact match or wildcard
+      igmp_group: arbitrary mask
 
 ' $1
 }
diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at
index 2923cf2..b4d1d0f 100644
--- a/tests/ovs-ofctl.at
+++ b/tests/ovs-ofctl.at
@@ -689,6 +689,25 @@  NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(00) NXM_OF_ICMP_CODE(10)
 NXM_OF_ETH_TYPE(0800) NXM_OF_ICMP_CODE(10)
 NXM_OF_ICMP_CODE(00)
 
+# IGMP type
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(02) NXM_NX_IGMP_TYPE(11)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(00) NXM_NX_IGMP_TYPE(10)
+
+# IGMP code
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(02) NXM_NX_IGMP_CODE(7f)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(00) NXM_NX_IGMP_CODE(10)
+NXM_OF_ETH_TYPE(0800) NXM_NX_IGMP_CODE(10)
+NXM_NX_IGMP_CODE(00)
+
+# IGMP Group
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(02) NXM_NX_IGMP_GROUP(e0000001)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(02) NXM_NX_IGMP_GROUP_W(e0000000/FFFF0000)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(02) NXM_NX_IGMP_GROUP_W(e0000001/5a5a5a5a)
+NXM_OF_ETH_TYPE(0800) NXM_OF_IP_PROTO(02) NXM_NX_IGMP_GROUP_W(e0000001/ffffffff)
+NXM_OF_ETH_TYPE(0800) NXM_NX_IGMP_GROUP(e0000001)
+NXM_NX_IGMP_GROUP(e0000001)
+NXM_OF_ETH_TYPE(0806) NXM_OF_IP_PROTO(02) NXM_NX_IGMP_GROUP(e0000001)
+
 # ARP opcode
 NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_OP(0001)
 NXM_OF_ETH_TYPE(0806) NXM_OF_ARP_OP(1111)
@@ -991,6 +1010,25 @@  nx_pull_match() returned error OFPBMC_BAD_PREREQ
 nx_pull_match() returned error OFPBMC_BAD_PREREQ
 nx_pull_match() returned error OFPBMC_BAD_PREREQ
 
+# IGMP type
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(02), NXM_NX_IGMP_TYPE(11)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# IGMP code
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(02), NXM_NX_IGMP_CODE(7f)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
+# IGMP Group
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(02), NXM_NX_IGMP_GROUP(e0000001)
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(02), NXM_NX_IGMP_GROUP_W(e0000000/ffff0000)
+nx_pull_match() returned error OFPBMC_BAD_WILDCARDS
+NXM_OF_ETH_TYPE(0800), NXM_OF_IP_PROTO(02), NXM_NX_IGMP_GROUP(e0000001)
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+nx_pull_match() returned error OFPBMC_BAD_PREREQ
+
 # ARP opcode
 NXM_OF_ETH_TYPE(0806), NXM_OF_ARP_OP(0001)
 nx_pull_match() returned error OFPBMC_BAD_VALUE