From: Santwona Behera [PATCH 1/3] [ethtool] Add RX pkt classification interface Signed-off-by: Santwona Behera --- 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 + /* 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 * Wake-on-LAN,natsemi,misc support by Tim Hockin * Portions Copyright 2002 Intel + * Portions Copyright (c) Sun Microsystems 2009 * do_test support by Eli Kupermann * ETHTOOL_PHYS_ID support by Chris Leech * e1000 support by Scott Feldman @@ -14,6 +15,7 @@ * amd8111e support by Reeja John * long arguments by Andi Kleen. * SMSC LAN911x support by Steve Glendinning + * Rx Network Flow Control configuration support * * TODO: * * no-args => summary of each device (mii-tool style) @@ -34,11 +36,15 @@ #include #include #include -#include +#include +#include +#include #include #include #include +#include +#include #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; + + /* is the classifier slot map. It is populated */ + /* with flow_spec pointers (or NULL). */ + struct ethtool_rx_flow_spec **slot; + /* is the maximum number of regions. */ + __u16 limit; + /* 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 " + "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 " + "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 " + "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 - " + "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 - " + "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