diff mbox

[ethtool,2/3] Add IPv6 support to NFC

Message ID 56C1E7EC.8020700@solarflare.com
State Not Applicable, archived
Delegated to: David Miller
Headers show

Commit Message

Edward Cree Feb. 15, 2016, 2:59 p.m. UTC
Signed-off-by: Edward Cree <ecree@solarflare.com>
---
 ethtool.c |  21 +++++
 rxclass.c | 272 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 279 insertions(+), 14 deletions(-)

Comments

Ben Hutchings March 13, 2016, 4:43 p.m. UTC | #1
On Mon, 2016-02-15 at 14:59 +0000, Edward Cree wrote:
> Signed-off-by: Edward Cree <ecree@solarflare.com>
[...]
> @@ -950,6 +1154,19 @@ static int rxclass_get_mask(char *str, unsigned char *p,
>                 *(__be32 *)&p[opt->moffset] = ~val;
>                 break;
>         }
> +       case OPT_IP6: {
> +               __be32 val[4];
> +               int i;
> +               err = rxclass_get_ipv6(str, val);
> +               if (err)
> +                       return -1;
> +               for (i = 0; i < 4; i++) {
> +                       ((__be32 *)&p[opt->offset])[i] = val[i];
> +                       if (opt->moffset >= 0)
> +                               ((__be32 *)&p[opt->moffset])[i] = ~val[i];

This pointer arithmetic looks terrible.  I think memcpy() would be much
clearer here.

[...]
> The information contained in this message is confidential and is
> intended for the addressee(s) only. If you have received this message
> in error, please notify the sender immediately and delete the
> message. Unless you are an addressee (or authorized to receive for an
> addressee), you may not use, copy or disclose to anyone this message
> or any information contained in this message. The unauthorized use,
> disclosure, copying or alteration of this message is strictly
> prohibited.

I won't apply patches labelled as "confidential".  You need to stop
including this nonsense in your public messages (I thought you fixed
this once before).

Ben.
Edward Cree March 16, 2016, 2:53 p.m. UTC | #2
On 13/03/16 16:43, Ben Hutchings wrote:
> On Mon, 2016-02-15 at 14:59 +0000, Edward Cree wrote:
>> Signed-off-by: Edward Cree <ecree@solarflare.com>
> [...]
>> @@ -950,6 +1154,19 @@ static int rxclass_get_mask(char *str, unsigned char *p,
>>                 *(__be32 *)&p[opt->moffset] = ~val;
>>                 break;
>>         }
>> +       case OPT_IP6: {
>> +               __be32 val[4];
>> +               int i;
>> +               err = rxclass_get_ipv6(str, val);
>> +               if (err)
>> +                       return -1;
>> +               for (i = 0; i < 4; i++) {
>> +                       ((__be32 *)&p[opt->offset])[i] = val[i];
>> +                       if (opt->moffset >= 0)
>> +                               ((__be32 *)&p[opt->moffset])[i] = ~val[i];
> This pointer arithmetic looks terrible.  I think memcpy() would be much
> clearer here.
I've changed the version in rxclass_get_val to use memcpy() (and memset() the
mask).  Unfortunately, we can't do that here, because we need to complement
the mask valueas we go, and afaik there's no library function to copy-and-
complement a byte array.
Glibc does, however, have a function memfrob(), which XORs every byte of an
arraywiththe constant 42.  Useful feature, that.
On the other hand, the quoted code is still wrong because it's also writing
throughopt->offset and checking for opt->moffset>= 0, both daft copy-and-
paste errors onmypart.  Will fix in next version.
> I won't apply patches labelled as "confidential".  You need to stop
> including this nonsense in your public messages (I thought you fixed
> this once before).
In theory it's been fixed harder now - please let me know if not.

-Ed
Ben Hutchings March 16, 2016, 5:15 p.m. UTC | #3
On Wed, 2016-03-16 at 14:53 +0000, Edward Cree wrote:
> On 13/03/16 16:43, Ben Hutchings wrote:
> > 
> > On Mon, 2016-02-15 at 14:59 +0000, Edward Cree wrote:
> > > 
> > > Signed-off-by: Edward Cree <ecree@solarflare.com>
> > [...]
> > > 
> > > @@ -950,6 +1154,19 @@ static int rxclass_get_mask(char *str, unsigned char *p,
> > >                 *(__be32 *)&p[opt->moffset] = ~val;
> > >                 break;
> > >         }
> > > +       case OPT_IP6: {
> > > +               __be32 val[4];
> > > +               int i;
> > > +               err = rxclass_get_ipv6(str, val);
> > > +               if (err)
> > > +                       return -1;
> > > +               for (i = 0; i < 4; i++) {
> > > +                       ((__be32 *)&p[opt->offset])[i] = val[i];
> > > +                       if (opt->moffset >= 0)
> > > +                               ((__be32 *)&p[opt->moffset])[i] = ~val[i];
> > This pointer arithmetic looks terrible.  I think memcpy() would be much
> > clearer here.
> I've changed the version in rxclass_get_val to use memcpy() (and memset() the
> mask).  Unfortunately, we can't do that here, because we need to complement
> the mask valueas we go, and afaik there's no library function to copy-and-
> complement a byte array.

Sorry, I missed the inversion.  But it would still be cleaner to use a
local variable:

			__be32 *field = (__be32 *)&p[opt->offset];

> Glibc does, however, have a function memfrob(), which XORs every byte of an
> arraywiththe constant 42.  Useful feature, that.

It is!

#include <string.h>

int main(void)
{
    char answer;
    memset(&answer, 0, 1);
    memfrob(&answer, 1);
    return answer;
}

> On the other hand, the quoted code is still wrong because it's also writing
> throughopt->offset and checking for opt->moffset>= 0, both daft copy-and-
> paste errors onmypart.  Will fix in next version.
> > 
> > I won't apply patches labelled as "confidential".  You need to stop
> > including this nonsense in your public messages (I thought you fixed
> > this once before).
> In theory it's been fixed harder now - please let me know if not.

Looks like you're sending two copies, one with and one without.  Which
works for me, though it might annoy some recipients...

Ben.
diff mbox

Patch

diff --git a/ethtool.c b/ethtool.c
index 92c40b8..f18ad73 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -32,6 +32,7 @@ 
 #include <sys/stat.h>
 #include <stdio.h>
 #include <stddef.h>
+#include <stdbool.h>
 #include <errno.h>
 #include <sys/utsname.h>
 #include <limits.h>
@@ -3492,6 +3493,22 @@  static int do_permaddr(struct cmd_context *ctx)
 	return err;
 }
 
+static bool flow_type_is_ntuple_supported(__u32 flow_type)
+{
+	switch (flow_type) {
+	case TCP_V4_FLOW:
+	case UDP_V4_FLOW:
+	case SCTP_V4_FLOW:
+	case AH_V4_FLOW:
+	case ESP_V4_FLOW:
+	case IPV4_USER_FLOW:
+	case ETHER_FLOW:
+		return true;
+	default:
+		return false;
+	}
+}
+
 static int flow_spec_to_ntuple(struct ethtool_rx_flow_spec *fsp,
 			       struct ethtool_rx_ntuple_flow_spec *ntuple)
 {
@@ -3515,6 +3532,10 @@  static int flow_spec_to_ntuple(struct ethtool_rx_flow_spec *fsp,
 	    fsp->m_ext.vlan_etype)
 		return -1;
 
+	/* IPv6 flow types are not supported by ntuple */
+	if (!flow_type_is_ntuple_supported(fsp->flow_type & ~FLOW_EXT))
+		return -1;
+
 	/* Set entire ntuple to ~0 to guarantee all masks are set */
 	memset(ntuple, ~0, sizeof(*ntuple));
 
diff --git a/rxclass.c b/rxclass.c
index cd686a3..d3150d5 100644
--- a/rxclass.c
+++ b/rxclass.c
@@ -39,6 +39,25 @@  static void rxclass_print_ipv4_rule(__be32 sip, __be32 sipm, __be32 dip,
 		tos, tosm);
 }
 
+static void rxclass_print_ipv6_rule(__be32 *sip, __be32 *sipm, __be32 *dip,
+				    __be32 *dipm, u8 tclass, u8 tclassm)
+{
+	char sip_str[INET6_ADDRSTRLEN];
+	char sipm_str[INET6_ADDRSTRLEN];
+	char dip_str[INET6_ADDRSTRLEN];
+	char dipm_str[INET6_ADDRSTRLEN];
+
+	fprintf(stdout,
+		"\tSrc IP addr: %s mask: %s\n"
+		"\tDest IP addr: %s mask: %s\n"
+		"\tTraffic Class: 0x%x mask: 0x%x\n",
+		inet_ntop(AF_INET6, sip, sip_str, INET6_ADDRSTRLEN),
+		inet_ntop(AF_INET6, sipm, sipm_str, INET6_ADDRSTRLEN),
+		inet_ntop(AF_INET6, dip, dip_str, INET6_ADDRSTRLEN),
+		inet_ntop(AF_INET6, dipm, dipm_str, INET6_ADDRSTRLEN),
+		tclass, tclassm);
+}
+
 static void rxclass_print_nfc_spec_ext(struct ethtool_rx_flow_spec *fsp)
 {
 	if (fsp->flow_type & FLOW_EXT) {
@@ -127,7 +146,7 @@  static void rxclass_print_nfc_rule(struct ethtool_rx_flow_spec *fsp)
 			ntohl(fsp->h_u.esp_ip4_spec.spi),
 			ntohl(fsp->m_u.esp_ip4_spec.spi));
 		break;
-	case IP_USER_FLOW:
+	case IPV4_USER_FLOW:
 		fprintf(stdout, "\tRule Type: Raw IPv4\n");
 		rxclass_print_ipv4_rule(fsp->h_u.usr_ip4_spec.ip4src,
 				     fsp->m_u.usr_ip4_spec.ip4src,
@@ -143,6 +162,62 @@  static void rxclass_print_nfc_rule(struct ethtool_rx_flow_spec *fsp)
 			ntohl(fsp->h_u.usr_ip4_spec.l4_4_bytes),
 			ntohl(fsp->m_u.usr_ip4_spec.l4_4_bytes));
 		break;
+	case TCP_V6_FLOW:
+	case UDP_V6_FLOW:
+	case SCTP_V6_FLOW:
+		if (flow_type == TCP_V6_FLOW)
+			fprintf(stdout, "\tRule Type: TCP over IPv6\n");
+		else if (flow_type == UDP_V6_FLOW)
+			fprintf(stdout, "\tRule Type: UDP over IPv6\n");
+		else
+			fprintf(stdout, "\tRule Type: SCTP over IPv6\n");
+		rxclass_print_ipv6_rule(fsp->h_u.tcp_ip6_spec.ip6src,
+				     fsp->m_u.tcp_ip6_spec.ip6src,
+				     fsp->h_u.tcp_ip6_spec.ip6dst,
+				     fsp->m_u.tcp_ip6_spec.ip6dst,
+				     fsp->h_u.tcp_ip6_spec.tclass,
+				     fsp->m_u.tcp_ip6_spec.tclass);
+		fprintf(stdout,
+			"\tSrc port: %d mask: 0x%x\n"
+			"\tDest port: %d mask: 0x%x\n",
+			ntohs(fsp->h_u.tcp_ip6_spec.psrc),
+			ntohs(fsp->m_u.tcp_ip6_spec.psrc),
+			ntohs(fsp->h_u.tcp_ip6_spec.pdst),
+			ntohs(fsp->m_u.tcp_ip6_spec.pdst));
+		break;
+	case AH_V6_FLOW:
+	case ESP_V6_FLOW:
+		if (flow_type == AH_V6_FLOW)
+			fprintf(stdout, "\tRule Type: IPSEC AH over IPv6\n");
+		else
+			fprintf(stdout, "\tRule Type: IPSEC ESP over IPv6\n");
+		rxclass_print_ipv6_rule(fsp->h_u.ah_ip6_spec.ip6src,
+				     fsp->m_u.ah_ip6_spec.ip6src,
+				     fsp->h_u.ah_ip6_spec.ip6dst,
+				     fsp->m_u.ah_ip6_spec.ip6dst,
+				     fsp->h_u.ah_ip6_spec.tclass,
+				     fsp->m_u.ah_ip6_spec.tclass);
+		fprintf(stdout,
+			"\tSPI: %d mask: 0x%x\n",
+			ntohl(fsp->h_u.esp_ip6_spec.spi),
+			ntohl(fsp->m_u.esp_ip6_spec.spi));
+		break;
+	case IPV6_USER_FLOW:
+		fprintf(stdout, "\tRule Type: Raw IPv6\n");
+		rxclass_print_ipv6_rule(fsp->h_u.usr_ip6_spec.ip6src,
+				     fsp->m_u.usr_ip6_spec.ip6src,
+				     fsp->h_u.usr_ip6_spec.ip6dst,
+				     fsp->m_u.usr_ip6_spec.ip6dst,
+				     fsp->h_u.usr_ip6_spec.tclass,
+				     fsp->m_u.usr_ip6_spec.tclass);
+		fprintf(stdout,
+			"\tProtocol: %d mask: 0x%x\n"
+			"\tL4 bytes: 0x%x mask: 0x%x\n",
+			fsp->h_u.usr_ip6_spec.l4_proto,
+			fsp->m_u.usr_ip6_spec.l4_proto,
+			ntohl(fsp->h_u.usr_ip6_spec.l4_4_bytes),
+			ntohl(fsp->m_u.usr_ip6_spec.l4_4_bytes));
+		break;
 	case ETHER_FLOW:
 		dmac = fsp->h_u.ether_spec.h_dest;
 		dmacm = fsp->m_u.ether_spec.h_dest;
@@ -190,21 +265,20 @@  static void rxclass_print_rule(struct ethtool_rx_flow_spec *fsp)
 	case SCTP_V4_FLOW:
 	case AH_V4_FLOW:
 	case ESP_V4_FLOW:
-	case ETHER_FLOW:
-		rxclass_print_nfc_rule(fsp);
-		break;
-	case IP_USER_FLOW:
-		if (fsp->h_u.usr_ip4_spec.ip_ver == ETH_RX_NFC_IP4) {
-			rxclass_print_nfc_rule(fsp);
-			break;
-		}
-		/* IPv6 User Flow falls through to the case below */
 	case TCP_V6_FLOW:
 	case UDP_V6_FLOW:
 	case SCTP_V6_FLOW:
 	case AH_V6_FLOW:
 	case ESP_V6_FLOW:
-		fprintf(stderr, "IPv6 flows not implemented\n");
+	case IPV6_USER_FLOW:
+	case ETHER_FLOW:
+		rxclass_print_nfc_rule(fsp);
+		break;
+	case IPV4_USER_FLOW:
+		if (fsp->h_u.usr_ip4_spec.ip_ver == ETH_RX_NFC_IP4)
+			rxclass_print_nfc_rule(fsp);
+		else /* IPv6 uses IPV6_USER_FLOW */
+			fprintf(stderr, "IPV4_USER_FLOW with wrong ip_ver\n");
 		break;
 	default:
 		fprintf(stderr, "rxclass: Unknown flow type\n");
@@ -530,6 +604,7 @@  typedef enum {
 	OPT_BE32,
 	OPT_BE64,
 	OPT_IP4,
+	OPT_IP6,
 	OPT_MAC,
 } rule_opt_type_t;
 
@@ -663,6 +738,114 @@  static const struct rule_opts rule_nfc_usr_ip4[] = {
 	  offsetof(struct ethtool_rx_flow_spec, m_ext.h_dest) },
 };
 
+static const struct rule_opts rule_nfc_tcp_ip6[] = {
+	{ "src-ip", OPT_IP6, NFC_FLAG_SADDR,
+	  offsetof(struct ethtool_rx_flow_spec, h_u.tcp_ip6_spec.ip6src),
+	  offsetof(struct ethtool_rx_flow_spec, m_u.tcp_ip6_spec.ip6src) },
+	{ "dst-ip", OPT_IP6, NFC_FLAG_DADDR,
+	  offsetof(struct ethtool_rx_flow_spec, h_u.tcp_ip6_spec.ip6dst),
+	  offsetof(struct ethtool_rx_flow_spec, m_u.tcp_ip6_spec.ip6dst) },
+	{ "tclass", OPT_U8, NFC_FLAG_TOS,
+	  offsetof(struct ethtool_rx_flow_spec, h_u.tcp_ip6_spec.tclass),
+	  offsetof(struct ethtool_rx_flow_spec, m_u.tcp_ip6_spec.tclass) },
+	{ "src-port", OPT_BE16, NFC_FLAG_SPORT,
+	  offsetof(struct ethtool_rx_flow_spec, h_u.tcp_ip6_spec.psrc),
+	  offsetof(struct ethtool_rx_flow_spec, m_u.tcp_ip6_spec.psrc) },
+	{ "dst-port", OPT_BE16, NFC_FLAG_DPORT,
+	  offsetof(struct ethtool_rx_flow_spec, h_u.tcp_ip6_spec.pdst),
+	  offsetof(struct ethtool_rx_flow_spec, m_u.tcp_ip6_spec.pdst) },
+	{ "action", OPT_U64, NFC_FLAG_RING,
+	  offsetof(struct ethtool_rx_flow_spec, ring_cookie), -1 },
+	{ "loc", OPT_U32, NFC_FLAG_LOC,
+	  offsetof(struct ethtool_rx_flow_spec, location), -1 },
+	{ "vlan-etype", OPT_BE16, NTUPLE_FLAG_VETH,
+	  offsetof(struct ethtool_rx_flow_spec, h_ext.vlan_etype),
+	  offsetof(struct ethtool_rx_flow_spec, m_ext.vlan_etype) },
+	{ "vlan", OPT_BE16, NTUPLE_FLAG_VLAN,
+	  offsetof(struct ethtool_rx_flow_spec, h_ext.vlan_tci),
+	  offsetof(struct ethtool_rx_flow_spec, m_ext.vlan_tci) },
+	{ "user-def", OPT_BE64, NTUPLE_FLAG_UDEF,
+	  offsetof(struct ethtool_rx_flow_spec, h_ext.data),
+	  offsetof(struct ethtool_rx_flow_spec, m_ext.data) },
+	{ "dst-mac", OPT_MAC, NFC_FLAG_MAC_ADDR,
+	  offsetof(struct ethtool_rx_flow_spec, h_ext.h_dest),
+	  offsetof(struct ethtool_rx_flow_spec, m_ext.h_dest) },
+};
+
+static const struct rule_opts rule_nfc_esp_ip6[] = {
+	{ "src-ip", OPT_IP6, NFC_FLAG_SADDR,
+	  offsetof(struct ethtool_rx_flow_spec, h_u.esp_ip6_spec.ip6src),
+	  offsetof(struct ethtool_rx_flow_spec, m_u.esp_ip6_spec.ip6src) },
+	{ "dst-ip", OPT_IP6, NFC_FLAG_DADDR,
+	  offsetof(struct ethtool_rx_flow_spec, h_u.esp_ip6_spec.ip6dst),
+	  offsetof(struct ethtool_rx_flow_spec, m_u.esp_ip6_spec.ip6dst) },
+	{ "tclass", OPT_U8, NFC_FLAG_TOS,
+	  offsetof(struct ethtool_rx_flow_spec, h_u.esp_ip6_spec.tclass),
+	  offsetof(struct ethtool_rx_flow_spec, m_u.esp_ip6_spec.tclass) },
+	{ "spi", OPT_BE32, NFC_FLAG_SPI,
+	  offsetof(struct ethtool_rx_flow_spec, h_u.esp_ip6_spec.spi),
+	  offsetof(struct ethtool_rx_flow_spec, m_u.esp_ip6_spec.spi) },
+	{ "action", OPT_U64, NFC_FLAG_RING,
+	  offsetof(struct ethtool_rx_flow_spec, ring_cookie), -1 },
+	{ "loc", OPT_U32, NFC_FLAG_LOC,
+	  offsetof(struct ethtool_rx_flow_spec, location), -1 },
+	{ "vlan-etype", OPT_BE16, NTUPLE_FLAG_VETH,
+	  offsetof(struct ethtool_rx_flow_spec, h_ext.vlan_etype),
+	  offsetof(struct ethtool_rx_flow_spec, m_ext.vlan_etype) },
+	{ "vlan", OPT_BE16, NTUPLE_FLAG_VLAN,
+	  offsetof(struct ethtool_rx_flow_spec, h_ext.vlan_tci),
+	  offsetof(struct ethtool_rx_flow_spec, m_ext.vlan_tci) },
+	{ "user-def", OPT_BE64, NTUPLE_FLAG_UDEF,
+	  offsetof(struct ethtool_rx_flow_spec, h_ext.data),
+	  offsetof(struct ethtool_rx_flow_spec, m_ext.data) },
+	{ "dst-mac", OPT_MAC, NFC_FLAG_MAC_ADDR,
+	  offsetof(struct ethtool_rx_flow_spec, h_ext.h_dest),
+	  offsetof(struct ethtool_rx_flow_spec, m_ext.h_dest) },
+};
+
+static const struct rule_opts rule_nfc_usr_ip6[] = {
+	{ "src-ip", OPT_IP6, NFC_FLAG_SADDR,
+	  offsetof(struct ethtool_rx_flow_spec, h_u.usr_ip6_spec.ip6src),
+	  offsetof(struct ethtool_rx_flow_spec, m_u.usr_ip6_spec.ip6src) },
+	{ "dst-ip", OPT_IP6, NFC_FLAG_DADDR,
+	  offsetof(struct ethtool_rx_flow_spec, h_u.usr_ip6_spec.ip6dst),
+	  offsetof(struct ethtool_rx_flow_spec, m_u.usr_ip6_spec.ip6dst) },
+	{ "tclass", OPT_U8, NFC_FLAG_TOS,
+	  offsetof(struct ethtool_rx_flow_spec, h_u.usr_ip6_spec.tclass),
+	  offsetof(struct ethtool_rx_flow_spec, m_u.usr_ip6_spec.tclass) },
+	{ "l4proto", OPT_U8, NFC_FLAG_PROTO,
+	  offsetof(struct ethtool_rx_flow_spec, h_u.usr_ip6_spec.l4_proto),
+	  offsetof(struct ethtool_rx_flow_spec, m_u.usr_ip6_spec.l4_proto) },
+	{ "l4data", OPT_BE32, NFC_FLAG_SPI,
+	  offsetof(struct ethtool_rx_flow_spec, h_u.usr_ip6_spec.l4_4_bytes),
+	  offsetof(struct ethtool_rx_flow_spec, m_u.usr_ip6_spec.l4_4_bytes) },
+	{ "spi", OPT_BE32, NFC_FLAG_SPI,
+	  offsetof(struct ethtool_rx_flow_spec, h_u.usr_ip6_spec.l4_4_bytes),
+	  offsetof(struct ethtool_rx_flow_spec, m_u.usr_ip6_spec.l4_4_bytes) },
+	{ "src-port", OPT_BE16, NFC_FLAG_SPORT,
+	  offsetof(struct ethtool_rx_flow_spec, h_u.usr_ip6_spec.l4_4_bytes),
+	  offsetof(struct ethtool_rx_flow_spec, m_u.usr_ip6_spec.l4_4_bytes) },
+	{ "dst-port", OPT_BE16, NFC_FLAG_DPORT,
+	  offsetof(struct ethtool_rx_flow_spec, h_u.usr_ip6_spec.l4_4_bytes) + 2,
+	  offsetof(struct ethtool_rx_flow_spec, m_u.usr_ip6_spec.l4_4_bytes) + 2 },
+	{ "action", OPT_U64, NFC_FLAG_RING,
+	  offsetof(struct ethtool_rx_flow_spec, ring_cookie), -1 },
+	{ "loc", OPT_U32, NFC_FLAG_LOC,
+	  offsetof(struct ethtool_rx_flow_spec, location), -1 },
+	{ "vlan-etype", OPT_BE16, NTUPLE_FLAG_VETH,
+	  offsetof(struct ethtool_rx_flow_spec, h_ext.vlan_etype),
+	  offsetof(struct ethtool_rx_flow_spec, m_ext.vlan_etype) },
+	{ "vlan", OPT_BE16, NTUPLE_FLAG_VLAN,
+	  offsetof(struct ethtool_rx_flow_spec, h_ext.vlan_tci),
+	  offsetof(struct ethtool_rx_flow_spec, m_ext.vlan_tci) },
+	{ "user-def", OPT_BE64, NTUPLE_FLAG_UDEF,
+	  offsetof(struct ethtool_rx_flow_spec, h_ext.data),
+	  offsetof(struct ethtool_rx_flow_spec, m_ext.data) },
+	{ "dst-mac", OPT_MAC, NFC_FLAG_MAC_ADDR,
+	  offsetof(struct ethtool_rx_flow_spec, h_ext.h_dest),
+	  offsetof(struct ethtool_rx_flow_spec, m_ext.h_dest) },
+};
+
 static const struct rule_opts rule_nfc_ether[] = {
 	{ "src", OPT_MAC, NFC_FLAG_SADDR,
 	  offsetof(struct ethtool_rx_flow_spec, h_u.ether_spec.h_source),
@@ -726,6 +909,14 @@  static int rxclass_get_ipv4(char *str, __be32 *val)
 	return 0;
 }
 
+static int rxclass_get_ipv6(char *str, __be32 *val)
+{
+	if (!inet_pton(AF_INET6, str, val))
+		return -1;
+
+	return 0;
+}
+
 static int rxclass_get_ether(char *str, unsigned char *val)
 {
 	unsigned int buf[ETH_ALEN];
@@ -851,6 +1042,19 @@  static int rxclass_get_val(char *str, unsigned char *p, u32 *flags,
 			*(__be32 *)&p[opt->moffset] = (__be32)mask;
 		break;
 	}
+	case OPT_IP6: {
+		__be32 val[4];
+		int i;
+		err = rxclass_get_ipv6(str, val);
+		if (err)
+			return -1;
+		for (i = 0; i < 4; i++) {
+			((__be32 *)&p[opt->offset])[i] = val[i];
+			if (opt->moffset >= 0)
+				((__be32 *)&p[opt->moffset])[i] = (__be32)mask;
+		}
+		break;
+	}
 	case OPT_MAC: {
 		unsigned char val[ETH_ALEN];
 		err = rxclass_get_ether(str, val);
@@ -950,6 +1154,19 @@  static int rxclass_get_mask(char *str, unsigned char *p,
 		*(__be32 *)&p[opt->moffset] = ~val;
 		break;
 	}
+	case OPT_IP6: {
+		__be32 val[4];
+		int i;
+		err = rxclass_get_ipv6(str, val);
+		if (err)
+			return -1;
+		for (i = 0; i < 4; i++) {
+			((__be32 *)&p[opt->offset])[i] = val[i];
+			if (opt->moffset >= 0)
+				((__be32 *)&p[opt->moffset])[i] = ~val[i];
+		}
+		break;
+	}
 	case OPT_MAC: {
 		unsigned char val[ETH_ALEN];
 		int i;
@@ -996,7 +1213,19 @@  int rxclass_parse_ruleopts(struct cmd_context *ctx,
 	else if (!strcmp(argp[0], "esp4"))
 		flow_type = ESP_V4_FLOW;
 	else if (!strcmp(argp[0], "ip4"))
-		flow_type = IP_USER_FLOW;
+		flow_type = IPV4_USER_FLOW;
+	else if (!strcmp(argp[0], "tcp6"))
+		flow_type = TCP_V6_FLOW;
+	else if (!strcmp(argp[0], "udp6"))
+		flow_type = UDP_V6_FLOW;
+	else if (!strcmp(argp[0], "sctp6"))
+		flow_type = SCTP_V6_FLOW;
+	else if (!strcmp(argp[0], "ah6"))
+		flow_type = AH_V6_FLOW;
+	else if (!strcmp(argp[0], "esp6"))
+		flow_type = ESP_V6_FLOW;
+	else if (!strcmp(argp[0], "ip6"))
+		flow_type = IPV6_USER_FLOW;
 	else if (!strcmp(argp[0], "ether"))
 		flow_type = ETHER_FLOW;
 	else
@@ -1014,10 +1243,25 @@  int rxclass_parse_ruleopts(struct cmd_context *ctx,
 		options = rule_nfc_esp_ip4;
 		n_opts = ARRAY_SIZE(rule_nfc_esp_ip4);
 		break;
-	case IP_USER_FLOW:
+	case IPV4_USER_FLOW:
 		options = rule_nfc_usr_ip4;
 		n_opts = ARRAY_SIZE(rule_nfc_usr_ip4);
 		break;
+	case TCP_V6_FLOW:
+	case UDP_V6_FLOW:
+	case SCTP_V6_FLOW:
+		options = rule_nfc_tcp_ip6;
+		n_opts = ARRAY_SIZE(rule_nfc_tcp_ip6);
+		break;
+	case AH_V6_FLOW:
+	case ESP_V6_FLOW:
+		options = rule_nfc_esp_ip6;
+		n_opts = ARRAY_SIZE(rule_nfc_esp_ip6);
+		break;
+	case IPV6_USER_FLOW:
+		options = rule_nfc_usr_ip6;
+		n_opts = ARRAY_SIZE(rule_nfc_usr_ip6);
+		break;
 	case ETHER_FLOW:
 		options = rule_nfc_ether;
 		n_opts = ARRAY_SIZE(rule_nfc_ether);
@@ -1081,7 +1325,7 @@  int rxclass_parse_ruleopts(struct cmd_context *ctx,
 		}
 	}
 
-	if (flow_type == IP_USER_FLOW)
+	if (flow_type == IPV4_USER_FLOW)
 		fsp->h_u.usr_ip4_spec.ip_ver = ETH_RX_NFC_IP4;
 	if (flags & (NTUPLE_FLAG_VLAN | NTUPLE_FLAG_UDEF | NTUPLE_FLAG_VETH))
 		fsp->flow_type |= FLOW_EXT;