diff mbox

[1/3,ethtool] Add support for RX packet classification in a network device

Message ID 4990B855.30905@Sun.COM
State Changes Requested, archived
Delegated to: David Miller
Headers show

Commit Message

Santwona.Behera@Sun.COM Feb. 9, 2009, 11:12 p.m. UTC

Comments

Jeff Garzik Feb. 10, 2009, 12:09 a.m. UTC | #1
Once the ethtool interface change is accepted in the upstream kernel, I 
will be happy to add ethtool(8) support.

I would say, though, adding 1000 lines of code, you might as well put 
that into a new file rxclass.c, to keep all that together in a nice tidy 
unit.

	Jeff




--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
David Miller Feb. 10, 2009, 2:03 a.m. UTC | #2
From: Santwona.Behera@Sun.COM
Date: Mon, 09 Feb 2009 15:12:21 -0800

> +struct ethtool_tcpip4_spec {
> +	__u32	ip4src;
> +	__u32	ip4dst;
> +	__u16	psrc;
> +	__u16	pdst;
> +	__u8    tos;
> +};

The IP addresses and ports in these new interfaces, are
they to be specified in network or cpu byte order?

That's usually the first thing a person will wonder when
trying to use to implement support for these interfaces.

My guess is that the specification is cpu endianness,
however note that this flies in the face with how this
is handled in pretty much every other networking interface
in the kernel.  The standard is to use network endianness
when passing ports and IP addressing information into the
kernel.

I wouldn't reject this patch series just for this reason,
in fact I'm ready to apply this stuff, but just thought I'd
mention it just in case you hadn't considered the issue
yourself.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Santwona.Behera@Sun.COM Feb. 10, 2009, 3:05 a.m. UTC | #3
On 02/ 9/09 04:09 PM, Jeff Garzik wrote:
> Once the ethtool interface change is accepted in the upstream kernel, I 
> will be happy to add ethtool(8) support.
> 
> I would say, though, adding 1000 lines of code, you might as well put 
> that into a new file rxclass.c, to keep all that together in a nice tidy 
> unit.

I agree. I will redo the ethtool(8) patch as recommended.

thanks,
--santwona

> 
>     Jeff
> 
> 
> 
> 
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Santwona.Behera@Sun.COM Feb. 10, 2009, 3:17 a.m. UTC | #4
On 02/ 9/09 06:03 PM, David Miller wrote:
> From: Santwona.Behera@Sun.COM
> Date: Mon, 09 Feb 2009 15:12:21 -0800
> 
>> +struct ethtool_tcpip4_spec {
>> +	__u32	ip4src;
>> +	__u32	ip4dst;
>> +	__u16	psrc;
>> +	__u16	pdst;
>> +	__u8    tos;
>> +};
> 
> The IP addresses and ports in these new interfaces, are
> they to be specified in network or cpu byte order?

The interfaces assumes that they are to be specified in network order. I 
will add a comment to mention that.

Looking back at my ethtool(8) patch, I have a bug in the code where I do 
not do this translation for the ports and spi (I got it right for the IP 
addresses). I will fix this and repost all the patches.

Thanks for raising this.

--santwona

> 
> That's usually the first thing a person will wonder when
> trying to use to implement support for these interfaces.
> 
> My guess is that the specification is cpu endianness,
> however note that this flies in the face with how this
> is handled in pretty much every other networking interface
> in the kernel.  The standard is to use network endianness
> when passing ports and IP addressing information into the
> kernel.
> 
> I wouldn't reject this patch series just for this reason,
> in fact I'm ready to apply this stuff, but just thought I'd
> mention it just in case you hadn't considered the issue
> yourself.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
David Miller Feb. 10, 2009, 3:44 a.m. UTC | #5
From: Santwona.Behera@Sun.COM
Date: Mon, 09 Feb 2009 19:17:09 -0800

> The interfaces assumes that they are to be specified in network
> order. I will add a comment to mention that.
> 
> Looking back at my ethtool(8) patch, I have a bug in the code where
> I do not do this translation for the ports and spi (I got it right
> for the IP addresses). I will fix this and repost all the patches.

Thank you.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

From: Santwona Behera <santwona.behera@sun.com>
[PATCH 1/3] [ethtool] Add RX pkt classification interface

Signed-off-by: Santwona Behera <santwona.behera@sun.com>
---
 ethtool-copy.h |   82 ++++-
 ethtool.8      |  138 +++++++-
 ethtool.c      | 1070 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 1260 insertions(+), 30 deletions(-)

diff --git a/ethtool-copy.h b/ethtool-copy.h
index eadba25..8c61f3e 100644
--- a/ethtool-copy.h
+++ b/ethtool-copy.h
@@ -12,6 +12,8 @@ 
 #ifndef _LINUX_ETHTOOL_H
 #define _LINUX_ETHTOOL_H
 
+#include <linux/types.h>
+
 /* This should work for both 32 and 64 bit userland. */
 struct ethtool_cmd {
 	__u32	cmd;
@@ -285,10 +287,71 @@  enum ethtool_flags {
 	ETH_FLAG_LRO		= (1 << 15),	/* LRO is enabled */
 };
 
-struct ethtool_rxnfc {
-	__u32		cmd;
+struct ethtool_tcpip4_spec {
+	__u32	ip4src;
+	__u32	ip4dst;
+	__u16	psrc;
+	__u16	pdst;
+	__u8    tos;
+};
+
+struct ethtool_ah_espip4_spec {
+	__u32	ip4src;
+	__u32	ip4dst;
+	__u32	spi;
+	__u8    tos;
+};
+
+struct ethtool_rawip4_spec {
+	__u32	ip4src;
+	__u32	ip4dst;
+	__u8	hdata[64];
+};
+
+struct ethtool_ether_spec {
+	__u16	ether_type;
+	__u8	frame_size;
+	__u8	eframe[16];
+};
+
+#define	ETH_RX_NFC_IP4	1
+#define	ETH_RX_NFC_IP6	2
+
+struct ethtool_usrip4_spec {
+	__u32	ip4src;
+	__u32	ip4dst;
+	__u32	l4_4_bytes;
+	__u8    tos;
+	__u8    ip_ver;
+	__u8    proto;
+};
+
+struct ethtool_rx_flow_spec {
 	__u32		flow_type;
-	__u64		data;
+	union
+	{
+		struct ethtool_tcpip4_spec		tcp_ip4_spec;
+		struct ethtool_tcpip4_spec		udp_ip4_spec;
+		struct ethtool_tcpip4_spec		sctp_ip4_spec;
+		struct ethtool_ah_espip4_spec		ah_ip4_spec;
+		struct ethtool_ah_espip4_spec		esp_ip4_spec;
+		struct ethtool_rawip4_spec		raw_ip4_spec;
+		struct ethtool_ether_spec		ether_spec;
+		struct ethtool_usrip4_spec		usr_ip4_spec;
+		__u8					hdata[64];
+	} h_u, m_u; /* entry, mask */
+	__u64		ring_cookie;
+	__u32		location;
+};
+
+struct ethtool_rxnfc {
+	__u32				cmd;
+	__u32				flow_type;
+	/* The rx flow hash value or the rule DB size or the # rx rings */
+	__u64				data;
+	struct ethtool_rx_flow_spec	fs;
+	__u32				rule_cnt;
+	__u32				rule_locs[0];
 };
 
 /* CMDs currently supported */
@@ -336,6 +399,12 @@  struct ethtool_rxnfc {
 
 #define	ETHTOOL_GRXFH		0x00000029 /* Get RX flow hash configuration */
 #define	ETHTOOL_SRXFH		0x0000002a /* Set RX flow hash configuration */
+#define	ETHTOOL_GRXRINGS	0x0000002d /* Get RX rings available for LB */
+#define	ETHTOOL_GRXCLSRLCNT	0x0000002e /* Get RX class rule count */
+#define	ETHTOOL_GRXCLSRULE	0x0000002f /* Get RX classification rule */
+#define	ETHTOOL_GRXCLSRLALL	0x00000030 /* Get all RX classification rule */
+#define	ETHTOOL_SRXCLSRLDEL	0x00000031 /* Delete RX classification rule */
+#define	ETHTOOL_SRXCLSRLINS	0x00000032 /* Insert RX classification rule */
 
 /* compatibility with older code */
 #define SPARC_ETH_GSET		ETHTOOL_GSET
@@ -432,9 +501,13 @@  struct ethtool_rxnfc {
 #define	UDP_V6_FLOW	0x06
 #define	SCTP_V6_FLOW	0x07
 #define	AH_ESP_V6_FLOW	0x08
+#define	AH_V4_FLOW	0x09
+#define	ESP_V4_FLOW	0x0a
+#define	AH_V6_FLOW	0x0b
+#define	ESP_V6_FLOW	0x0c
+#define	IP_USER_FLOW	0x0d
 
 /* L3-L4 network traffic flow hash options */
-#define	RXH_DEV_PORT	(1 << 0)
 #define	RXH_L2DA	(1 << 1)
 #define	RXH_VLAN	(1 << 2)
 #define	RXH_L3_PROTO	(1 << 3)
@@ -444,5 +517,6 @@  struct ethtool_rxnfc {
 #define	RXH_L4_B_2_3	(1 << 7) /* dst port in case of TCP/UDP/SCTP */
 #define	RXH_DISCARD	(1 << 31)
 
+#define	RX_CLS_FLOW_DISC	0xffffffffffffffffULL
 
 #endif /* _LINUX_ETHTOOL_H */
diff --git a/ethtool.8 b/ethtool.8
index 1beb387..e8a843d 100644
--- a/ethtool.8
+++ b/ethtool.8
@@ -37,17 +37,30 @@ 
 .\"
 .ds MA \fIxx\fP\fB:\fP\fIyy\fP\fB:\fP\fIzz\fP\fB:\fP\fIaa\fP\fB:\fP\fIbb\fP\fB:\fP\fIcc\fP
 .\"
+.\"	\(*PA - IP address
+.\"
+.ds PA \fIN\fP\fB.\fP\fIN\fP\fB.\fP\fIN\fP\fB.\fP\fIN\fP
+.\"
 .\"	\(*WO - wol flags
 .\"
 .ds WO \fBp\fP|\fBu\fP|\fBm\fP|\fBb\fP|\fBa\fP|\fBg\fP|\fBs\fP|\fBd\fP...
 .\"
 .\"	\(*FL - flow type values
 .\"
-.ds FL \fBtcp4\fP|\fBudp4\fP|\fBah4\fP|\fBsctp4\fP|\fBtcp6\fP|\fBudp6\fP|\fBah6\fP|\fBsctp6\fP
+.ds FL \fBtcp4\fP|\fBudp4\fP|\fBah4\fP||\fBesp4\fP|\fBsctp4\fP|\fBtcp6\fP|\fBudp6\fP|\fBah6\fP|\fBesp6\fP|\fBsctp6\fP
 .\"
 .\"	\(*HO - hash options
 .\"
-.ds HO \fBp\fP|\fm\fP|\fBv\fP|\fBt\fP|\fBs\fP|\fBd\fP|\fBf\fP|\fBn\fP|\fBr\fP...
+.ds HO \fBm\fP|\fBv\fP|\fBt\fP|\fBs\fP|\fBd\fP|\fBf\fP|\fBn\fP|\fBr\fP...
+.\"
+.\"	\(*PV - ipversion options
+.\"
+.ds PV \fBip4\fP|\fBip6\fP
+.\"
+.\"	\(*L4 - L4 proto options
+.\"
+.ds L4 \fBtcp\fP|\fBudp\fP|\fBsctp\fP|\fBah\fP|\fBesp\fP|\fIN\fP
+
 .TH ETHTOOL 8 "July 2007" "Ethtool version 6"
 .SH NAME
 ethtool \- Display or change ethernet card settings
@@ -200,11 +213,53 @@  ethtool \- Display or change ethernet card settings
 .B ethtool \-n
 .I ethX
 .RB [ rx-flow-hash \ \*(FL]
+.RB [ rx-rings ]
+.RB [ rx-class-rule-all ]
+.RB [ rx-class-rule
+.IR N ]
 
 .B ethtool \-N
 .I ethX
 .RB [ rx-flow-hash \ \*(FL
 .RB \ \*(HO]
+.RB [ rx-class-rule-del
+.IR N ]
+.RB [ rx-class-rule-add \ \*(PV
+.RB \ \*(L4
+.RB [ tos
+.IR N
+.RB [ m
+.IR N ]
+.RB ]
+.RB [ sip \ \*(PA
+.RB [ m \ \*(PA ]
+.RB ]
+.RB [ dip \ \*(PA
+.RB [ m \ \*(PA ]
+.RB ]
+.RB [ sport
+.IR N
+.RB [ m
+.IR N ]
+.RB ]
+.RB [ dport
+.IR N
+.RB [ m
+.IR N ]
+.RB ]
+.RB [ spi
+.IR N
+.RB [ m
+.IR N ]
+.RB ]
+.RB [ ring
+.IR N |
+.B drop
+.RB ]
+.RB [ loc
+.IR N ]
+.RB ]
+
 .SH DESCRIPTION
 .BI ethtool
 is used for querying settings of an ethernet device and changing them.
@@ -457,6 +512,15 @@  Retrieves the hash options for the specified network traffic type.
 .PD
 .RE
 .TP
+.B rx-rings
+Retrieves the number of RX rings available for this interface.
+.TP
+.B rx-class-rule-all
+Retrieves all the RX classification rules programmed for this interface.
+.TP
+.BI rx-class-rule \ N
+Retrieves the RX classification rule with the given ID.
+.TP
 .B \-N \-\-config-nfc
 Configures the receive network flow classification.
 .TP
@@ -465,9 +529,6 @@  Configures the hash options for the specified network traffic type.
 .RS
 .PD 0
 .TP 3
-.B p
-Hash on the device port number on which the packet was received.
-.TP 3
 .B m
 Hash on the Layer 2 destination address of the rx packet.
 .TP 3
@@ -490,9 +551,74 @@  Hash on bytes 0 and 1 of the Layer 4 header of the rx packet.
 Hash on bytes 2 and 3 of the Layer 4 header of the rx packet.
 .TP 3
 .B r
-Discard all packets of this flow type. When this option is set, all other options are ignored.
+Discard all packets of this flow type. When this option is set, all
+other options are ignored.
 .PD
 .RE
+.TP
+.BI rx-class-rule-del \ N
+Deletes the RX classification rule with the given ID.
+.TP
+.BR rx-class-rule-add
+Adds an RX packet classification rule.
+.TP
+.B \*(PV
+Select IP version for the rule - IPv4 or IPv6
+.TP
+.B \*(L4
+Select the L4 protocol for the rule. An integer value for a user defined
+protocol can be specified.
+.TP
+.BI tos \ N
+.B [
+.BI m \ N
+.BR ]
+Specify the value of the Type of Service field in the incoming packet to
+match along with an optional mask.
+.TP
+.BR sip \ \*(PA \ [ \  m \ \*(PA \ ]
+Specify the source IP address of the incoming packet to
+match along with an optional mask.
+.TP
+.BR dip \ \*(PA \ [ \  m \ \*(PA \ ]
+Specify the destination IP address of the incoming packet to
+match along with an optional mask.
+.TP
+.BI sport \ N
+.B [
+.BI m \ N
+.BR ]
+Specify the value of the source port field (applicable to
+TCP/UDP packets)in the incoming packet to match along with an
+optional mask.
+.TP
+.BI dport \ N
+.B [
+.BI m \ N
+.BR ]
+Specify the value of the destination port field (applicable to
+TCP/UDP packets)in the incoming packet to match along with an
+optional mask.
+.TP
+.BI spi \ N
+.B [
+.BI m \ N
+.BR ]
+Specify the value of the SPI field (applicable to
+SCTP packets)in the incoming packet to match along with an
+optional mask.
+.TP
+.BI ring \ N
+.B |
+.BR drop
+Specify the RX ring index to which a packet matching this
+rule should be steered, or specify if the matching packet
+should be dropped.
+.TP
+.BI loc \ \ \ N
+Specify the location/ID to insert the rule. This will overwrite
+any rule present in that location and will not go through any
+of the rule ordering process.
 .SH BUGS
 Not supported (in part or whole) on all ethernet drivers.
 .SH AUTHOR
diff --git a/ethtool.c b/ethtool.c
index a7c02d0..d4050e9 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -6,6 +6,7 @@ 
  * Kernel 2.4 update Copyright 2001 Jeff Garzik <jgarzik@mandrakesoft.com>
  * Wake-on-LAN,natsemi,misc support by Tim Hockin <thockin@sun.com>
  * Portions Copyright 2002 Intel
+ * Portions Copyright (c) Sun Microsystems 2009
  * do_test support by Eli Kupermann <eli.kupermann@intel.com>
  * ETHTOOL_PHYS_ID support by Chris Leech <christopher.leech@intel.com>
  * e1000 support by Scott Feldman <scott.feldman@intel.com>
@@ -14,6 +15,7 @@ 
  * amd8111e support by Reeja John <reeja.john@amd.com>
  * long arguments by Andi Kleen.
  * SMSC LAN911x support by Steve Glendinning <steve.glendinning@smsc.com>
+ * Rx Network Flow Control configuration support <santwona.behera@sun.com>
  *
  * TODO:
  *   * no-args => summary of each device (mii-tool style)
@@ -34,11 +36,15 @@ 
 #include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <stdio.h>
-#include <string.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <strings.h>
 #include <errno.h>
 #include <net/if.h>
 
 #include <linux/sockios.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
 #include "ethtool-util.h"
 
 #ifndef SIOCETHTOOL
@@ -72,9 +78,21 @@  static int do_gstats(int fd, struct ifreq *ifr);
 static int rxflow_str_to_type(const char *str);
 static int parse_rxfhashopts(char *optstr, u32 *data);
 static char *unparse_rxfhashopts(u64 opts);
+static int parse_rxclassruleopts(char **optstr, int opt_cnt,
+				 struct ethtool_rx_flow_spec *fsp);
 static int dump_rxfhash(int fhash, u64 val);
 static int do_srxclass(int fd, struct ifreq *ifr);
 static int do_grxclass(int fd, struct ifreq *ifr);
+static void rmgr_print_ipv4_rule(struct ethtool_rx_flow_spec *fsp);
+static void rmgr_print_rule(struct ethtool_rx_flow_spec *fsp);
+static int rmgr_add(struct ethtool_rx_flow_spec *fsp, __u8 loc_valid);
+static int rmgr_del(__u32 loc);
+static int rmgr_init(int fd, struct ifreq *ifr);
+static int rmgr_rx_rule_getall(int fd, struct ifreq *ifr);
+static int rmgr_rx_rule_get(int fd, struct ifreq *ifr, __u32 loc);
+static int rmgr_rx_rule_ins(int fd, struct ifreq *ifr,
+			    struct ethtool_rx_flow_spec *fsp, __u8 loc_valid);
+static int rmgr_rx_rule_del(int fd, struct ifreq *ifr, __u32 loc);
 
 static enum {
 	MODE_HELP = -1,
@@ -180,14 +198,28 @@  static struct option {
     { "-t", "--test", MODE_TEST, "Execute adapter self test",
                 "               [ online | offline ]\n" },
     { "-S", "--statistics", MODE_GSTATS, "Show adapter statistics" },
-    { "-n", "--show-nfc", MODE_GNFC, "Show Rx network flow classification"
+    { "-n", "--show-nfc", MODE_GNFC, "Show Rx network flow classification "
 		"options",
-		"		[ rx-flow-hash tcp4|udp4|ah4|sctp4|"
-		"tcp6|udp6|ah6|sctp6 ]\n" },
+		"		[ rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|"
+		"tcp6|udp6|ah6|esp6|sctp6 ]\n"
+		"		[ rx-rings ]\n"
+		"		[ rx-class-rule-all ]\n"
+		"		[ rx-class-rule %%d ]\n"},
     { "-N", "--config-nfc", MODE_SNFC, "Configure Rx network flow "
 		"classification options",
-		"		[ rx-flow-hash tcp4|udp4|ah4|sctp4|"
-		"tcp6|udp6|ah6|sctp6 p|m|v|t|s|d|f|n|r... ]\n" },
+		"		[ rx-flow-hash tcp4|udp4|ah4|esp4|sctp4|"
+		"tcp6|udp6|ah6|esp6|sctp6 m|v|t|s|d|f|n|r... ]\n"
+		"		[ rx-class-rule-del %%d ]\n"
+		"		[ rx-class-rule-add ip4|ip6 tcp|udp|sctp|ah|esp|%%d\n"
+		"			[tos %%d [m %%x]]\n"
+		"			[sip %%d.%%d.%%d.%%d "
+		"[m %%d.%%d.%%d.%%d]]\n"
+		"			[dip %%d.%%d.%%d.%%d "
+		"[m %%d.%%d.%%d.%%d]]\n"
+		"			[sport %%d [m %%x]] "
+		"[dport %%d [m %%x]]\n"
+		"			[spi %%d [m %%x]]\n"
+		"			[ring %%d | drop] [loc %%d]]\n"},
     { "-h", "--help", MODE_HELP, "Show this help" },
     {}
 };
@@ -289,6 +321,95 @@  static int rx_fhash_get = 0;
 static int rx_fhash_set = 0;
 static u32 rx_fhash_val = 0;
 static int rx_fhash_changed = 0;
+static int rx_rings_get = 0;
+static int rx_class_rule_get = -1;
+static int rx_class_rule_getall = 0;
+static int rx_class_rule_del = -1;
+static int rx_class_rule_added = 0;
+static struct ethtool_rx_flow_spec rx_rule_fs;
+static u8 rx_class_rule_loc_given = 0;
+
+/*
+ * This is a rule manager implementation for ordering rx flow
+ * classification rules in a longest prefix first match order.
+ * The assumption is that this rule manager is the only one adding rules to
+ * the device's hardware classifier.
+ */
+
+/*
+ * There are 9 regions defined for ordering the rules, listed in decreasing
+ * priority order, i.e., the rules in a particular region are matched after
+ * the rules in the region before it. Withing a given region, lower numbered
+ * rules are matched first.
+ *
+ * Region 1: Rules with all 5 tuples specified, no masks.
+ *		- L4 proto
+ *		- src IP
+ *		- dst IP
+ *		- IP TOS byte
+ *		- src port and/or dst port OR spi
+ *
+ * Region 2: Rules with all 5 tuples specified, and one or more of them
+ * have masks specified.
+ *
+ * Region 3: Rules with 4 tuples specified, no masks.
+ *		- L4 proto and any 3 of the following
+ *			- src IP
+ *			- dst IP
+ *			- IP TOS byte
+ *			- src port and/or dst port OR spi
+ *
+ * Region 4: Rules with 4 tuples specified, and one or more of them
+ * have masks specified.
+ *
+ * Region 5: Rules with 3 tuples specified, no masks.
+ *		- L4 proto and any 2 of the following
+ *			- src IP
+ *			- dst IP
+ *			- IP TOS byte
+ *			- src port and/or dst port OR spi
+ *
+ * Region 6: Rules with 3 tuples specified, and one or more of them
+ * have masks specified.
+ *
+ *
+ * Region 7: Rules with 2 tuples specified, no masks.
+ *		- L4 proto and any 1 of the following
+ *			- src IP
+ *			- dst IP
+ *			- IP TOS byte
+ *			- src port and/or dst port OR spi
+ *
+ * Region 8: Rules with 2 tuples specified, and one or more of them
+ * have masks specified.
+ *
+ * Region 9: Rules with only the L4 proto specified.
+ *
+ */
+#define RMGR_MAX_REGIONS	9
+
+struct rmgr_range {
+	__u32	top;
+	__u32	len;
+	int	region;
+};
+
+struct rmgr_ctrl {
+	__u32				size;
+
+	/* <slot> is the classifier slot map.  It is populated */
+	/* with flow_spec pointers (or NULL). */
+	struct ethtool_rx_flow_spec	**slot;
+	/* <limit> is the maximum number of regions. */
+	__u16				limit;
+	/* <legend> is the set of ranges */
+	struct rmgr_range		legend[RMGR_MAX_REGIONS];
+	__u32				n_rules;
+};
+
+static struct rmgr_ctrl rmgr;
+static int rmgr_init_done = 0;
+
 static enum {
 	ONLINE=0,
 	OFFLINE,
@@ -427,7 +548,9 @@  static int rxflow_str_to_type(const char *str)
 	else if (!strcmp(str, "udp4"))
 		flow_type = UDP_V4_FLOW;
 	else if (!strcmp(str, "ah4"))
-		flow_type = AH_ESP_V4_FLOW;
+		flow_type = AH_V4_FLOW;
+	else if (!strcmp(str, "esp4"))
+		flow_type = ESP_V4_FLOW;
 	else if (!strcmp(str, "sctp4"))
 		flow_type = SCTP_V4_FLOW;
 	else if (!strcmp(str, "tcp6"))
@@ -435,7 +558,9 @@  static int rxflow_str_to_type(const char *str)
 	else if (!strcmp(str, "udp6"))
 		flow_type = UDP_V6_FLOW;
 	else if (!strcmp(str, "ah6"))
-		flow_type = AH_ESP_V6_FLOW;
+		flow_type = AH_V6_FLOW;
+	else if (!strcmp(str, "esp6"))
+		flow_type = ESP_V6_FLOW;
 	else if (!strcmp(str, "sctp6"))
 		flow_type = SCTP_V6_FLOW;
 
@@ -570,6 +695,23 @@  static void parse_cmdline(int argc, char **argp)
 						rxflow_str_to_type(argp[i]);
 					if (!rx_fhash_get)
 						show_usage(1);
+				} else if (!strcmp(argp[i], "rx-rings")) {
+					i += 1;
+					rx_rings_get = 1;
+				} else if (!strcmp(argp[i],
+						   "rx-class-rule-all")) {
+					i += 1;
+					rx_class_rule_getall = 1;
+				} else if (!strcmp(argp[i], "rx-class-rule")) {
+					i += 1;
+					if (i >= argc) {
+						show_usage(1);
+						break;
+					}
+					rx_class_rule_get =
+						strtol(argp[i], NULL, 0);
+					if (rx_class_rule_get < 0)
+						show_usage(1);
 				} else
 					show_usage(1);
 				break;
@@ -597,8 +739,37 @@  static void parse_cmdline(int argc, char **argp)
 						show_usage(1);
 					else
 						rx_fhash_changed = 1;
-				} else
+				} else if (!strcmp(argp[i],
+						   "rx-class-rule-del")) {
+					i += 1;
+					if (i >= argc) {
+						show_usage(1);
+						break;
+					}
+					rx_class_rule_del =
+						strtol(argp[i], NULL, 0);
+					if (rx_class_rule_del < 0)
+						show_usage(1);
+				} else if (!strcmp(argp[i],
+						   "rx-class-rule-add")) {
+					i += 1;
+					if (i >= argc) {
+						show_usage(1);
+						break;
+					}
+					if (parse_rxclassruleopts(&argp[i],
+								  argc - i,
+								  &rx_rule_fs)
+					    < 0) {
+						show_usage(1);
+					} else {
+						i = argc;
+						rx_class_rule_added = 1;
+					}
+				} else {
 					show_usage(1);
+				}
+
 				break;
 			}
 			if (mode != MODE_SSET)
@@ -1097,9 +1268,6 @@  static int parse_rxfhashopts(char *optstr, u32 *data)
 	*data = 0;
 	while (*optstr) {
 		switch (*optstr) {
-			case 'p':
-				*data |= RXH_DEV_PORT;
-				break;
 			case 'm':
 				*data |= RXH_L2DA;
 				break;
@@ -1132,6 +1300,334 @@  static int parse_rxfhashopts(char *optstr, u32 *data)
 	return 0;
 }
 
+static int parse_rxclassruleopts(char **optstr, int opt_cnt,
+				 struct ethtool_rx_flow_spec *fsp)
+{
+	int i = 0;
+
+	u_int8_t discard, ring_set;
+	u_int32_t ipsa, ipsm, ipda, ipdm, spi, spim;
+	u_int16_t sp, spm, dp, dpm;
+	u_int8_t ip_ver, proto, tos, tm;
+	struct in_addr in_addr;
+
+	if (*optstr == NULL || **optstr == '\0' || opt_cnt < 2) {
+		fprintf(stdout, "Add rule, invalid syntax \n");
+		return -1;
+	}
+
+	bzero (fsp, sizeof (struct ethtool_rx_flow_spec));
+	ipsa = ipda = ipsm = ipdm = spi = spim = 0x0;
+	sp = dp = spm = dpm = 0x0;
+	ip_ver = proto = tos = tm = 0x0;
+	discard = ring_set = 0;
+
+	if (!strcmp(optstr[i], "ip4")) {
+		ip_ver = ETH_RX_NFC_IP4;
+	} else if (!strcmp(optstr[i], "ip6")) {
+		ip_ver = ETH_RX_NFC_IP6;
+	} else {
+		fprintf(stdout, "Add rule, invalid syntax for IP version\n");
+		return -1;
+	}
+
+	i++;
+
+	switch (ip_ver) {
+	case ETH_RX_NFC_IP4:
+		if (!strcmp(optstr[i], "tcp")) {
+			fsp->flow_type = TCP_V4_FLOW;
+		} else if (!strcmp(optstr[i], "udp")) {
+			fsp->flow_type = UDP_V4_FLOW;
+		} else if (!strcmp(optstr[i], "sctp")) {
+			fsp->flow_type = SCTP_V4_FLOW;
+		} else if (!strcmp(optstr[i], "ah")) {
+			fsp->flow_type = AH_V4_FLOW;
+		} else if (!strcmp(optstr[i], "esp")) {
+			fsp->flow_type = ESP_V4_FLOW;
+		}
+		break;
+
+	case ETH_RX_NFC_IP6:
+		if (!strcmp(optstr[i], "tcp")) {
+			fsp->flow_type = TCP_V6_FLOW;
+		} else if (!strcmp(optstr[i], "udp")) {
+			fsp->flow_type = UDP_V6_FLOW;
+		} else if (!strcmp(optstr[i], "sctp")) {
+			fsp->flow_type = SCTP_V6_FLOW;
+		} else if (!strcmp(optstr[i], "ah")) {
+			fsp->flow_type = AH_V6_FLOW;
+		} else if (!strcmp(optstr[i], "esp")) {
+			fsp->flow_type = ESP_V6_FLOW;
+		}
+		break;
+	default:
+		fprintf(stdout, "Add rule, Invalid IP version %d\n", ip_ver);
+			return -1;
+	}
+
+	if (fsp->flow_type == 0) {
+		if ((proto = (u_int8_t) strtoul(optstr[i], (char **)NULL, 0))
+		    != 0) {
+			fprintf(stdout, "Add rule, user defined proto %d\n",
+				proto);
+			fsp->flow_type = IP_USER_FLOW;
+			fsp->h_u.usr_ip4_spec.proto = proto;
+			fsp->h_u.usr_ip4_spec.ip_ver = ip_ver;
+		} else {
+			fprintf(stdout, "Add rule, invalid IP proto %s\n",
+				optstr[i]);
+			return -1;
+		}
+	}
+
+	if (ip_ver == ETH_RX_NFC_IP6) {
+		fprintf(stdout, "IPv6 not yet implemented\n");
+		return -1;
+	}
+
+	for (i = 2; i < opt_cnt;) {
+
+		if (!strcmp(optstr[i], "tos")) {
+			tos = (u_int8_t) strtoul(optstr[i+1], (char **)NULL,
+						 0);
+			tm = 0xff;
+			fsp->h_u.tcp_ip4_spec.tos = tos;
+
+			i += 2;
+			if (opt_cnt > (i+1)) {
+				if (!strcmp(optstr[i], "m")) {
+					tm = (u_int8_t)strtoul(optstr[i+1],
+								 (char **)NULL, 16);
+					i += 2;
+				}
+			}
+			fsp->m_u.tcp_ip4_spec.tos = tm;
+
+		} else if (!strcmp(optstr[i], "sip")) {
+			if (strchr(optstr[i+1], '.') == NULL) {
+				ipsa = strtoul(optstr[i+1], (char **)NULL, 16);
+			} else {
+				if (!inet_pton(AF_INET, optstr[i+1], &in_addr)) {
+					fprintf(stdout,
+						"Invalid src address [%s]\n" ,
+						optstr[i+1]);
+					return -1;
+				}
+				ipsa = htonl(in_addr.s_addr);
+			}
+			ipsm = 0xffffffff;
+			fsp->h_u.tcp_ip4_spec.ip4src = ipsa;
+
+			i += 2;
+			if (opt_cnt > (i+1)) {
+				if (!strcmp(optstr[i], "m")) {
+					if (strchr(optstr[i+1], '.') == NULL) {
+						ipsm = strtoul(optstr[i+1],
+							       (char **)NULL,
+							       16);
+					} else {
+						
+						if (!inet_pton(AF_INET,
+							       optstr[i+1],
+							       &in_addr)) {
+							fprintf(stdout,
+								"Invalid smask"
+								"[%s]\n",
+								optstr[i+1]);
+								return -1;
+						}
+						ipsm = htonl(in_addr.s_addr);
+					}
+					i += 2;
+				}
+			}
+			fsp->m_u.tcp_ip4_spec.ip4src = ipsm;
+		} else if (!strcmp(optstr[i], "dip")) {
+			if (strchr(optstr[i+1], '.') == NULL) {
+				ipda = strtoul(optstr[i+1], (char **)NULL, 16);
+			} else {
+				if (!inet_pton(AF_INET, optstr[i+1], &in_addr)) {
+					fprintf(stdout,
+						"Invalid dst address [%s]\n",
+						optstr[i+1]);
+					return -1;
+				}
+				ipda = htonl(in_addr.s_addr);
+			}
+			ipdm = 0xffffffff;
+			fsp->h_u.tcp_ip4_spec.ip4dst = ipda;
+
+			i += 2;
+			if (opt_cnt > (i+1)) {
+				if (!strcmp(optstr[i], "m")) {
+					if (strchr(optstr[i+1], '.') == NULL) {
+						ipdm = strtoul(optstr[i+1],
+							       (char **)NULL,
+							       16);
+					} else {
+						
+						if (!inet_pton(AF_INET,
+							       optstr[i+1],
+							       &in_addr)) {
+							fprintf(stdout,
+								"Invalid dmask"
+								"[%s]\n",
+								optstr[i+1]);
+								return -1;
+						}
+						ipdm = htonl(in_addr.s_addr);
+					}
+					i += 2;
+				}
+			}
+			fsp->m_u.tcp_ip4_spec.ip4dst = ipdm;
+		} else if (!strcmp(optstr[i], "sport")) {
+			switch (fsp->flow_type) {
+			case TCP_V4_FLOW:
+			case UDP_V4_FLOW:
+			case SCTP_V4_FLOW:
+			case TCP_V6_FLOW:
+			case UDP_V6_FLOW:
+			case SCTP_V6_FLOW:
+			case IP_USER_FLOW:
+				break;
+			default:
+				fprintf(stdout, "Invalid option <sport> "
+					"for this flow\n");
+				return -1;
+			}
+			sp = (u_int16_t) strtoul(optstr[i+1], (char **)NULL,
+						 0);
+			spm = 0xffff;
+			if (fsp->flow_type == IP_USER_FLOW) {
+				fsp->h_u.usr_ip4_spec.l4_4_bytes |=
+					((u32)sp << 16);
+			} else {
+				fsp->h_u.tcp_ip4_spec.psrc = sp;
+			}
+			i += 2;
+			if (opt_cnt > (i+1)) {
+				if (!strcmp(optstr[i], "m")) {
+					spm = (u_int16_t)strtoul(optstr[i+1],
+								 (char **)NULL, 16);
+					i += 2;
+				}
+			}
+			if (fsp->flow_type == IP_USER_FLOW) {
+				fsp->m_u.usr_ip4_spec.l4_4_bytes |=
+					((u32)spm << 16);
+			} else {
+				fsp->m_u.tcp_ip4_spec.psrc = spm;
+			}
+		} else if (!strcmp(optstr[i], "dport")) {
+			switch (fsp->flow_type) {
+			case TCP_V4_FLOW:
+			case UDP_V4_FLOW:
+			case SCTP_V4_FLOW:
+			case TCP_V6_FLOW:
+			case UDP_V6_FLOW:
+			case SCTP_V6_FLOW:
+			case IP_USER_FLOW:
+				break;
+			default:
+				fprintf(stdout, "Invalid option <dport> "
+					"for this flow\n");
+				return -1;
+			}
+			dp = (u_int16_t) strtoul(optstr[i+1], (char **)NULL,
+						 0);
+			dpm = 0xffff;
+			if (fsp->flow_type == IP_USER_FLOW)
+				fsp->h_u.usr_ip4_spec.l4_4_bytes |= dp;
+			else
+				fsp->h_u.tcp_ip4_spec.pdst = dp;
+
+			i += 2;
+			if (opt_cnt > (i+1)) {
+				if (!strcmp(optstr[i], "m")) {
+					dpm = (u_int16_t)strtoul(optstr[i+1],
+								 (char **)NULL, 16);
+					i += 2;
+				}
+			}
+			if (fsp->flow_type == IP_USER_FLOW)
+				fsp->m_u.usr_ip4_spec.l4_4_bytes |= dpm;
+			else
+				fsp->m_u.tcp_ip4_spec.pdst = dpm;
+		} else if (!strcmp(optstr[i], "spi")) {
+			switch (fsp->flow_type) {
+			case AH_V4_FLOW:
+			case ESP_V4_FLOW:
+			case AH_V6_FLOW:
+			case ESP_V6_FLOW:
+			case IP_USER_FLOW:
+				break;
+			case TCP_V4_FLOW:
+			case UDP_V4_FLOW:
+			case SCTP_V4_FLOW:
+			case TCP_V6_FLOW:
+			case UDP_V6_FLOW:
+			case SCTP_V6_FLOW:
+			default:
+				fprintf(stdout, "Invalid option <spi> "
+					"for this flow\n");
+				return -1;
+			}
+			spi = (u_int32_t) strtoul(optstr[i+1], (char **)NULL,
+						 0);
+			spim = 0xffffffff;
+			if (fsp->flow_type == IP_USER_FLOW)
+				fsp->h_u.usr_ip4_spec.l4_4_bytes = spi;
+			else
+				fsp->h_u.esp_ip4_spec.spi = spi;
+
+			i += 2;
+			if (opt_cnt > (i+1)) {
+				if (!strcmp(optstr[i], "m")) {
+					spim = (u_int32_t)strtoul(optstr[i+1],
+								 (char **)NULL, 16);
+					i += 2;
+				}
+			}
+			if (fsp->flow_type == IP_USER_FLOW)
+				fsp->m_u.usr_ip4_spec.l4_4_bytes = spim;
+			else
+				fsp->m_u.esp_ip4_spec.spi = spim;
+		} else if (!strcmp(optstr[i], "ring")) {
+			if (discard == 1) {
+				fprintf(stdout, "Invalid syntax - <drop> "
+					"option already specified\n");
+				return -1;
+			}
+			fsp->ring_cookie = strtol(optstr[i+1], (char **)NULL,
+                                                  0);
+			i += 2;
+			ring_set = 1;
+		} else if (!strcmp(optstr[i], "drop")) {
+			if (ring_set == 1) {
+				fprintf(stdout, "Invalid syntax - <ring> "
+					"option already specified\n");
+				return -1;
+			}
+			fsp->ring_cookie = RX_CLS_FLOW_DISC;
+			i++;
+			discard = 1;
+		} else if (!strcmp(optstr[i], "loc")) {
+			fsp->location = strtol(optstr[i+1], (char **)NULL,
+						  0);
+			i += 2;
+			rx_class_rule_loc_given = 1;
+		} else {
+			
+			fprintf(stdout, "Add rule, invalid syntax\n");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
 static char *unparse_rxfhashopts(u64 opts)
 {
 	static char buf[300];
@@ -1139,9 +1635,6 @@  static char *unparse_rxfhashopts(u64 opts)
 	memset(buf, 0, sizeof(buf));
 
 	if (opts) {
-		if (opts & RXH_DEV_PORT) {
-			strcat(buf, "Dev port\n");
-		}
 		if (opts & RXH_L2DA) {
 			strcat(buf, "L2DA\n");
 		}
@@ -1420,9 +1913,12 @@  static int dump_rxfhash(int fhash, u64 val)
 	case SCTP_V4_FLOW:
 		fprintf(stdout, "SCTP over IPV4 flows");
 		break;
-	case AH_ESP_V4_FLOW:
+	case AH_V4_FLOW:
 		fprintf(stdout, "IPSEC AH over IPV4 flows");
 		break;
+	case ESP_V4_FLOW:
+		fprintf(stdout, "IPSEC ESP over IPV4 flows");
+		break;
 	case TCP_V6_FLOW:
 		fprintf(stdout, "TCP over IPV6 flows");
 		break;
@@ -1432,9 +1928,12 @@  static int dump_rxfhash(int fhash, u64 val)
 	case SCTP_V6_FLOW:
 		fprintf(stdout, "SCTP over IPV6 flows");
 		break;
-	case AH_ESP_V6_FLOW:
+	case AH_V6_FLOW:
 		fprintf(stdout, "IPSEC AH over IPV6 flows");
 		break;
+	case ESP_V6_FLOW:
+		fprintf(stdout, "IPSEC ESP over IPV6 flows");
+		break;
 	default:
 		break;
 	}
@@ -1450,6 +1949,143 @@  static int dump_rxfhash(int fhash, u64 val)
 	return 0;
 }
 
+static void rmgr_print_ipv4_rule(struct ethtool_rx_flow_spec *fsp)
+{
+	char		chan[16];
+	char		l4_proto[16];
+
+	if (fsp->ring_cookie != RX_CLS_FLOW_DISC)
+		sprintf(chan, "Rx Ring [%d]", (int)fsp->ring_cookie);
+	else
+		sprintf(chan, "Discard");
+
+	switch (fsp->flow_type) {
+	case TCP_V4_FLOW:
+	case UDP_V4_FLOW:
+	case SCTP_V4_FLOW:
+	case AH_V4_FLOW:
+	case ESP_V4_FLOW:
+	case IP_USER_FLOW:
+		fprintf(stdout,
+			"      IPv4 Rule:   ID[%d] Target[%s]\n"
+			"      IP src addr[%d.%d.%d.%d] mask[%d.%d.%d.%d]\n"
+			"      IP dst addr[%d.%d.%d.%d] mask[%d.%d.%d.%d]\n",
+			fsp->location, chan,
+			(fsp->h_u.tcp_ip4_spec.ip4src & 0xff000000) >> 24,
+			(fsp->h_u.tcp_ip4_spec.ip4src & 0xff0000) >> 16,
+			(fsp->h_u.tcp_ip4_spec.ip4src & 0xff00) >> 8,
+			fsp->h_u.tcp_ip4_spec.ip4src & 0xff,
+			(fsp->m_u.tcp_ip4_spec.ip4src & 0xff000000) >> 24,
+			(fsp->m_u.tcp_ip4_spec.ip4src & 0xff0000) >> 16,
+			(fsp->m_u.tcp_ip4_spec.ip4src & 0xff00) >> 8,
+			fsp->m_u.tcp_ip4_spec.ip4src & 0xff,
+			(fsp->h_u.tcp_ip4_spec.ip4dst & 0xff000000) >> 24,
+			(fsp->h_u.tcp_ip4_spec.ip4dst & 0xff0000) >> 16,
+			(fsp->h_u.tcp_ip4_spec.ip4dst & 0xff00) >> 8,
+			fsp->h_u.tcp_ip4_spec.ip4dst & 0xff,
+			(fsp->m_u.tcp_ip4_spec.ip4dst & 0xff000000) >> 24,
+			(fsp->m_u.tcp_ip4_spec.ip4dst & 0xff0000) >> 16,
+			(fsp->m_u.tcp_ip4_spec.ip4dst & 0xff00) >> 8,
+			fsp->m_u.tcp_ip4_spec.ip4dst & 0xff);
+
+		switch (fsp->flow_type) {
+		case TCP_V4_FLOW:
+			sprintf(l4_proto, "TCP");
+			break;
+		case UDP_V4_FLOW:
+			sprintf(l4_proto, "UDP");
+			break;
+		case SCTP_V4_FLOW:
+			sprintf(l4_proto, "SCTP");
+			break;
+		case AH_V4_FLOW:
+			sprintf(l4_proto, "AH");
+			break;
+		case ESP_V4_FLOW:
+			sprintf(l4_proto, "ESP");
+			break;
+		default:
+			break;
+		}
+		switch (fsp->flow_type) {
+		case TCP_V4_FLOW:
+		case UDP_V4_FLOW:
+		case SCTP_V4_FLOW:
+			fprintf(stdout,
+				"      L4 proto[%s]\n"
+				"      L4 src port[%d] mask[0x%x]\n"
+				"      L4 dst port[%d] mask[0x%x]\n",
+				l4_proto,
+				fsp->h_u.tcp_ip4_spec.psrc,
+				fsp->m_u.tcp_ip4_spec.psrc,
+				fsp->h_u.tcp_ip4_spec.pdst,
+				fsp->m_u.tcp_ip4_spec.pdst);
+			break;
+		case AH_V4_FLOW:
+		case ESP_V4_FLOW:
+			fprintf(stdout,
+				"      L4 proto[%s]\n"
+				"      L4 SPI[%d] mask[0x%x]\n",
+				l4_proto,
+				fsp->h_u.esp_ip4_spec.spi,
+				fsp->m_u.esp_ip4_spec.spi);
+			break;
+		case IP_USER_FLOW:
+			fprintf(stdout,
+				"      L4 user proto[%d]\n"
+				"      L4 first 4 bytes[0x%x] mask[0x%x]\n",
+				fsp->h_u.usr_ip4_spec.proto,
+				fsp->h_u.usr_ip4_spec.l4_4_bytes,
+				fsp->m_u.usr_ip4_spec.l4_4_bytes);
+			break;
+		default:
+			break;
+		}
+		break;
+	default:
+		fprintf(stdout,
+			"      Unknown L4 proto, type[%d]\n", fsp->flow_type);
+		break;
+	}
+
+	fprintf(stdout,
+		"      IP TOS[0x%x] mask[0x%x]\n",
+		fsp->h_u.tcp_ip4_spec.tos,
+		fsp->m_u.tcp_ip4_spec.tos);
+
+	fprintf(stdout,"\n\n");
+}
+
+static void rmgr_print_rule(struct ethtool_rx_flow_spec *fsp)
+{
+	/* print the rule in this location */
+	switch (fsp->flow_type) {
+	case TCP_V4_FLOW:
+	case UDP_V4_FLOW:
+	case SCTP_V4_FLOW:
+	case AH_V4_FLOW:
+	case ESP_V4_FLOW:
+		rmgr_print_ipv4_rule(fsp);
+		break;
+	case TCP_V6_FLOW:
+	case UDP_V6_FLOW:
+	case SCTP_V6_FLOW:
+	case AH_V6_FLOW:
+	case ESP_V6_FLOW:
+		perror("IPv6 flows not implemented");
+		break;
+	case IP_USER_FLOW:
+		if (fsp->h_u.usr_ip4_spec.ip_ver == ETH_RX_NFC_IP4)
+			rmgr_print_ipv4_rule(fsp);
+		else
+			perror("IPv6 flows not implemented");
+		break;
+	default:
+		perror("rmgr: Unknown flow type");
+		break;
+	}
+}
+
 static int doit(void)
 {
 	struct ifreq ifr;
@@ -2338,14 +2974,12 @@  static int do_gstats(int fd, struct ifreq *ifr)
 	return 0;
 }
 
-
 static int do_srxclass(int fd, struct ifreq *ifr)
 {
 	int err;
+	struct ethtool_rxnfc nfccmd;
 
 	if (rx_fhash_changed) {
-		struct ethtool_rxnfc nfccmd;
-
 		nfccmd.cmd = ETHTOOL_SRXFH;
 		nfccmd.flow_type = rx_fhash_set;
 		nfccmd.data = rx_fhash_val;
@@ -2357,6 +2991,20 @@  static int do_srxclass(int fd, struct ifreq *ifr)
 
 	}
 
+	if (rx_class_rule_added) {
+		err = rmgr_rx_rule_ins(fd, ifr, &rx_rule_fs,
+				       rx_class_rule_loc_given);
+		if (err < 0)
+			perror("Cannot insert RX classification rule");
+	}
+
+	if (rx_class_rule_del >= 0) {
+		err = rmgr_rx_rule_del(fd, ifr, rx_class_rule_del);
+
+		if (err < 0)
+			perror("Cannot delete RX classification rule");
+	}
+
 	return 0;
 }
 
@@ -2377,6 +3025,388 @@  static int do_grxclass(int fd, struct ifreq *ifr)
 			dump_rxfhash(rx_fhash_get, nfccmd.data);
 	}
 
+	if (rx_rings_get) {
+		struct ethtool_rxnfc nfccmd;
+
+		nfccmd.cmd = ETHTOOL_GRXRINGS;
+		ifr->ifr_data = (caddr_t)&nfccmd;
+		err = ioctl(fd, SIOCETHTOOL, ifr);
+		if (err < 0)
+			perror("Cannot get RX rings");
+		else
+			fprintf(stdout, "%d RX rings available\n",
+				(int)nfccmd.data);
+	}
+
+	if (rx_class_rule_get >= 0) {
+		err = rmgr_rx_rule_get(fd, ifr, rx_class_rule_get);
+		if (err < 0)
+			perror("Cannot get RX classification rule");
+	}
+
+	if (rx_class_rule_getall) {
+		err = rmgr_rx_rule_getall(fd, ifr);
+		if (err < 0)
+			perror("RX classification rule retrieval failed");
+	}
+
+	return 0;
+}
+
+static int rmgr_add(struct ethtool_rx_flow_spec *fsp, __u8 loc_valid)
+{
+	struct ethtool_rx_flow_spec *new_rule = NULL;
+	struct rmgr_range region;
+	int i, j;
+
+	if (loc_valid) {
+		/* Do a direct insertion by location index, no sorting */
+		rmgr.slot[fsp->location] =
+			calloc(1, sizeof (struct ethtool_rx_flow_spec));
+		*rmgr.slot[fsp->location] = *fsp;
+			
+		return 0;
+	}
+
+	/* evaluate rule and add in appropriate region */
+	i = rmgr.limit;
+
+	/* Proto is always present (specified or implicit) */
+	i--;
+	switch (fsp->flow_type) {
+	case TCP_V4_FLOW:
+	case UDP_V4_FLOW:
+	case SCTP_V4_FLOW:
+	case AH_V4_FLOW:
+	case ESP_V4_FLOW:
+		if (fsp->m_u.tcp_ip4_spec.tos == 0xff)
+			i--;
+		if (fsp->h_u.tcp_ip4_spec.tos)
+			i--;
+		if (fsp->m_u.tcp_ip4_spec.ip4src == 0xffffffff)
+			i--;
+		if (fsp->h_u.tcp_ip4_spec.ip4src)
+			i--;
+		if (fsp->m_u.tcp_ip4_spec.ip4dst == 0xffffffff)
+			i--;
+		if (fsp->h_u.tcp_ip4_spec.ip4dst)
+			i--;
+		if (fsp->flow_type == AH_V4_FLOW ||
+		    fsp->flow_type == ESP_V4_FLOW) {
+			if (fsp->m_u.esp_ip4_spec.spi == 0xffffffff)
+				i--;
+			if (fsp->h_u.esp_ip4_spec.spi)
+				i--;
+		} else {
+			if ((fsp->m_u.tcp_ip4_spec.psrc == 0xffff) ||
+			    (fsp->m_u.tcp_ip4_spec.pdst == 0xffff))
+				i--;
+			if (fsp->h_u.tcp_ip4_spec.psrc ||
+			    fsp->h_u.tcp_ip4_spec.pdst)
+				i--;
+		}
+		break;
+	case TCP_V6_FLOW:
+	case UDP_V6_FLOW:
+	case SCTP_V6_FLOW:
+	case AH_V6_FLOW:
+	case ESP_V6_FLOW:
+		perror("IPv6 flows not implemented");
+		return -1;
+	case IP_USER_FLOW:
+		if (fsp->h_u.usr_ip4_spec.ip_ver == ETH_RX_NFC_IP4) {
+			if (fsp->m_u.usr_ip4_spec.tos == 0xff)
+				i--;
+			if (fsp->h_u.usr_ip4_spec.tos)
+				i--;
+			if (fsp->m_u.usr_ip4_spec.ip4src == 0xffffffff)
+				i--;
+			if (fsp->h_u.usr_ip4_spec.ip4src)
+				i--;
+			if (fsp->m_u.usr_ip4_spec.ip4dst == 0xffffffff)
+				i--;
+			if (fsp->h_u.usr_ip4_spec.ip4dst)
+				i--;
+			if (fsp->m_u.usr_ip4_spec.l4_4_bytes == 0xffffffff)
+				i--;
+			if (fsp->h_u.usr_ip4_spec.l4_4_bytes)
+				i--;
+			break;
+		} else {
+			perror("IPv6 flows not implemented");
+			return -1;
+		}
+	default:
+		return -1;
+	}
+
+	/* find an empty slot in this region */
+	region = rmgr.legend[i];
+	for (j = region.top; j < (region.top + region.len); j++) {
+		if (rmgr.slot[j] == (struct ethtool_rx_flow_spec *)NULL) {
+			new_rule =
+				calloc(1,
+				       sizeof (struct ethtool_rx_flow_spec));
+			if (new_rule == (struct ethtool_rx_flow_spec *)NULL)
+				return -1;
+			*new_rule = *fsp;
+			rmgr.slot[j] = new_rule;
+			break;
+		}
+	}
+	if (new_rule == (struct ethtool_rx_flow_spec *)NULL) {
+		/* no space in this region */
+		/* Try to append */
+		j = region.top + region.len;
+		if (j < rmgr.size) {
+			if (rmgr.slot[j] ==
+			    (struct ethtool_rx_flow_spec *)NULL) {
+				new_rule =
+					calloc(1,
+					       sizeof (struct ethtool_rx_flow_spec ));
+				if (new_rule ==
+				    (struct ethtool_rx_flow_spec *)NULL)
+					return -1;
+				*new_rule = *fsp;
+				rmgr.slot[j] = new_rule;
+				rmgr.legend[i+1].top++;
+				rmgr.legend[i+1].len--;
+				rmgr.legend[i].len++;
+			}
+		}
+		if (new_rule == (struct ethtool_rx_flow_spec *)NULL) {
+			/* Try to prepend */
+			j = region.top - 1;
+			if (j >= 0) {
+				if (rmgr.slot[j] ==
+				    (struct ethtool_rx_flow_spec *)NULL) {
+					new_rule =
+						calloc(1,
+						       sizeof (struct ethtool_rx_flow_spec ));
+					if (new_rule == NULL)
+						return -1;
+					*new_rule = *fsp;
+					rmgr.slot[j] = new_rule;
+					rmgr.legend[i].top--;
+					rmgr.legend[i].len++;
+					rmgr.legend[i-1].len--;
+				}
+			}
+		}
+	}
+	if (new_rule == (struct ethtool_rx_flow_spec *)NULL) {
+		/* No space to add this rule */
+		perror("rmgr: Cannot find appropriate slot to insert rule");
+		return -1;
+	}
+
+	fsp->location = j;
+	     
+	return 0;
+}
+
+static int rmgr_del(__u32 loc)
+{
+	int i;
+	__u32 bottom;
+
+	if (rmgr.slot[loc] == NULL) {
+		perror("rmgr: No such rule");
+		return -1;
+	}
+
+	/* find the region */
+	for (i = 0; i < rmgr.limit; i++) {
+		bottom = rmgr.legend[i].top + rmgr.legend[i].len;
+		if (loc >= rmgr.legend[i].top &&
+		    loc < bottom) {
+			if (rmgr.slot[loc] !=
+			    (struct ethtool_rx_flow_spec *)NULL)
+				free(rmgr.slot[loc]);
+			rmgr.slot[loc] = NULL;
+		}
+	}
+
+	if (rmgr.slot[loc] != NULL) {
+		/* Did not find the region -- something is wrong */
+		perror("rmgr: Could not find rule");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int rmgr_init(int fd, struct ifreq *ifr)
+{
+	struct ethtool_rxnfc *nfccmd;
+	struct rmgr_range *range;
+	int err, i, j;
+	__u32 reg_len;
+	__u32 *rule_locs;
+
+	if (rmgr_init_done)
+		return 0;
+
+	bzero((void *)&rmgr, sizeof (struct rmgr_ctrl));
+
+	nfccmd = calloc(1, sizeof(*nfccmd));
+	nfccmd->cmd = ETHTOOL_GRXCLSRLCNT;
+	ifr->ifr_data = (caddr_t)nfccmd;
+	err = ioctl(fd, SIOCETHTOOL, ifr);
+	rmgr.n_rules = nfccmd->rule_cnt;
+	free(nfccmd);
+	if (err < 0) {
+		perror("rmgr: Cannot get RX class rule count");
+		return -1;
+	}
+
+	nfccmd = calloc(1, sizeof(*nfccmd) + (rmgr.n_rules * sizeof(__u32)));
+
+	nfccmd->cmd = ETHTOOL_GRXCLSRLALL;
+	nfccmd->rule_cnt = rmgr.n_rules;
+	ifr->ifr_data = (caddr_t)nfccmd;
+	err = ioctl(fd, SIOCETHTOOL, ifr);
+	if (err < 0) {
+		perror("rmgr: Cannot get RX class rules");
+		free(nfccmd);
+		return -1;
+	}
+
+	rmgr.size = nfccmd->data;
+	rmgr.slot = calloc(1, rmgr.size *
+			   sizeof (struct ethtool_rx_flow_spec *));
+
+	rmgr.limit = RMGR_MAX_REGIONS;
+
+	reg_len = rmgr.size / rmgr.limit;
+	range = rmgr.legend;
+
+	for (i = 0, j = 0; i < rmgr.limit; i++) {
+		range->top = (__u32)j;
+		range->len = reg_len;
+		range->region = i;
+		j += reg_len;
+		range++;
+	}
+
+	rule_locs = nfccmd->rule_locs;
+
+	for (i = 0; i < rmgr.n_rules; i++) {
+		/* get rule from device and add to rule manager */
+		nfccmd->cmd = ETHTOOL_GRXCLSRULE;
+		nfccmd->fs.location = rule_locs[i];
+		ifr->ifr_data = (caddr_t)nfccmd;
+		err = ioctl(fd, SIOCETHTOOL, ifr);
+		if (err < 0) {
+			perror("rmgr: Cannot get RX class rule");
+			free(rmgr.slot);
+			free(nfccmd);
+			return -1;
+		}
+		err = rmgr_add(&nfccmd->fs, 1);
+	}
+
+	rmgr_init_done = 1;
+	free(nfccmd);
+	return 0;
+}
+
+static int rmgr_rx_rule_getall(int fd, struct ifreq *ifr)
+{
+	struct ethtool_rx_flow_spec *fsp;
+	int err, i;
+
+	if (!rmgr_init_done) {
+		err = rmgr_init(fd, ifr);
+		if (err < 0)
+			return err;
+	}
+
+	fprintf(stdout, "Total %d rules\n\n", rmgr.n_rules);
+
+	for (i = 0; i < rmgr.size; i++) {
+		fsp = rmgr.slot[i];
+		if (fsp == (struct ethtool_rx_flow_spec *)NULL)
+			continue;
+		rmgr_print_rule(fsp);
+	}
+	return 0;
+}
+
+static int rmgr_rx_rule_get(int fd, struct ifreq *ifr, __u32 loc)
+{
+	struct ethtool_rx_flow_spec *fsp;
+	int err;
+
+	if (!rmgr_init_done) {
+		err = rmgr_init(fd, ifr);
+		if (err < 0)
+			return err;
+	}
+
+	fsp = rmgr.slot[loc];
+	if (fsp == NULL || fsp->location != loc) {
+		perror("rmgr: No such rule");
+		return -1;
+	}
+
+	rmgr_print_rule(fsp);
+
+	return 0;
+}
+
+static int rmgr_rx_rule_ins(int fd, struct ifreq *ifr,
+			    struct ethtool_rx_flow_spec *fsp, __u8 loc_valid)
+{
+	struct ethtool_rxnfc nfccmd;
+	int err;
+
+	if (!rmgr_init_done) {
+		err = rmgr_init(fd, ifr);
+		if (err < 0)
+			return err;
+	}
+
+	err = rmgr_add(fsp, loc_valid);
+	if (err < 0)
+		return err;
+
+	nfccmd.cmd = ETHTOOL_SRXCLSRLINS;
+	nfccmd.fs = *fsp;
+	ifr->ifr_data = (caddr_t)&nfccmd;
+	err = ioctl(fd, SIOCETHTOOL, ifr);
+	if (err < 0) {
+		perror("rmgr: Cannot insert RX class rule");
+		return -1;
+	}
+	rmgr.n_rules++;
+	return 0;
+}
+
+static int rmgr_rx_rule_del(int fd, struct ifreq *ifr, __u32 loc)
+{
+	struct ethtool_rxnfc nfccmd;
+	int err;
+
+	if (!rmgr_init_done) {
+		err = rmgr_init(fd, ifr);
+		if (err < 0)
+			return err;
+	}
+
+	err = rmgr_del(loc);
+	if (err < 0)
+		return err;
+
+	nfccmd.cmd = ETHTOOL_SRXCLSRLDEL;
+	nfccmd.fs.location = loc;
+	ifr->ifr_data = (caddr_t)&nfccmd;
+	err = ioctl(fd, SIOCETHTOOL, ifr);
+	if (err < 0) {
+		perror("rmgr: Cannot delete RX class rule");
+		return -1;
+	}
+	rmgr.n_rules--;
 	return 0;
 }
 
-- 
1.6.0.4