* Re: [RFC v3 00/10] snet: Security for NETwork syscalls
From: Samir Bellabes @ 2011-05-03 17:15 UTC (permalink / raw)
To: Casey Schaufler
Cc: linux-security-module, linux-kernel, netdev, netfilter-devel,
jamal, Patrick McHardy, Evgeniy Polyakov, Grzegorz Nosek
In-Reply-To: <4DC0330C.60208@schaufler-ca.com>
Casey Schaufler <casey@schaufler-ca.com> writes:
> On 5/3/2011 7:24 AM, Samir Bellabes wrote:
>> Hello lsm and netdev people,
>> This set of patches is the version 3 of snet, which I would like to submit as a
>> RFC.
>>
>> snet is a linux security module. It provides a mecanism defering syscall
>> security hooks and decision (verdict) to userspace.
>
> As you have submitted this as a Request For Comments I will make one.
>
> I first saw this approach in 1987, on Unix, from a company called
> SecureWare (long completely assimilated into HP). The potential for
> deadlock, where the system prevents the decision making application
> from accessing the information it needs to grant itself access is
> great. The performance impact of making security checks in user
> space is appalling. The exposure for attack, especially regarding
> denial of service, is enormous. I do not recommend this approach.
>
> There are cases where user space access control assistance could
> be appropriate, in particular controls based on the data involved.
> Even those controls must be very carefully crafted to avoid
> impacting the correct function of the system in the unhappily
> likely event of the access control enforcing applications being
> unavailable or incapable of keeping up with demand.
>
As everything may be exposed to denial of service attack..
I have some thoughts. snet is not a tool for securing the kernel code,
there is only one way to do so, it's to fix bug and to add code feature
to protect memory (cf grsecurity). snet is a tool to manage the
behaviour of users and applications, regarding network connections.
the risk of deadlock is uneffective, as every sleeps occurs in process
context, so application can sleep without trouble.
there are 2 ways to go out of sleep :
- receiving the verdict
- timeouting
so deadlock are more "latency".
You win a admin tool, you loose some latency. I'm ok with that, as this
feature as its own public.
and of course, I'm not pretending to add a new idea. I'm sure some
mecanism like this already exist before 1987. I'm just the man who put
the code in order to be discuss on the lists, which was never been done
so far.
there are some request from public distro:
http://brainstorm.ubuntu.com/idea/23333/
^ permalink raw reply
* Re: [PATCH] ipheth.c: Enable IP header alignment
From: L. Alberto Giménez @ 2011-05-03 16:57 UTC (permalink / raw)
To: Ben Hutchings
Cc: linux-kernel, dgiagio, dborca, davem, pmcenery, david.hill,
open list:USB SUBSYSTEM, open list:NETWORKING DRIVERS
In-Reply-To: <1304370274.2833.192.camel@localhost>
On Mon, May 02, 2011 at 10:04:34PM +0100, Ben Hutchings wrote:
> So this was using NET_IP_ALIGN as an offset into the URB. Which was
> totally bogus, as its value has long been architecture-dependent. The
> code is also claiming to put len bytes but only copying len - delta.
>
> The correct code would be something like:
>
> if (urb->actual_length <= IPHETH_IP_ALIGN) {
> dev->net->stats.rx_length_errors++;
> return;
> }
> len = urb->actual_length - IPHETH_IP_ALIGN;
> buf = urb->transfer_buffer + IPHETH_IP_ALIGN;
>
> dev_alloc_skb(len);
> ...
> memcpy(skb_put(skb, len), buf, len);
Thanks for the response Ben.
I can try to change the code, but I don't own the device anymore. Changing the
code without being able to test it would be walking blindfolded :-/
If upstrem (everyone involved is in CC) can't do it, I can submit the changes
advised by Ben, but I can't warantee anything beyond successful compilation. I
don't think that it would be acceptable here.
Regards,
--
L. Alberto Giménez
JabberID agimenez@jabber.sysvalve.es
GnuPG key ID 0x3BAABDE1
^ permalink raw reply
* Re: [RFC v3 00/10] snet: Security for NETwork syscalls
From: Casey Schaufler @ 2011-05-03 16:53 UTC (permalink / raw)
To: Samir Bellabes
Cc: linux-security-module, linux-kernel, netdev, netfilter-devel,
jamal, Patrick McHardy, Evgeniy Polyakov, Grzegorz Nosek,
Casey Schaufler
In-Reply-To: <1304432663-1575-1-git-send-email-sam@synack.fr>
On 5/3/2011 7:24 AM, Samir Bellabes wrote:
> Hello lsm and netdev people,
> This set of patches is the version 3 of snet, which I would like to submit as a
> RFC.
>
> snet is a linux security module. It provides a mecanism defering syscall
> security hooks and decision (verdict) to userspace.
As you have submitted this as a Request For Comments I will make one.
I first saw this approach in 1987, on Unix, from a company called
SecureWare (long completely assimilated into HP). The potential for
deadlock, where the system prevents the decision making application
from accessing the information it needs to grant itself access is
great. The performance impact of making security checks in user
space is appalling. The exposure for attack, especially regarding
denial of service, is enormous. I do not recommend this approach.
There are cases where user space access control assistance could
be appropriate, in particular controls based on the data involved.
Even those controls must be very carefully crafted to avoid
impacting the correct function of the system in the unhappily
likely event of the access control enforcing applications being
unavailable or incapable of keeping up with demand.
> snet has some subsystems :
> - snet_core : init and exit the system
> - snet_hooks : LSM hooks
> - snet_netlink : kernel-user communication (genetlink)
> - snet_event : manages the list of protected syscalls
> - snet_verdict : provides a waitqueue for syscalls and manage verdicts
> - snet_ticket : provides a granted-access ticket mecanism
>
> I believe that snet will help to get over the classical configuration
> complexity of others security modules, by providing interactivity to users.
> I also think that monolithic strategy is broken with snet, as we can provide
> security for others syscall's categories:
> - sfs : security for filesystem,
> - stask: security for task,
> - smem : security for memory
> ..
>
> In this way, and by putting abstraction on how this subsystems can talk to each
> others, we may use the security combinaison we want: choose to run sfs,
> stask, but not snet nor smem. Better, developpers may investigated how to build
> another security subsystem for tasks, and use others existing (smem, snet..)
> which they don't want to modify
>
> I think that interactivity is very usefull for users, as they may be notify when
> something is wrong and take decision, and from userspace, the decision may be
> defered to another box. In this way, snet also have a advantage for mobile
> devices as the policy decision will be push to a distant server, mobile device
> will then wait for verdicts and as policy strategies are centralized.
>
> Interactivity is *not* only clicking a Yes/No question, as said, we
> can centralised previous locals LSM security subsytems, and make the
> network aware of events occuring on it.
>
> Finally, and a important point: snet integration respects the LSM framework idea
> of using LSM hooks.
>
> New feature from the previous version:
> * Building a ticket mecanism for each task_struct using pointer void *security
> Use the pointer (void*) security related to task_struct to provides
> granted-acces tickets: if two identical requests are coming, ask the user
> for the first one, store the result in a ticket and for the second request,
> just look in the tickets owned by the task-struct
> * send data buffer of sendmsg to userspace
> this may provide a way to look inside the data (as a anti-virus do)
>
> roadmap:
> * find a way to send data buffer of sendmsg to userspace (using netfilter)
> * adding other security systems
> we can think about adding fork(), exec(), open(), close()..
>
> I'm Ccing netfilter-devel, as snet may be seen as a way to do filtering.
>
> Last devel patchs were:
> * using kmem_cache instead of kmalloc
> * remove attend to send buffer socker to userspace
>
> Samir Bellabes (10):
> lsm: add security_socket_closed()
> Revert "lsm: Remove the socket_post_accept() hook"
> snet: introduce snet_core
> snet: introduce snet_event
> snet: introduce snet_hooks
> snet: introduce snet_netlink
> snet: introduce snet_verdict
> snet: introduce snet_ticket
> snet: introduce snet_utils
> snet: introduce security/snet, Makefile and Kconfig changes
>
> include/linux/security.h | 23 ++
> include/linux/snet.h | 117 ++++++
> net/socket.c | 3 +
> security/Kconfig | 6 +
> security/Makefile | 2 +
> security/capability.c | 10 +
> security/security.c | 10 +
> security/snet/Kconfig | 11 +
> security/snet/Makefile | 14 +
> security/snet/snet_core.c | 82 ++++
> security/snet/snet_event.c | 201 ++++++++++
> security/snet/snet_event.h | 21 +
> security/snet/snet_hooks.c | 722 +++++++++++++++++++++++++++++++++++
> security/snet/snet_hooks.h | 10 +
> security/snet/snet_netlink.c | 442 +++++++++++++++++++++
> security/snet/snet_netlink.h | 17 +
> security/snet/snet_netlink_helper.c | 220 +++++++++++
> security/snet/snet_netlink_helper.h | 7 +
> security/snet/snet_ticket.c | 195 ++++++++++
> security/snet/snet_ticket.h | 37 ++
> security/snet/snet_ticket_helper.c | 127 ++++++
> security/snet/snet_ticket_helper.h | 8 +
> security/snet/snet_utils.c | 38 ++
> security/snet/snet_utils.h | 9 +
> security/snet/snet_verdict.c | 203 ++++++++++
> security/snet/snet_verdict.h | 23 ++
> 26 files changed, 2558 insertions(+), 0 deletions(-)
> create mode 100644 include/linux/snet.h
> create mode 100644 security/snet/Kconfig
> create mode 100644 security/snet/Makefile
> create mode 100644 security/snet/snet_core.c
> create mode 100644 security/snet/snet_event.c
> create mode 100644 security/snet/snet_event.h
> create mode 100644 security/snet/snet_hooks.c
> create mode 100644 security/snet/snet_hooks.h
> create mode 100644 security/snet/snet_netlink.c
> create mode 100644 security/snet/snet_netlink.h
> create mode 100644 security/snet/snet_netlink_helper.c
> create mode 100644 security/snet/snet_netlink_helper.h
> create mode 100644 security/snet/snet_ticket.c
> create mode 100644 security/snet/snet_ticket.h
> create mode 100644 security/snet/snet_ticket_helper.c
> create mode 100644 security/snet/snet_ticket_helper.h
> create mode 100644 security/snet/snet_utils.c
> create mode 100644 security/snet/snet_utils.h
> create mode 100644 security/snet/snet_verdict.c
> create mode 100644 security/snet/snet_verdict.h
>
^ permalink raw reply
* Re: Frequent spurious tx_timeouts for libertas
From: Ben Hutchings @ 2011-05-03 16:47 UTC (permalink / raw)
To: Daniel Drake
Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
libertas-dev-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, linux-wireless
In-Reply-To: <1304303082.2833.159.camel@localhost>
On Mon, 2011-05-02 at 03:24 +0100, Ben Hutchings wrote:
[...]
> And now, for the specific case of libertas:
>
> It appears that libertas reports carrier off (link down) while scanning,
> so that should mean the watchdog does not fire. However:
>
> 1. The watchdog will only check the current link state, which might be
> up again when it runs.
[...]
However, netif_carrier_on() will reset the watchdog timer, so the link
really does have to be reported up continuously for 5 seconds before the
watchdog will fire.
There is a small race condition though...
void netif_carrier_on(struct net_device *dev)
{
if (test_and_clear_bit(__LINK_STATE_NOCARRIER, &dev->state)) {
/*
* If the device is running and the link has been down for exactly
* dev->watchdog_timeo ticks, the watchdog can fire now.
*/
if (dev->reg_state == NETREG_UNINITIALIZED)
return;
linkwatch_fire_event(dev);
if (netif_running(dev))
/*
* The watchdog timer is reset here:
*/
__netdev_watchdog_up(dev);
}
}
However, given the timing of channel scanning that you described, I
don't think this would explain the watchdog firing for libertas.
Ben.
--
Ben Hutchings, Senior Software Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [ethtool PATCH 4/4] v5 Add RX packet classification interface
From: Alexander Duyck @ 2011-05-03 16:12 UTC (permalink / raw)
To: davem, jeffrey.t.kirsher, bhutchings; +Cc: netdev
In-Reply-To: <20110503160547.29251.84333.stgit@gitlad.jf.intel.com>
From: Santwona Behera <santwona.behera@sun.com>
This patch was originally introduced as:
[PATCH 1/3] [ethtool] Add rx pkt classification interface
Signed-off-by: Santwona Behera <santwona.behera@sun.com>
http://patchwork.ozlabs.org/patch/23223/
v2:
I have updated it to address a number of issues. As a result I removed the
local caching of rules due to the fact that there were memory leaks in this
code and the rule manager would consume over 1Mb of space for an 8K table
when all that was needed was 1K in order to store which rules were active
and which were not.
In addition I dropped the use of regions as there were multiple issue found
including the fact that the regions were not properly expanding beyond 2
and the fact that the regions required reading all of the rules in order to
correctly expand beyond 2. By dropping the regions from the rule manager
it is possible to write a much cleaner interface leaving region management
to be done by either the driver or by external management scripts.
v3:
The latest update to this patch now inverts the masks to match the mask
types used for n-tuple. As such a network flow classifier is defined using
the exact same syntax as n-tuple, and the tool will correct for the fact
that NFC uses the 1's compliment of the n-tuple mask.
I also updated the ordering of new rules being added. All new rules will
take the highest numbered open rule when no location is specified.
Since NFC now uses the same syntax as n-tuple I added code such that now
when location is not specified the -U option will first try to add a new
n-tuple rule, and if that fails with a ENOTSUPP it will then try to add the
rule via the NFC interface.
Finally I split out the addition of bitops and the updates to documentation
into separate patches. This makes the total patch size a bit more
manageable since the addition of NFC and the merging of it with n-tuple were
combined into this patch.
v4:
This change merges the ntuple and network flow classifier rules so that if
we setup a rule and the device has the NTUPLE flag set we will first try to
use set_rx_ntuple. If that fails with EOPNOTSUPP we then will attempt to
use the network flow classifier rule insertion. This way we can support
legacy configurations such as niu on kernels prior to 2.6.40 that support
network flow classifier but not ntuple, but for drivers such as ixgbe we
can test for ntuple first, and then network flow classifier.
This patch has also updated the output to make use of the updated network
flow classifier extensions that have been accepted into the kernel.
v5:
This change merged the documentation update into this patch. In addition the
documentation changes were made such that there is only one listing of the
individual options and they are all listed as optional.
In addition this patch contains several fixes to address things such as the
fact that we were maintaining the table logic even though we only need it for
displaying all of the rules, or when adding a rule with no location specified.
As such all of the logic for deleting or finding rules in the table has been
removed.
Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
---
Makefile.am | 3
ethtool-util.h | 14 +
ethtool.8.in | 194 ++++++----
ethtool.c | 400 ++++++++++++----------
rxclass.c | 1039 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 1384 insertions(+), 266 deletions(-)
create mode 100644 rxclass.c
diff --git a/Makefile.am b/Makefile.am
index a0d2116..0262c31 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -8,7 +8,8 @@ ethtool_SOURCES = ethtool.c ethtool-copy.h ethtool-util.h \
amd8111e.c de2104x.c e100.c e1000.c igb.c \
fec_8xx.c ibm_emac.c ixgb.c ixgbe.c natsemi.c \
pcnet32.c realtek.c tg3.c marvell.c vioc.c \
- smsc911x.c at76c50x-usb.c sfc.c stmmac.c
+ smsc911x.c at76c50x-usb.c sfc.c stmmac.c \
+ rxclass.c
dist-hook:
cp $(top_srcdir)/ethtool.spec $(distdir)
diff --git a/ethtool-util.h b/ethtool-util.h
index d8b621c..79be7f2 100644
--- a/ethtool-util.h
+++ b/ethtool-util.h
@@ -65,6 +65,8 @@ static inline u64 cpu_to_be64(u64 value)
#define SIOCETHTOOL 0x8946
#endif
+#define RX_CLS_LOC_UNSPEC 0xffffffffUL
+
/* National Semiconductor DP83815, DP83816 */
int natsemi_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
int natsemi_dump_eeprom(struct ethtool_drvinfo *info,
@@ -128,4 +130,14 @@ int sfc_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
int st_mac100_dump_regs(struct ethtool_drvinfo *info,
struct ethtool_regs *regs);
int st_gmac_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
-#endif
+
+/* Rx flow classification */
+int rxclass_parse_ruleopts(char **optstr, int opt_cnt,
+ struct ethtool_rx_flow_spec *fsp);
+int rxclass_rule_getall(int fd, struct ifreq *ifr);
+int rxclass_rule_get(int fd, struct ifreq *ifr, __u32 loc);
+int rxclass_rule_ins(int fd, struct ifreq *ifr,
+ struct ethtool_rx_flow_spec *fsp);
+int rxclass_rule_del(int fd, struct ifreq *ifr, __u32 loc);
+
+#endif /* ETHTOOL_UTIL_H__ */
diff --git a/ethtool.8.in b/ethtool.8.in
index 9f484fb..c923319 100644
--- a/ethtool.8.in
+++ b/ethtool.8.in
@@ -42,10 +42,20 @@
[\\fB\\$1\\fP\ \\fIN\\fP]
..
.\"
+.\" .BM - same as above but has a mask field for format "[value N [m N]]"
+.\"
+.de BM
+[\\fB\\$1\\fP\ \\fIN\\fP\ [\\fBm\\fP\ \\fIN\\fP]]
+..
+.\"
.\" \(*MA - mac address
.\"
.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 \fIx\fP\fB.\fP\fIx\fP\fB.\fP\fIx\fP\fB.\fP\fIx\fP
+.\"
.\" \(*WO - wol flags
.\"
.ds WO \fBp\fP|\fBu\fP|\fBm\fP|\fBb\fP|\fBa\fP|\fBg\fP|\fBs\fP|\fBd\fP...
@@ -57,6 +67,12 @@
.\" \(*HO - hash options
.\"
.ds HO \fBm\fP|\fBv\fP|\fBt\fP|\fBs\fP|\fBd\fP|\fBf\fP|\fBn\fP|\fBr\fP...
+.\"
+.\" \(*NC - Network Classifier type values
+.\"
+.ds NC \fBether\fP|\fBip4\fP|\fBtcp4\fP|\fBudp4\fP|\fBsctp4\fP|\fBah4\fP|\fBesp4\fP
+
+.\"
.\" Start URL.
.de UR
. ds m1 \\$1\"
@@ -236,8 +252,7 @@ ethtool \- query or control network driver and hardware settings
.HP
.B ethtool \-N
.I ethX
-.RB [ rx\-flow\-hash \ \*(FL
-.RB \ \*(HO]
+.RB [ rx\-flow\-hash \ \*(FL \ \*(HO]
.HP
.B ethtool \-x|\-\-show\-rxfh\-indir
.I ethX
@@ -257,50 +272,28 @@ ethtool \- query or control network driver and hardware settings
.HP
.B ethtool \-u|\-\-show\-ntuple
.I ethX
-.TP
+.BN rule
+.HP
+
.BI ethtool\ \-U|\-\-config\-ntuple \ ethX
-.RB {
-.A3 flow\-type tcp4 udp4 sctp4
-.RB [ src\-ip
-.IR addr
-.RB [ src\-ip\-mask
-.IR mask ]]
-.RB [ dst\-ip
-.IR addr
-.RB [ dst\-ip\-mask
-.IR mask ]]
-.RB [ src\-port
-.IR port
-.RB [ src\-port\-mask
-.IR mask ]]
-.RB [ dst\-port
-.IR port
-.RB [ dst\-port\-mask
-.IR mask ]]
-.br
-.RB | \ flow\-type\ ether
-.RB [ src
-.IR mac\-addr
-.RB [ src\-mask
-.IR mask ]]
-.RB [ dst
-.IR mac\-addr
-.RB [ dst\-mask
-.IR mask ]]
-.RB [ proto
-.IR N
-.RB [ proto\-mask
-.IR mask ]]\ }
-.br
-.RB [ vlan
-.IR VLAN\-tag
-.RB [ vlan\-mask
-.IR mask ]]
-.RB [ user\-def
-.IR data
-.RB [ user\-def\-mask
-.IR mask ]]
-.RI action \ N
+.BN delete
+.RB [\ flow\-type \ \*(NC
+.RB [ src \ \*(MA\ [ m \ \*(MA]]
+.RB [ dst \ \*(MA\ [ m \ \*(MA]]
+.BM proto
+.RB [ src\-ip \ \*(PA\ [ m \ \*(PA]]
+.RB [ dst\-ip \ \*(PA\ [ m \ \*(PA]]
+.BM tos
+.BM l4proto
+.BM src\-port
+.BM dst\-port
+.BM spi
+.BM vlan\-etype
+.BM vlan
+.BM user\-def
+.BN action
+.BN loc
+.RB ]
.
.\" Adjust lines (i.e. full justification) and hyphenate.
.ad
@@ -630,77 +623,90 @@ Default region is 0 which denotes all regions in the flash.
.TP
.B \-u \-\-show\-ntuple
Get Rx ntuple filters and actions, then display them to the user.
+.TP
+.BI rule \ N
+Retrieves the RX classification rule with the given ID.
.PD
.RE
.TP
.B \-U \-\-config\-ntuple
Configure Rx ntuple filters and actions
.TP
-.B flow\-type tcp4|udp4|sctp4|ether
+.BI delete \ N
+Deletes the RX classification rule with the given ID.
+.TP
+.B flow\-type \*(NC
.TS
nokeep;
lB l.
+ether Ethernet
+ip4 Raw IPv4
tcp4 TCP over IPv4
udp4 UDP over IPv4
sctp4 SCTP over IPv4
-ether Ethernet
+ah4 IPSEC AH over IPv4
+esp4 IPSEC ESP over IPv4
.TE
+.PP
+All fields below that include a mask option may either use "m" to
+indicate a mask, or may use the full name of the field with a "-mask"
+appended to indicate that this is the mask for a given field.
+.PD
+.RE
.TP
-.BI src\-ip \ addr
-Includes the source IP address, specified using dotted-quad notation
-or as a single 32-bit number.
-.TP
-.BI src\-ip\-mask \ mask
-Specify a mask for the source IP address.
-.TP
-.BI dst\-ip \ addr
-Includes the destination IP address.
-.TP
-.BI dst\-ip\-mask \ mask
-Specify a mask for the destination IP address.
-.TP
-.BI src\-port \ port
-Includes the source port.
-.TP
-.BI src\-port\-mask \ mask
-Specify a mask for the source port.
+.BR src \ \*(MA\ [ m \ \*(MA]
+Includes the source MAC address, specified as 6 bytes in hexadecimal
+separated by colons, along with an optional mask. Valid only for
+flow-type ether.
.TP
-.BI dst\-port \ port
-Includes the destination port.
+.BR dst \ \*(MA\ [ m \ \*(MA]
+Includes the destination MAC address, specified as 6 bytes in hexadecimal
+separated by colons, along with an optional mask. Valid only for
+flow-type ether.
.TP
-.BI dst\-port\-mask \ mask
-Specify a mask for the destination port.
+.BI proto \ N \\fR\ [\\fPm \ N \\fR]\\fP
+Includes the Ethernet protocol number (ethertype) and an optional mask.
+Valid only for flow-type ether.
.TP
-.BI src \ mac\-addr
-Includes the source MAC address, specified as 6 bytes in hexadecimal
-separated by colons.
+.BR src\-ip \ \*(PA\ [ m \ \*(PA]
+Specify the source IP address of the incoming packet to match along with
+an optional mask. Valid for all IPv4 based flow-types.
.TP
-.BI src\-mask \ mask
-Specify a mask for the source MAC address.
+.BR dst\-ip \ \*(PA\ [ m \ \*(PA]
+Specify the destination IP address of the incoming packet to match along
+with an optional mask. Valid for all IPv4 based flow-types.
.TP
-.BI dst \ mac\-addr
-Includes the destination MAC address.
+.BI tos \ N \\fR\ [\\fPm \ N \\fR]\\fP
+Specify the value of the Type of Service field in the incoming packet to
+match along with an optional mask. Applies to all IPv4 based flow-types.
.TP
-.BI dst\-mask \ mask
-Specify a mask for the destination MAC address.
+.BI l4proto \ N \\fR\ [\\fPl4m \ N \\fR]\\fP
+Includes the layer 4 protocol number and optional mask. Valid only for
+flow-type ip4.
.TP
-.BI proto \ N
-Includes the Ethernet protocol number (ethertype).
+.BI src\-port \ N \\fR\ [\\fPm \ N \\fR]\\fP
+Specify the value of the source port field (applicable to TCP/UDP packets)
+in the incoming packet to match along with an optional mask. Valid for
+flow-types ip4, tcp4, udp4, and sctp4.
.TP
-.BI proto\-mask \ mask
-Specify a mask for the Ethernet protocol number.
+.BI dst\-port \ N \\fR\ [\\fPm \ N \\fR]\\fP
+Specify the value of the destination port field (applicable to TCP/UDP
+packets)in the incoming packet to match along with an optional mask.
+Valid for flow-types ip4, tcp4, udp4, and sctp4.
.TP
-.BI vlan \ VLAN\-tag
-Includes the VLAN tag.
+.BI spi \ N \\fR\ [\\fPm \ N \\fR]\\fP
+Specify the value of the security parameter index field (applicable to
+AH/ESP packets)in the incoming packet to match along with an optional
+mask. Valid for flow-types ip4, ah4, and esp4.
.TP
-.BI vlan\-mask \ mask
-Specify a mask for the VLAN tag.
+.BI vlan\-etype \ N \\fR\ [\\fPm \ N \\fR]\\fP
+Includes the VLAN tag Ethertype and an optional mask.
.TP
-.BI user\-def \ data
-Includes 64-bits of user-specific data.
+.BI vlan \ N \\fR\ [\\fPm \ N \\fR]\\fP
+Includes the VLAN tag and an optional mask.
.TP
-.BI user\-def\-mask \ mask
-Specify a mask for the user-specific data.
+.BI user\-def \ N \\fR\ [\\fPm \ N \\fR]\\fP
+Includes 64-bits of user-specific data and an optional mask.
.TP
.BI action \ N
Specifies the Rx queue to send packets to, or some other action.
@@ -711,6 +717,11 @@ lB l.
-1 Drop the matched flow
0 or higher Rx queue to route the flow
.TE
+.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 network drivers.
.SH AUTHOR
@@ -724,7 +735,8 @@ Jakub Jelinek,
Andre Majorel,
Eli Kupermann,
Scott Feldman,
-Andi Kleen.
+Andi Kleen,
+Alexander Duyck.
.SH AVAILABILITY
.B ethtool
is available from
diff --git a/ethtool.c b/ethtool.c
index 24d4e4f..2e04d87 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 2008
* 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>
* Various features by Ben Hutchings <bhutchings@solarflare.com>;
* Copyright 2009, 2010 Solarflare Communications
*
@@ -85,14 +87,13 @@ 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 void parse_rxntupleopts(int argc, char **argp, int first_arg);
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 int do_grxfhindir(int fd, struct ifreq *ifr);
static int do_srxfhindir(int fd, struct ifreq *ifr);
-static int do_srxntuple(int fd, struct ifreq *ifr);
-static int do_grxntuple(int fd, struct ifreq *ifr);
+static int do_srxclsrule(int fd, struct ifreq *ifr);
+static int do_grxclsrule(int fd, struct ifreq *ifr);
static int do_flash(int fd, struct ifreq *ifr);
static int do_permaddr(int fd, struct ifreq *ifr);
@@ -123,8 +124,8 @@ static enum {
MODE_SNFC,
MODE_GRXFHINDIR,
MODE_SRXFHINDIR,
- MODE_SNTUPLE,
- MODE_GNTUPLE,
+ MODE_SCLSRULE,
+ MODE_GCLSRULE,
MODE_FLASHDEV,
MODE_PERMADDR,
} mode = MODE_GSET;
@@ -230,22 +231,28 @@ static struct option {
"indirection" },
{ "-X", "--set-rxfh-indir", MODE_SRXFHINDIR, "Set Rx flow hash indirection",
" equal N | weight W0 W1 ...\n" },
- { "-U", "--config-ntuple", MODE_SNTUPLE, "Configure Rx ntuple filters "
+ { "-U", "--config-ntuple", MODE_SCLSRULE, "Configure Rx ntuple filters "
"and actions",
- " { flow-type tcp4|udp4|sctp4\n"
- " [ src-ip ADDR [src-ip-mask MASK] ]\n"
- " [ dst-ip ADDR [dst-ip-mask MASK] ]\n"
- " [ src-port PORT [src-port-mask MASK] ]\n"
- " [ dst-port PORT [dst-port-mask MASK] ]\n"
- " | flow-type ether\n"
- " [ src MAC-ADDR [src-mask MASK] ]\n"
- " [ dst MAC-ADDR [dst-mask MASK] ]\n"
- " [ proto N [proto-mask MASK] ] }\n"
- " [ vlan VLAN-TAG [vlan-mask MASK] ]\n"
- " [ user-def DATA [user-def-mask MASK] ]\n"
- " action N\n" },
- { "-u", "--show-ntuple", MODE_GNTUPLE,
- "Get Rx ntuple filters and actions\n" },
+ " [ delete %d ] |\n"
+ " [ flow-type ether|ip4|tcp4|udp4|sctp4|ah4|esp4\n"
+ " [ src %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n"
+ " [ dst %x:%x:%x:%x:%x:%x [m %x:%x:%x:%x:%x:%x] ]\n"
+ " [ proto %d [m %x] ]\n"
+ " [ src-ip %d.%d.%d.%d [m %d.%d.%d.%d] ]\n"
+ " [ dst-ip %d.%d.%d.%d [m %d.%d.%d.%d] ]\n"
+ " [ tos %d [m %x] ]\n"
+ " [ l4proto %d [m %x] ]\n"
+ " [ src-port %d [m %x] ]\n"
+ " [ dst-port %d [m %x] ]\n"
+ " [ spi %d [m %x] ]\n"
+ " [ vlan-etype %x [m %x] ]\n"
+ " [ vlan %x [m %x] ]\n"
+ " [ user-def %x [m %x] ]\n"
+ " [ action %d ]\n"
+ " [ loc %d]]\n" },
+ { "-u", "--show-ntuple", MODE_GCLSRULE,
+ "Get Rx ntuple filters and actions",
+ " [ rule %d ]\n"},
{ "-P", "--show-permaddr", MODE_PERMADDR,
"Show permanent hardware address" },
{ "-h", "--help", MODE_HELP, "Show this help" },
@@ -371,26 +378,6 @@ static u32 rx_fhash_val = 0;
static int rx_fhash_changed = 0;
static int rxfhindir_equal = 0;
static char **rxfhindir_weight = NULL;
-static int sntuple_changed = 0;
-static struct ethtool_rx_ntuple_flow_spec ntuple_fs;
-static int ntuple_ip4src_seen = 0;
-static int ntuple_ip4src_mask_seen = 0;
-static int ntuple_ip4dst_seen = 0;
-static int ntuple_ip4dst_mask_seen = 0;
-static int ntuple_psrc_seen = 0;
-static int ntuple_psrc_mask_seen = 0;
-static int ntuple_pdst_seen = 0;
-static int ntuple_pdst_mask_seen = 0;
-static int ntuple_ether_dst_seen = 0;
-static int ntuple_ether_dst_mask_seen = 0;
-static int ntuple_ether_src_seen = 0;
-static int ntuple_ether_src_mask_seen = 0;
-static int ntuple_ether_proto_seen = 0;
-static int ntuple_ether_proto_mask_seen = 0;
-static int ntuple_vlan_tag_seen = 0;
-static int ntuple_vlan_tag_mask_seen = 0;
-static int ntuple_user_def_seen = 0;
-static int ntuple_user_def_mask_seen = 0;
static char *flash_file = NULL;
static int flash = -1;
static int flash_region = -1;
@@ -399,6 +386,11 @@ static int msglvl_changed;
static u32 msglvl_wanted = 0;
static u32 msglvl_mask = 0;
+static int rx_class_rule_get = -1;
+static int rx_class_rule_del = -1;
+static int rx_class_rule_added = 0;
+static struct ethtool_rx_flow_spec rx_rule_fs;
+
static enum {
ONLINE=0,
OFFLINE,
@@ -511,58 +503,6 @@ static struct cmdline_info cmdline_coalesce[] = {
{ "tx-frames-high", CMDL_S32, &coal_tx_frames_high_wanted, &ecoal.tx_max_coalesced_frames_high },
};
-static struct cmdline_info cmdline_ntuple_tcp_ip4[] = {
- { "src-ip", CMDL_IP4, &ntuple_fs.h_u.tcp_ip4_spec.ip4src, NULL,
- 0, &ntuple_ip4src_seen },
- { "src-ip-mask", CMDL_IP4, &ntuple_fs.m_u.tcp_ip4_spec.ip4src, NULL,
- 0, &ntuple_ip4src_mask_seen },
- { "dst-ip", CMDL_IP4, &ntuple_fs.h_u.tcp_ip4_spec.ip4dst, NULL,
- 0, &ntuple_ip4dst_seen },
- { "dst-ip-mask", CMDL_IP4, &ntuple_fs.m_u.tcp_ip4_spec.ip4dst, NULL,
- 0, &ntuple_ip4dst_mask_seen },
- { "src-port", CMDL_BE16, &ntuple_fs.h_u.tcp_ip4_spec.psrc, NULL,
- 0, &ntuple_psrc_seen },
- { "src-port-mask", CMDL_BE16, &ntuple_fs.m_u.tcp_ip4_spec.psrc, NULL,
- 0, &ntuple_psrc_mask_seen },
- { "dst-port", CMDL_BE16, &ntuple_fs.h_u.tcp_ip4_spec.pdst, NULL,
- 0, &ntuple_pdst_seen },
- { "dst-port-mask", CMDL_BE16, &ntuple_fs.m_u.tcp_ip4_spec.pdst, NULL,
- 0, &ntuple_pdst_mask_seen },
- { "vlan", CMDL_U16, &ntuple_fs.vlan_tag, NULL,
- 0, &ntuple_vlan_tag_seen },
- { "vlan-mask", CMDL_U16, &ntuple_fs.vlan_tag_mask, NULL,
- 0, &ntuple_vlan_tag_mask_seen },
- { "user-def", CMDL_U64, &ntuple_fs.data, NULL,
- 0, &ntuple_user_def_seen },
- { "user-def-mask", CMDL_U64, &ntuple_fs.data_mask, NULL,
- 0, &ntuple_user_def_mask_seen },
- { "action", CMDL_S32, &ntuple_fs.action, NULL },
-};
-
-static struct cmdline_info cmdline_ntuple_ether[] = {
- { "dst", CMDL_MAC, ntuple_fs.h_u.ether_spec.h_dest, NULL,
- 0, &ntuple_ether_dst_seen },
- { "dst-mask", CMDL_MAC, ntuple_fs.m_u.ether_spec.h_dest, NULL,
- 0, &ntuple_ether_dst_mask_seen },
- { "src", CMDL_MAC, ntuple_fs.h_u.ether_spec.h_source, NULL,
- 0, &ntuple_ether_src_seen },
- { "src-mask", CMDL_MAC, ntuple_fs.m_u.ether_spec.h_source, NULL,
- 0, &ntuple_ether_src_mask_seen },
- { "proto", CMDL_BE16, &ntuple_fs.h_u.ether_spec.h_proto, NULL,
- 0, &ntuple_ether_proto_seen },
- { "proto-mask", CMDL_BE16, &ntuple_fs.m_u.ether_spec.h_proto, NULL,
- 0, &ntuple_ether_proto_mask_seen },
- { "vlan", CMDL_U16, &ntuple_fs.vlan_tag, NULL,
- 0, &ntuple_vlan_tag_seen },
- { "vlan-mask", CMDL_U16, &ntuple_fs.vlan_tag_mask, NULL,
- 0, &ntuple_vlan_tag_mask_seen },
- { "user-def", CMDL_U64, &ntuple_fs.data, NULL,
- 0, &ntuple_user_def_seen },
- { "user-def-mask", CMDL_U64, &ntuple_fs.data_mask, NULL,
- 0, &ntuple_user_def_mask_seen },
- { "action", CMDL_S32, &ntuple_fs.action, NULL },
-};
-
static struct cmdline_info cmdline_msglvl[] = {
{ "drv", CMDL_FLAG, &msglvl_wanted, NULL,
NETIF_MSG_DRV, &msglvl_mask },
@@ -833,8 +773,8 @@ static void parse_cmdline(int argc, char **argp)
(mode == MODE_SNFC) ||
(mode == MODE_GRXFHINDIR) ||
(mode == MODE_SRXFHINDIR) ||
- (mode == MODE_SNTUPLE) ||
- (mode == MODE_GNTUPLE) ||
+ (mode == MODE_SCLSRULE) ||
+ (mode == MODE_GCLSRULE) ||
(mode == MODE_PHYS_ID) ||
(mode == MODE_FLASHDEV) ||
(mode == MODE_PERMADDR)) {
@@ -918,16 +858,45 @@ static void parse_cmdline(int argc, char **argp)
i = argc;
break;
}
- if (mode == MODE_SNTUPLE) {
+ if (mode == MODE_SCLSRULE) {
if (!strcmp(argp[i], "flow-type")) {
i += 1;
if (i >= argc) {
exit_bad_args();
break;
}
- parse_rxntupleopts(argc, argp, i);
- i = argc;
- break;
+ if (rxclass_parse_ruleopts(&argp[i],
+ argc - i,
+ &rx_rule_fs) < 0) {
+ exit_bad_args();
+ } else {
+ i = argc;
+ rx_class_rule_added = 1;
+ }
+ } else if (!strcmp(argp[i], "delete")) {
+ i += 1;
+ if (i >= argc) {
+ exit_bad_args();
+ break;
+ }
+ rx_class_rule_del =
+ get_uint_range(argp[i], 0,
+ INT_MAX);
+ } else {
+ exit_bad_args();
+ }
+ break;
+ }
+ if (mode == MODE_GCLSRULE) {
+ if (!strcmp(argp[i], "rule")) {
+ i += 1;
+ if (i >= argc) {
+ exit_bad_args();
+ break;
+ }
+ rx_class_rule_get =
+ get_uint_range(argp[i], 0,
+ INT_MAX);
} else {
exit_bad_args();
}
@@ -1598,66 +1567,6 @@ static char *unparse_rxfhashopts(u64 opts)
return buf;
}
-static void parse_rxntupleopts(int argc, char **argp, int i)
-{
- ntuple_fs.flow_type = rxflow_str_to_type(argp[i]);
-
- switch (ntuple_fs.flow_type) {
- case TCP_V4_FLOW:
- case UDP_V4_FLOW:
- case SCTP_V4_FLOW:
- parse_generic_cmdline(argc, argp, i + 1,
- &sntuple_changed,
- cmdline_ntuple_tcp_ip4,
- ARRAY_SIZE(cmdline_ntuple_tcp_ip4));
- if (!ntuple_ip4src_seen)
- ntuple_fs.m_u.tcp_ip4_spec.ip4src = 0xffffffff;
- if (!ntuple_ip4dst_seen)
- ntuple_fs.m_u.tcp_ip4_spec.ip4dst = 0xffffffff;
- if (!ntuple_psrc_seen)
- ntuple_fs.m_u.tcp_ip4_spec.psrc = 0xffff;
- if (!ntuple_pdst_seen)
- ntuple_fs.m_u.tcp_ip4_spec.pdst = 0xffff;
- ntuple_fs.m_u.tcp_ip4_spec.tos = 0xff;
- break;
- case ETHER_FLOW:
- parse_generic_cmdline(argc, argp, i + 1,
- &sntuple_changed,
- cmdline_ntuple_ether,
- ARRAY_SIZE(cmdline_ntuple_ether));
- if (!ntuple_ether_dst_seen)
- memset(ntuple_fs.m_u.ether_spec.h_dest, 0xff, ETH_ALEN);
- if (!ntuple_ether_src_seen)
- memset(ntuple_fs.m_u.ether_spec.h_source, 0xff,
- ETH_ALEN);
- if (!ntuple_ether_proto_seen)
- ntuple_fs.m_u.ether_spec.h_proto = 0xffff;
- break;
- default:
- fprintf(stderr, "Unsupported flow type \"%s\"\n", argp[i]);
- exit(106);
- break;
- }
-
- if (!ntuple_vlan_tag_seen)
- ntuple_fs.vlan_tag_mask = 0xffff;
- if (!ntuple_user_def_seen)
- ntuple_fs.data_mask = 0xffffffffffffffffULL;
-
- if ((ntuple_ip4src_mask_seen && !ntuple_ip4src_seen) ||
- (ntuple_ip4dst_mask_seen && !ntuple_ip4dst_seen) ||
- (ntuple_psrc_mask_seen && !ntuple_psrc_seen) ||
- (ntuple_pdst_mask_seen && !ntuple_pdst_seen) ||
- (ntuple_ether_dst_mask_seen && !ntuple_ether_dst_seen) ||
- (ntuple_ether_src_mask_seen && !ntuple_ether_src_seen) ||
- (ntuple_ether_proto_mask_seen && !ntuple_ether_proto_seen) ||
- (ntuple_vlan_tag_mask_seen && !ntuple_vlan_tag_seen) ||
- (ntuple_user_def_mask_seen && !ntuple_user_def_seen)) {
- fprintf(stderr, "Cannot specify mask without value\n");
- exit(107);
- }
-}
-
static struct {
const char *name;
int (*func)(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
@@ -2019,10 +1928,10 @@ static int doit(void)
return do_grxfhindir(fd, &ifr);
} else if (mode == MODE_SRXFHINDIR) {
return do_srxfhindir(fd, &ifr);
- } else if (mode == MODE_SNTUPLE) {
- return do_srxntuple(fd, &ifr);
- } else if (mode == MODE_GNTUPLE) {
- return do_grxntuple(fd, &ifr);
+ } else if (mode == MODE_SCLSRULE) {
+ return do_srxclsrule(fd, &ifr);
+ } else if (mode == MODE_GCLSRULE) {
+ return do_grxclsrule(fd, &ifr);
} else if (mode == MODE_FLASHDEV) {
return do_flash(fd, &ifr);
} else if (mode == MODE_PERMADDR) {
@@ -3155,21 +3064,143 @@ static int do_permaddr(int fd, struct ifreq *ifr)
return err;
}
+static int flow_spec_to_ntuple(struct ethtool_rx_flow_spec *fsp,
+ struct ethtool_rx_ntuple_flow_spec *ntuple)
+{
+ size_t i;
+
+ /* verify location is not specified */
+ if (fsp->location != RX_CLS_LOC_UNSPEC)
+ return -1;
+
+ /* verify ring cookie can transfer to action */
+ if (fsp->ring_cookie > INT_MAX && fsp->ring_cookie < (u64)(-2))
+ return -1;
+
+ /* verify only one field is setting data field */
+ if ((fsp->flow_type & FLOW_EXT) &&
+ (fsp->m_ext.data[0] || fsp->m_ext.data[1]) &&
+ fsp->m_ext.vlan_etype)
+ return -1;
+
+ /* Set entire ntuple to ~0 to guarantee all masks are set */
+ memset(ntuple, ~0, sizeof(*ntuple));
+
+ /* set non-filter values */
+ ntuple->flow_type = fsp->flow_type;
+ ntuple->action = fsp->ring_cookie;
+
+ /*
+ * Copy over header union, they are identical in layout however
+ * the ntuple union contains additional padding on the end
+ */
+ memcpy(&ntuple->h_u, &fsp->h_u, sizeof(fsp->h_u));
+
+ /*
+ * The same rule mentioned above applies to the mask union. However,
+ * in addition we need to invert the mask bits to match the ntuple
+ * mask which is 1 for masked, versus 0 for masked as seen in nfc.
+ */
+ memcpy(&ntuple->m_u, &fsp->m_u, sizeof(fsp->m_u));
+ for (i = 0; i < sizeof(fsp->m_u); i++)
+ ntuple->m_u.hdata[i] ^= 0xFF;
+
+ /* copy extended fields */
+ if (fsp->flow_type & FLOW_EXT) {
+ ntuple->vlan_tag =
+ ntohs(fsp->h_ext.vlan_tci);
+ ntuple->vlan_tag_mask =
+ ~ntohs(fsp->m_ext.vlan_tci);
+ if (fsp->m_ext.vlan_etype) {
+ /*
+ * vlan_etype and user data are mutually exclusive
+ * in ntuple configuration as they occupy the same
+ * space.
+ */
+ if (fsp->m_ext.data[0] || fsp->m_ext.data[1])
+ return -1;
+ ntuple->data =
+ ntohl(fsp->h_ext.vlan_etype);
+ ntuple->data_mask =
+ ~(u64)ntohl(fsp->m_ext.vlan_etype);
+ } else {
+ ntuple->data =
+ (u64)ntohl(fsp->h_ext.data[0]) << 32;
+ ntuple->data |=
+ (u64)ntohl(fsp->h_ext.data[1]);
+ ntuple->data_mask =
+ (u64)ntohl(~fsp->m_ext.data[0]) << 32;
+ ntuple->data_mask |=
+ (u64)ntohl(~fsp->m_ext.data[1]);
+ }
+ }
+
+ return 0;
+}
+
static int do_srxntuple(int fd, struct ifreq *ifr)
{
+ struct ethtool_rx_ntuple ntuplecmd;
+ struct ethtool_value eval;
int err;
- if (sntuple_changed) {
- struct ethtool_rx_ntuple ntuplecmd;
+ /* attempt to convert the flow classifier to an ntuple classifier */
+ err = flow_spec_to_ntuple(&rx_rule_fs, &ntuplecmd.fs);
+ if (err)
+ return -1;
- ntuplecmd.cmd = ETHTOOL_SRXNTUPLE;
- memcpy(&ntuplecmd.fs, &ntuple_fs,
- sizeof(struct ethtool_rx_ntuple_flow_spec));
+ /*
+ * Check to see if the flag is set for N-tuple, this allows
+ * us to avoid the possible EINVAL response for the N-tuple
+ * flag not being set on the device
+ */
+ eval.cmd = ETHTOOL_GFLAGS;
+ ifr->ifr_data = (caddr_t)&eval;
+ err = ioctl(fd, SIOCETHTOOL, ifr);
+ if (err || !(eval.data & ETH_FLAG_NTUPLE))
+ return -1;
- ifr->ifr_data = (caddr_t)&ntuplecmd;
- err = ioctl(fd, SIOCETHTOOL, ifr);
- if (err < 0)
- perror("Cannot add new RX n-tuple filter");
+ /* send rule via N-tuple */
+ ntuplecmd.cmd = ETHTOOL_SRXNTUPLE;
+ ifr->ifr_data = (caddr_t)&ntuplecmd;
+ err = ioctl(fd, SIOCETHTOOL, ifr);
+
+ /*
+ * Display error only if reponse is something other than op not
+ * supported. It is possible that the interface uses the network
+ * flow classifier interface instead of N-tuple.
+ */
+ if (err && errno != EOPNOTSUPP)
+ perror("Cannot add new rule via N-tuple");
+
+ return err;
+}
+
+static int do_srxclsrule(int fd, struct ifreq *ifr)
+{
+ int err;
+
+ if (rx_class_rule_added) {
+ /* attempt to add rule via N-tuple specifier */
+ err = do_srxntuple(fd, ifr);
+ if (!err)
+ return 0;
+
+ /* attempt to add rule via network flow classifier */
+ err = rxclass_rule_ins(fd, ifr, &rx_rule_fs);
+ if (err < 0) {
+ fprintf(stderr, "Cannot insert"
+ " classification rule\n");
+ return 1;
+ }
+ } else if (rx_class_rule_del >= 0) {
+ err = rxclass_rule_del(fd, ifr, rx_class_rule_del);
+
+ if (err < 0) {
+ fprintf(stderr, "Cannot delete"
+ " classification rule\n");
+ return 1;
+ }
} else {
exit_bad_args();
}
@@ -3177,9 +3208,32 @@ static int do_srxntuple(int fd, struct ifreq *ifr)
return 0;
}
-static int do_grxntuple(int fd, struct ifreq *ifr)
+static int do_grxclsrule(int fd, struct ifreq *ifr)
{
- return 0;
+ struct ethtool_rxnfc nfccmd;
+ int err;
+
+ if (rx_class_rule_get >= 0) {
+ err = rxclass_rule_get(fd, ifr, rx_class_rule_get);
+ if (err < 0)
+ fprintf(stderr, "Cannot get RX classification rule\n");
+ return err ? 1 : 0;
+ }
+
+ 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);
+
+ err = rxclass_rule_getall(fd, ifr);
+ if (err < 0)
+ fprintf(stderr, "RX classification rule retrieval failed\n");
+
+ return err ? 1 : 0;
}
static int send_ioctl(int fd, struct ifreq *ifr)
diff --git a/rxclass.c b/rxclass.c
new file mode 100644
index 0000000..1e6b2a6
--- /dev/null
+++ b/rxclass.c
@@ -0,0 +1,1039 @@
+/*
+ * Copyright (C) 2008 Sun Microsystems, Inc. All rights reserved.
+ */
+#include <stdio.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <linux/sockios.h>
+#include <arpa/inet.h>
+#include "ethtool-util.h"
+#include "ethtool-bitops.h"
+
+/*
+ * 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.
+ */
+
+struct rmgr_ctrl {
+ /* slot contains a bitmap indicating which filters are valid */
+ unsigned long *slot;
+ __u32 n_rules;
+ __u32 size;
+};
+
+static struct rmgr_ctrl rmgr;
+static int rmgr_init_done = 0;
+
+static void invert_flow_mask(struct ethtool_rx_flow_spec *fsp)
+{
+ size_t i;
+
+ for (i = 0; i < sizeof(fsp->m_u); i++)
+ fsp->m_u.hdata[i] ^= 0xFF;
+}
+
+static void rmgr_print_ipv4_rule(__be32 sip, __be32 sipm, __be32 dip,
+ __be32 dipm, u8 tos, u8 tosm)
+{
+ char sip_str[INET_ADDRSTRLEN];
+ char sipm_str[INET_ADDRSTRLEN];
+ char dip_str[INET_ADDRSTRLEN];
+ char dipm_str[INET_ADDRSTRLEN];
+
+ fprintf(stdout,
+ "\tSrc IP addr: %s mask: %s\n"
+ "\tDest IP addr: %s mask: %s\n"
+ "\tTOS: 0x%x mask: 0x%x\n",
+ inet_ntop(AF_INET, &sip, sip_str, INET_ADDRSTRLEN),
+ inet_ntop(AF_INET, &sipm, sipm_str, INET_ADDRSTRLEN),
+ inet_ntop(AF_INET, &dip, dip_str, INET_ADDRSTRLEN),
+ inet_ntop(AF_INET, &dipm, dipm_str, INET_ADDRSTRLEN),
+ tos, tosm);
+}
+
+static void rmgr_print_nfc_spec_ext(struct ethtool_rx_flow_spec *fsp)
+{
+ u64 data, datam;
+ __u16 etype, etypem, tci, tcim;
+
+ if (!(fsp->flow_type & FLOW_EXT))
+ return;
+
+ etype = ntohs(fsp->h_ext.vlan_etype);
+ etypem = ntohs(~fsp->m_ext.vlan_etype);
+ tci = ntohs(fsp->h_ext.vlan_tci);
+ tcim = ntohs(~fsp->m_ext.vlan_tci);
+ data = (u64)ntohl(fsp->h_ext.data[0]) << 32;
+ data = (u64)ntohl(fsp->h_ext.data[1]);
+ datam = (u64)ntohl(~fsp->m_ext.data[0]) << 32;
+ datam |= (u64)ntohl(~fsp->m_ext.data[1]);
+
+ fprintf(stdout,
+ "\tVLAN EtherType: 0x%x mask: 0x%x\n"
+ "\tVLAN: 0x%x mask: 0x%x\n"
+ "\tUser-defined: 0x%llx mask: 0x%llx\n",
+ etype, etypem, tci, tcim, data, datam);
+}
+
+static void rmgr_print_nfc_rule(struct ethtool_rx_flow_spec *fsp)
+{
+ unsigned char *smac, *smacm, *dmac, *dmacm;
+ __u32 flow_type;
+
+ if (fsp->location != RX_CLS_LOC_UNSPEC)
+ fprintf(stdout, "Filter: %d\n", fsp->location);
+ else
+ fprintf(stdout, "Filter: Unspecified\n");
+
+ flow_type = fsp->flow_type & ~FLOW_EXT;
+
+ invert_flow_mask(fsp);
+
+ switch (flow_type) {
+ case TCP_V4_FLOW:
+ case UDP_V4_FLOW:
+ case SCTP_V4_FLOW:
+ if (flow_type == TCP_V4_FLOW)
+ fprintf(stdout, "\tRule Type: TCP over IPv4\n");
+ else if (flow_type == UDP_V4_FLOW)
+ fprintf(stdout, "\tRule Type: UDP over IPv4\n");
+ else
+ fprintf(stdout, "\tRule Type: SCTP over IPv4\n");
+ rmgr_print_ipv4_rule(fsp->h_u.tcp_ip4_spec.ip4src,
+ fsp->m_u.tcp_ip4_spec.ip4src,
+ fsp->h_u.tcp_ip4_spec.ip4dst,
+ fsp->m_u.tcp_ip4_spec.ip4dst,
+ fsp->h_u.tcp_ip4_spec.tos,
+ fsp->m_u.tcp_ip4_spec.tos);
+ fprintf(stdout,
+ "\tSrc port: %d mask: 0x%x\n"
+ "\tDest port: %d mask: 0x%x\n",
+ ntohs(fsp->h_u.tcp_ip4_spec.psrc),
+ ntohs(fsp->m_u.tcp_ip4_spec.psrc),
+ ntohs(fsp->h_u.tcp_ip4_spec.pdst),
+ ntohs(fsp->m_u.tcp_ip4_spec.pdst));
+ break;
+ case AH_V4_FLOW:
+ case ESP_V4_FLOW:
+ if (flow_type == AH_V4_FLOW)
+ fprintf(stdout, "\tRule Type: IPSEC AH over IPv4\n");
+ else
+ fprintf(stdout, "\tRule Type: IPSEC ESP over IPv4\n");
+ rmgr_print_ipv4_rule(fsp->h_u.ah_ip4_spec.ip4src,
+ fsp->m_u.ah_ip4_spec.ip4src,
+ fsp->h_u.ah_ip4_spec.ip4dst,
+ fsp->m_u.ah_ip4_spec.ip4dst,
+ fsp->h_u.ah_ip4_spec.tos,
+ fsp->m_u.ah_ip4_spec.tos);
+ fprintf(stdout,
+ "\tSPI: %d mask: 0x%x\n",
+ ntohl(fsp->h_u.esp_ip4_spec.spi),
+ ntohl(fsp->m_u.esp_ip4_spec.spi));
+ break;
+ case IP_USER_FLOW:
+ fprintf(stdout, "\tRule Type: Raw IPv4\n");
+ rmgr_print_ipv4_rule(fsp->h_u.usr_ip4_spec.ip4src,
+ fsp->m_u.usr_ip4_spec.ip4src,
+ fsp->h_u.usr_ip4_spec.ip4dst,
+ fsp->m_u.usr_ip4_spec.ip4dst,
+ fsp->h_u.usr_ip4_spec.tos,
+ fsp->m_u.usr_ip4_spec.tos);
+ fprintf(stdout,
+ "\tProtocol: %d mask: 0x%x\n"
+ "\tL4 bytes: 0x%x mask: 0x%x\n",
+ fsp->h_u.usr_ip4_spec.proto,
+ fsp->m_u.usr_ip4_spec.proto,
+ ntohl(fsp->h_u.usr_ip4_spec.l4_4_bytes),
+ ntohl(fsp->m_u.usr_ip4_spec.l4_4_bytes));
+ break;
+ case ETHER_FLOW:
+ dmac = fsp->h_u.ether_spec.h_dest;
+ dmacm = fsp->m_u.ether_spec.h_dest;
+ smac = fsp->h_u.ether_spec.h_source;
+ smacm = fsp->m_u.ether_spec.h_source;
+
+ fprintf(stdout,
+ "\tFlow Type: Raw Ethernet\n"
+ "\tSrc MAC addr: %02X:%02X:%02X:%02X:%02X:%02X"
+ " mask: %02X:%02X:%02X:%02X:%02X:%02X\n"
+ "\tDest MAC addr: %02X:%02X:%02X:%02X:%02X:%02X"
+ " mask: %02X:%02X:%02X:%02X:%02X:%02X\n"
+ "\tEthertype: 0x%X mask: 0x%X\n",
+ smac[0], smac[1], smac[2], smac[3], smac[4], smac[5],
+ smacm[0], smacm[1], smacm[2], smacm[3], smacm[4],
+ smacm[5], dmac[0], dmac[1], dmac[2], dmac[3], dmac[4],
+ dmac[5], dmacm[0], dmacm[1], dmacm[2], dmacm[3],
+ dmacm[4], dmacm[5],
+ ntohs(fsp->h_u.ether_spec.h_proto),
+ ntohs(fsp->m_u.ether_spec.h_proto));
+ break;
+ default:
+ fprintf(stdout,
+ "\tUnknown Flow type: %d\n", flow_type);
+ break;
+ }
+
+ rmgr_print_nfc_spec_ext(fsp);
+
+ if (fsp->ring_cookie != RX_CLS_FLOW_DISC)
+ fprintf(stdout, "\tAction: Direct to queue %llu\n",
+ fsp->ring_cookie);
+ else
+ fprintf(stdout, "\tAction: Drop\n");
+
+ fprintf(stdout, "\n");
+}
+
+static void rmgr_print_rule(struct ethtool_rx_flow_spec *fsp)
+{
+ /* print the rule in this location */
+ switch (fsp->flow_type & ~FLOW_EXT) {
+ case TCP_V4_FLOW:
+ case UDP_V4_FLOW:
+ case SCTP_V4_FLOW:
+ case AH_V4_FLOW:
+ case ESP_V4_FLOW:
+ case ETHER_FLOW:
+ rmgr_print_nfc_rule(fsp);
+ break;
+ case IP_USER_FLOW:
+ if (fsp->h_u.usr_ip4_spec.ip_ver == ETH_RX_NFC_IP4) {
+ rmgr_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");
+ break;
+ default:
+ fprintf(stderr, "rmgr: Unknown flow type\n");
+ break;
+ }
+}
+
+static int rmgr_ins(__u32 loc)
+{
+ /* verify location is in rule manager range */
+ if (loc >= rmgr.size) {
+ fprintf(stderr, "rmgr: Location out of range\n");
+ return -1;
+ }
+
+ /* set bit for the rule */
+ set_bit(loc, rmgr.slot);
+
+ return 0;
+}
+
+static int rmgr_add(struct ethtool_rx_flow_spec *fsp)
+{
+ __u32 loc = fsp->location;
+ __u32 slot_num;
+
+ /* start at the end of the list since it is lowest priority */
+ loc = rmgr.size - 1;
+
+ /* locate the first slot a rule can be placed in */
+ slot_num = loc / BITS_PER_LONG;
+
+ /*
+ * Avoid testing individual bits by inverting the word and checking
+ * to see if any bits are left set, if so there are empty spots. By
+ * moving 1 + loc % BITS_PER_LONG we align ourselves to the last bit
+ * in the previous word.
+ *
+ * If loc rolls over it should be greater than or equal to rmgr.size
+ * and as such we know we have reached the end of the list.
+ */
+ if (!~(rmgr.slot[slot_num] | (~1UL << rmgr.size % BITS_PER_LONG))) {
+ loc -= 1 + (loc % BITS_PER_LONG);
+ slot_num--;
+ }
+
+ /*
+ * Now that we are aligned with the last bit in each long we can just
+ * go though and eliminate all the longs with no free bits
+ */
+ while (loc < rmgr.size && !~(rmgr.slot[slot_num])) {
+ loc -= BITS_PER_LONG;
+ slot_num--;
+ }
+
+ /*
+ * If we still are inside the range, test individual bits as one is
+ * likely available for our use.
+ */
+ while (loc < rmgr.size && test_bit(loc, rmgr.slot))
+ loc--;
+
+ /* location found, insert rule */
+ if (loc < rmgr.size) {
+ fsp->location = loc;
+ return rmgr_ins(loc);
+ }
+
+ /* No space to add this rule */
+ fprintf(stderr, "rmgr: Cannot find appropriate slot to insert rule\n");
+
+ return -1;
+}
+
+static int rmgr_init(int fd, struct ifreq *ifr)
+{
+ struct ethtool_rxnfc *nfccmd;
+ int err, i;
+ __u32 *rule_locs;
+
+ if (rmgr_init_done)
+ return 0;
+
+ /* clear rule manager settings */
+ memset(&rmgr, 0, sizeof(struct rmgr_ctrl));
+
+ /* allocate memory for count request */
+ nfccmd = calloc(1, sizeof(*nfccmd));
+ if (!nfccmd) {
+ perror("rmgr: Cannot allocate memory for RX class rule data");
+ return -1;
+ }
+
+ /* request count and store in rmgr.n_rules */
+ 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;
+ }
+
+ /* alloc memory for request of location list */
+ nfccmd = calloc(1, sizeof(*nfccmd) + (rmgr.n_rules * sizeof(__u32)));
+ if (!nfccmd) {
+ perror("rmgr: Cannot allocate memory for"
+ " RX class rule locations");
+ return -1;
+ }
+
+ /* request location list */
+ 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;
+ }
+
+ /* make certain the table size is valid */
+ rmgr.size = nfccmd->data;
+ if (rmgr.size == 0 || rmgr.size < rmgr.n_rules) {
+ perror("rmgr: Invalid RX class rules table size");
+ return -1;
+ }
+
+ /* initialize bitmap for storage of valid locations */
+ rmgr.slot = calloc(1, BITS_TO_LONGS(rmgr.size) * sizeof(long));
+ if (!rmgr.slot) {
+ perror("rmgr: Cannot allocate memory for RX class rules");
+ return -1;
+ }
+
+ /* write locations to bitmap */
+ rule_locs = nfccmd->rule_locs;
+ for (i = 0; i < rmgr.n_rules; i++) {
+ err = rmgr_ins(rule_locs[i]);
+ if (err < 0)
+ break;
+ }
+
+ /* free memory and set flag to avoid reinit */
+ free(nfccmd);
+ rmgr_init_done = 1;
+
+ return err;
+}
+
+static void rmgr_cleanup(void)
+{
+ if (!rmgr_init_done)
+ return;
+
+ rmgr_init_done = 0;
+
+ free(rmgr.slot);
+ rmgr.slot = NULL;
+ rmgr.size = 0;
+}
+
+int rxclass_rule_get(int fd, struct ifreq *ifr, __u32 loc)
+{
+ struct ethtool_rxnfc nfccmd;
+
+ /* fetch rule from netdev and display */
+ nfccmd.cmd = ETHTOOL_GRXCLSRULE;
+ memset(&nfccmd.fs, 0, sizeof(struct ethtool_rx_flow_spec));
+ nfccmd.fs.location = loc;
+ ifr->ifr_data = (caddr_t)&nfccmd;
+ if (ioctl(fd, SIOCETHTOOL, ifr) < 0) {
+ perror("rmgr: Cannot get RX class rule");
+ return -1;
+ }
+
+ rmgr_print_rule(&nfccmd.fs);
+ return 0;
+}
+
+int rxclass_rule_getall(int fd, struct ifreq *ifr)
+{
+ int err, i, j;
+
+ /* init table of available rules */
+ err = rmgr_init(fd, ifr);
+ if (err < 0)
+ return err;
+
+ fprintf(stdout, "Total %d rules\n\n", rmgr.n_rules);
+
+ /* fetch and display all available rules */
+ for (i = 0; i < rmgr.size; i += BITS_PER_LONG) {
+ if (!rmgr.slot[i / BITS_PER_LONG])
+ continue;
+ for (j = 0; j < BITS_PER_LONG; j++) {
+ if (!test_bit(i + j, rmgr.slot))
+ continue;
+ err = rxclass_rule_get(fd, ifr, i + j);
+ if (err < 0) {
+ rmgr_cleanup();
+ return err;
+ }
+ }
+ }
+
+ rmgr_cleanup();
+ return 0;
+}
+
+int rxclass_rule_ins(int fd, struct ifreq *ifr,
+ struct ethtool_rx_flow_spec *fsp)
+{
+ struct ethtool_rxnfc nfccmd;
+ __u32 loc = fsp->location;
+ int err;
+
+ /*
+ * if location is unspecified pull rules from device
+ * and allocate a free rule for our use
+ */
+ if (loc == RX_CLS_LOC_UNSPEC) {
+ /* init table of available rules */
+ err = rmgr_init(fd, ifr);
+ if (err < 0)
+ return err;
+
+ /* verify rule location */
+ err = rmgr_add(fsp);
+ if (err < 0)
+ return err;
+
+ /* cleanup table and free resources */
+ rmgr_cleanup();
+ }
+
+ /* notify netdev of new rule */
+ 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");
+ else if (loc == RX_CLS_LOC_UNSPEC)
+ printf("Added rule with ID %d\n", fsp->location);
+
+ return 0;
+}
+
+int rxclass_rule_del(int fd, struct ifreq *ifr, __u32 loc)
+{
+ struct ethtool_rxnfc nfccmd;
+ int err;
+
+ /* notify netdev of rule removal */
+ 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 err;
+}
+
+typedef enum {
+ OPT_NONE = 0,
+ OPT_S32,
+ OPT_U8,
+ OPT_U16,
+ OPT_U32,
+ OPT_U64,
+ OPT_BE16,
+ OPT_BE32,
+ OPT_BE64,
+ OPT_IP4,
+ OPT_MAC,
+} rule_opt_type_t;
+
+#define NFC_FLAG_RING 0x001
+#define NFC_FLAG_LOC 0x002
+#define NFC_FLAG_SADDR 0x004
+#define NFC_FLAG_DADDR 0x008
+#define NFC_FLAG_SPORT 0x010
+#define NFC_FLAG_DPORT 0x020
+#define NFC_FLAG_SPI 0x030
+#define NFC_FLAG_TOS 0x040
+#define NFC_FLAG_PROTO 0x080
+#define NTUPLE_FLAG_VLAN 0x100
+#define NTUPLE_FLAG_UDEF 0x200
+#define NTUPLE_FLAG_VETH 0x400
+
+struct rule_opts {
+ const char *name;
+ rule_opt_type_t type;
+ u32 flag;
+ int offset;
+ int moffset;
+};
+
+static struct rule_opts rule_nfc_tcp_ip4[] = {
+ { "src-ip", OPT_IP4, NFC_FLAG_SADDR,
+ offsetof(struct ethtool_rx_flow_spec, h_u.tcp_ip4_spec.ip4src),
+ offsetof(struct ethtool_rx_flow_spec, m_u.tcp_ip4_spec.ip4src) },
+ { "dst-ip", OPT_IP4, NFC_FLAG_DADDR,
+ offsetof(struct ethtool_rx_flow_spec, h_u.tcp_ip4_spec.ip4dst),
+ offsetof(struct ethtool_rx_flow_spec, m_u.tcp_ip4_spec.ip4dst) },
+ { "tos", OPT_U8, NFC_FLAG_TOS,
+ offsetof(struct ethtool_rx_flow_spec, h_u.tcp_ip4_spec.tos),
+ offsetof(struct ethtool_rx_flow_spec, m_u.tcp_ip4_spec.tos) },
+ { "src-port", OPT_BE16, NFC_FLAG_SPORT,
+ offsetof(struct ethtool_rx_flow_spec, h_u.tcp_ip4_spec.psrc),
+ offsetof(struct ethtool_rx_flow_spec, m_u.tcp_ip4_spec.psrc) },
+ { "dst-port", OPT_BE16, NFC_FLAG_DPORT,
+ offsetof(struct ethtool_rx_flow_spec, h_u.tcp_ip4_spec.pdst),
+ offsetof(struct ethtool_rx_flow_spec, m_u.tcp_ip4_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) },
+};
+
+static struct rule_opts rule_nfc_esp_ip4[] = {
+ { "src-ip", OPT_IP4, NFC_FLAG_SADDR,
+ offsetof(struct ethtool_rx_flow_spec, h_u.esp_ip4_spec.ip4src),
+ offsetof(struct ethtool_rx_flow_spec, m_u.esp_ip4_spec.ip4src) },
+ { "dst-ip", OPT_IP4, NFC_FLAG_DADDR,
+ offsetof(struct ethtool_rx_flow_spec, h_u.esp_ip4_spec.ip4dst),
+ offsetof(struct ethtool_rx_flow_spec, m_u.esp_ip4_spec.ip4dst) },
+ { "tos", OPT_U8, NFC_FLAG_TOS,
+ offsetof(struct ethtool_rx_flow_spec, h_u.esp_ip4_spec.tos),
+ offsetof(struct ethtool_rx_flow_spec, m_u.esp_ip4_spec.tos) },
+ { "spi", OPT_BE32, NFC_FLAG_SPI,
+ offsetof(struct ethtool_rx_flow_spec, h_u.esp_ip4_spec.spi),
+ offsetof(struct ethtool_rx_flow_spec, m_u.esp_ip4_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) },
+};
+
+static struct rule_opts rule_nfc_usr_ip4[] = {
+ { "src-ip", OPT_IP4, NFC_FLAG_SADDR,
+ offsetof(struct ethtool_rx_flow_spec, h_u.usr_ip4_spec.ip4src),
+ offsetof(struct ethtool_rx_flow_spec, m_u.usr_ip4_spec.ip4src) },
+ { "dst-ip", OPT_IP4, NFC_FLAG_DADDR,
+ offsetof(struct ethtool_rx_flow_spec, h_u.usr_ip4_spec.ip4dst),
+ offsetof(struct ethtool_rx_flow_spec, m_u.usr_ip4_spec.ip4dst) },
+ { "tos", OPT_U8, NFC_FLAG_TOS,
+ offsetof(struct ethtool_rx_flow_spec, h_u.usr_ip4_spec.tos),
+ offsetof(struct ethtool_rx_flow_spec, m_u.usr_ip4_spec.tos) },
+ { "l4proto", OPT_U8, NFC_FLAG_PROTO,
+ offsetof(struct ethtool_rx_flow_spec, h_u.usr_ip4_spec.proto),
+ offsetof(struct ethtool_rx_flow_spec, m_u.usr_ip4_spec.proto) },
+ { "spi", OPT_BE32, NFC_FLAG_SPI,
+ offsetof(struct ethtool_rx_flow_spec, h_u.usr_ip4_spec.l4_4_bytes),
+ offsetof(struct ethtool_rx_flow_spec, m_u.usr_ip4_spec.l4_4_bytes) },
+ { "src-port", OPT_BE16, NFC_FLAG_SPORT,
+ offsetof(struct ethtool_rx_flow_spec, h_u.usr_ip4_spec.l4_4_bytes),
+ offsetof(struct ethtool_rx_flow_spec, m_u.usr_ip4_spec.l4_4_bytes) },
+ { "dst-port", OPT_BE16, NFC_FLAG_DPORT,
+ offsetof(struct ethtool_rx_flow_spec, h_u.usr_ip4_spec.l4_4_bytes) + 2,
+ offsetof(struct ethtool_rx_flow_spec, m_u.usr_ip4_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) },
+};
+
+static struct rule_opts rule_nfc_ether[] = {
+ { "src", OPT_MAC, NFC_FLAG_SADDR,
+ offsetof(struct ethtool_rx_flow_spec, h_u.ether_spec.h_dest),
+ offsetof(struct ethtool_rx_flow_spec, m_u.ether_spec.h_dest) },
+ { "dst", OPT_MAC, NFC_FLAG_DADDR,
+ offsetof(struct ethtool_rx_flow_spec, h_u.ether_spec.h_source),
+ offsetof(struct ethtool_rx_flow_spec, m_u.ether_spec.h_source) },
+ { "proto", OPT_BE16, NFC_FLAG_PROTO,
+ offsetof(struct ethtool_rx_flow_spec, h_u.ether_spec.h_proto),
+ offsetof(struct ethtool_rx_flow_spec, m_u.ether_spec.h_proto) },
+ { "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) },
+};
+
+static int rxclass_get_long(char *str, long long *val, int size)
+{
+ long long max = ~0ULL >> (65 - size);
+ char *endp;
+
+ errno = 0;
+
+ *val = strtoll(str, &endp, 0);
+
+ if (*endp || errno || (*val > max) || (*val < ~max))
+ return -1;
+
+ return 0;
+}
+
+static int rxclass_get_ulong(char *str, unsigned long long *val, int size)
+{
+ long long max = ~0ULL >> (64 - size);
+ char *endp;
+
+ errno = 0;
+
+ *val = strtoull(str, &endp, 0);
+
+ if (*endp || errno || (*val > max))
+ return -1;
+
+ return 0;
+}
+
+static int rxclass_get_ipv4(char *str, __be32 *val)
+{
+ if (!inet_pton(AF_INET, str, val))
+ return -1;
+
+ return 0;
+}
+
+static int rxclass_get_ether(char *str, unsigned char *val)
+{
+ unsigned int buf[ETH_ALEN];
+ int count;
+
+ if (!strchr(str, ':'))
+ return -1;
+
+ count = sscanf(str, "%2x:%2x:%2x:%2x:%2x:%2x",
+ &buf[0], &buf[1], &buf[2],
+ &buf[3], &buf[4], &buf[5]);
+
+ if (count != ETH_ALEN)
+ return -1;
+
+ do {
+ count--;
+ val[count] = buf[count];
+ } while (count);
+
+ return 0;
+}
+
+static int rxclass_get_val(char *str, unsigned char *p, u32 *flags,
+ const struct rule_opts *opt)
+{
+ unsigned long long mask = ~0ULL;
+ int err = 0;
+
+ if (*flags & opt->flag)
+ return -1;
+
+ *flags |= opt->flag;
+
+ switch (opt->type) {
+ case OPT_S32: {
+ long long val;
+ err = rxclass_get_long(str, &val, 32);
+ if (err)
+ return -1;
+ *(int *)&p[opt->offset] = (int)val;
+ if (opt->moffset >= 0)
+ *(int *)&p[opt->moffset] = (int)mask;
+ break;
+ }
+ case OPT_U8: {
+ unsigned long long val;
+ err = rxclass_get_ulong(str, &val, 8);
+ if (err)
+ return -1;
+ *(u8 *)&p[opt->offset] = (u8)val;
+ if (opt->moffset >= 0)
+ *(u8 *)&p[opt->moffset] = (u8)mask;
+ break;
+ }
+ case OPT_U16: {
+ unsigned long long val;
+ err = rxclass_get_ulong(str, &val, 16);
+ if (err)
+ return -1;
+ *(u16 *)&p[opt->offset] = (u16)val;
+ if (opt->moffset >= 0)
+ *(u16 *)&p[opt->moffset] = (u16)mask;
+ break;
+ }
+ case OPT_U32: {
+ unsigned long long val;
+ err = rxclass_get_ulong(str, &val, 32);
+ if (err)
+ return -1;
+ *(u32 *)&p[opt->offset] = (u32)val;
+ if (opt->moffset >= 0)
+ *(u32 *)&p[opt->moffset] = (u32)mask;
+ break;
+ }
+ case OPT_U64: {
+ unsigned long long val;
+ err = rxclass_get_ulong(str, &val, 64);
+ if (err)
+ return -1;
+ *(u64 *)&p[opt->offset] = (u64)val;
+ if (opt->moffset >= 0)
+ *(u64 *)&p[opt->moffset] = (u64)mask;
+ break;
+ }
+ case OPT_BE16: {
+ unsigned long long val;
+ err = rxclass_get_ulong(str, &val, 16);
+ if (err)
+ return -1;
+ *(__be16 *)&p[opt->offset] = htons((u16)val);
+ if (opt->moffset >= 0)
+ *(__be16 *)&p[opt->moffset] = (__be16)mask;
+ break;
+ }
+ case OPT_BE32: {
+ unsigned long long val;
+ err = rxclass_get_ulong(str, &val, 32);
+ if (err)
+ return -1;
+ *(__be32 *)&p[opt->offset] = htonl((u32)val);
+ if (opt->moffset >= 0)
+ *(__be32 *)&p[opt->moffset] = (__be32)mask;
+ break;
+ }
+ case OPT_BE64: {
+ unsigned long long val;
+ err = rxclass_get_ulong(str, &val, 64);
+ if (err)
+ return -1;
+ *(__be64 *)&p[opt->offset] = htonll((u64)val);
+ if (opt->moffset >= 0)
+ *(__be64 *)&p[opt->moffset] = (__be64)mask;
+ break;
+ }
+ case OPT_IP4: {
+ __be32 val;
+ err = rxclass_get_ipv4(str, &val);
+ if (err)
+ return -1;
+ *(__be32 *)&p[opt->offset] = val;
+ if (opt->moffset >= 0)
+ *(__be32 *)&p[opt->moffset] = (__be32)mask;
+ break;
+ }
+ case OPT_MAC: {
+ unsigned char val[ETH_ALEN];
+ err = rxclass_get_ether(str, val);
+ if (err)
+ return -1;
+ memcpy(&p[opt->offset], val, ETH_ALEN);
+ if (opt->moffset >= 0)
+ memcpy(&p[opt->moffset], &mask, ETH_ALEN);
+ break;
+ }
+ case OPT_NONE:
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+static int rxclass_get_mask(char *str, unsigned char *p,
+ const struct rule_opts *opt)
+{
+ int err = 0;
+
+ if (opt->moffset < 0)
+ return -1;
+
+ switch (opt->type) {
+ case OPT_S32: {
+ long long val;
+ err = rxclass_get_long(str, &val, 32);
+ if (err)
+ return -1;
+ *(int *)&p[opt->moffset] = ~(int)val;
+ break;
+ }
+ case OPT_U8: {
+ unsigned long long val;
+ err = rxclass_get_ulong(str, &val, 8);
+ if (err)
+ return -1;
+ *(u8 *)&p[opt->moffset] = ~(u8)val;
+ break;
+ }
+ case OPT_U16: {
+ unsigned long long val;
+ err = rxclass_get_ulong(str, &val, 16);
+ if (err)
+ return -1;
+ *(u16 *)&p[opt->moffset] = ~(u16)val;
+ break;
+ }
+ case OPT_U32: {
+ unsigned long long val;
+ err = rxclass_get_ulong(str, &val, 32);
+ if (err)
+ return -1;
+ *(u32 *)&p[opt->moffset] = ~(u32)val;
+ break;
+ }
+ case OPT_U64: {
+ unsigned long long val;
+ err = rxclass_get_ulong(str, &val, 64);
+ if (err)
+ return -1;
+ *(u64 *)&p[opt->moffset] = ~(u64)val;
+ break;
+ }
+ case OPT_BE16: {
+ unsigned long long val;
+ err = rxclass_get_ulong(str, &val, 16);
+ if (err)
+ return -1;
+ *(__be16 *)&p[opt->moffset] = ~htons((u16)val);
+ break;
+ }
+ case OPT_BE32: {
+ unsigned long long val;
+ err = rxclass_get_ulong(str, &val, 32);
+ if (err)
+ return -1;
+ *(__be32 *)&p[opt->moffset] = ~htonl((u32)val);
+ break;
+ }
+ case OPT_BE64: {
+ unsigned long long val;
+ err = rxclass_get_ulong(str, &val, 64);
+ if (err)
+ return -1;
+ *(__be64 *)&p[opt->moffset] = ~htonll((u64)val);
+ break;
+ }
+ case OPT_IP4: {
+ __be32 val;
+ err = rxclass_get_ipv4(str, &val);
+ if (err)
+ return -1;
+ *(__be32 *)&p[opt->moffset] = ~val;
+ break;
+ }
+ case OPT_MAC: {
+ unsigned char val[ETH_ALEN];
+ int i;
+ err = rxclass_get_ether(str, val);
+ if (err)
+ return -1;
+
+ for (i = 0; i < ETH_ALEN; i++)
+ val[i] = ~val[i];
+
+ memcpy(&p[opt->moffset], val, ETH_ALEN);
+ break;
+ }
+ case OPT_NONE:
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+int rxclass_parse_ruleopts(char **argp, int argc,
+ struct ethtool_rx_flow_spec *fsp)
+{
+ const struct rule_opts *options;
+ unsigned char *p = (unsigned char *)fsp;
+ int i = 0, n_opts, err;
+ u32 flags = 0;
+ int flow_type;
+
+ if (argc < 1)
+ goto syntax_err;
+
+ if (!strcmp(argp[0], "tcp4"))
+ flow_type = TCP_V4_FLOW;
+ else if (!strcmp(argp[0], "udp4"))
+ flow_type = UDP_V4_FLOW;
+ else if (!strcmp(argp[0], "sctp4"))
+ flow_type = SCTP_V4_FLOW;
+ else if (!strcmp(argp[0], "ah4"))
+ flow_type = AH_V4_FLOW;
+ else if (!strcmp(argp[0], "esp4"))
+ flow_type = ESP_V4_FLOW;
+ else if (!strcmp(argp[0], "ip4"))
+ flow_type = IP_USER_FLOW;
+ else if (!strcmp(argp[0], "ether"))
+ flow_type = ETHER_FLOW;
+ else
+ goto syntax_err;
+
+ switch (flow_type) {
+ case TCP_V4_FLOW:
+ case UDP_V4_FLOW:
+ case SCTP_V4_FLOW:
+ options = rule_nfc_tcp_ip4;
+ n_opts = ARRAY_SIZE(rule_nfc_tcp_ip4);
+ break;
+ case AH_V4_FLOW:
+ case ESP_V4_FLOW:
+ options = rule_nfc_esp_ip4;
+ n_opts = ARRAY_SIZE(rule_nfc_esp_ip4);
+ break;
+ case IP_USER_FLOW:
+ options = rule_nfc_usr_ip4;
+ n_opts = ARRAY_SIZE(rule_nfc_usr_ip4);
+ break;
+ case ETHER_FLOW:
+ options = rule_nfc_ether;
+ n_opts = ARRAY_SIZE(rule_nfc_ether);
+ break;
+ default:
+ fprintf(stderr, "Add rule, invalid rule type[%s]\n", argp[0]);
+ return -1;
+ }
+
+ memset(p, 0, sizeof(*fsp));
+ fsp->flow_type = flow_type;
+ fsp->location = RX_CLS_LOC_UNSPEC;
+
+ for (i = 1; i < argc;) {
+ const struct rule_opts *opt;
+ int idx;
+ for (opt = options, idx = 0; idx < n_opts; idx++, opt++) {
+ char mask_name[16];
+
+ if (strcmp(argp[i], opt->name))
+ continue;
+
+ i++;
+ if (i >= argc)
+ break;
+
+ err = rxclass_get_val(argp[i], p, &flags, opt);
+ if (err) {
+ fprintf(stderr, "Invalid %s value[%s]\n",
+ opt->name, argp[i]);
+ return -1;
+ }
+
+ i++;
+ if (i >= argc)
+ break;
+
+ sprintf(mask_name, "%s-mask", opt->name);
+ if (strcmp(argp[i], "m") && strcmp(argp[i], mask_name))
+ break;
+
+ i++;
+ if (i >= argc)
+ goto syntax_err;
+
+ err = rxclass_get_mask(argp[i], p, opt);
+ if (err) {
+ fprintf(stderr, "Invalid %s mask[%s]\n",
+ opt->name, argp[i]);
+ return -1;
+ }
+
+ i++;
+
+ break;
+ }
+ if (idx == n_opts) {
+ fprintf(stdout, "Add rule, unrecognized option[%s]\n",
+ argp[i]);
+ return -1;
+ }
+ }
+
+ if (flags & (NTUPLE_FLAG_VLAN | NTUPLE_FLAG_UDEF | NTUPLE_FLAG_VETH))
+ fsp->flow_type |= FLOW_EXT;
+
+ return 0;
+
+syntax_err:
+ fprintf(stderr, "Add rule, invalid syntax\n");
+ return -1;
+}
^ permalink raw reply related
* [ethtool PATCH 3/4] Add support for __be64 and bitops, centralize several needed macros
From: Alexander Duyck @ 2011-05-03 16:12 UTC (permalink / raw)
To: davem, jeffrey.t.kirsher, bhutchings; +Cc: netdev
In-Reply-To: <20110503160547.29251.84333.stgit@gitlad.jf.intel.com>
This change is meant to add support for __be64 values and bitops to
ethtool. In addition the patch pulls the SIOCETHTOOL define and the
ARRAY_SIZE define into ethtool-util.h for later use by the rxclass files.
These changes will be needed in order to support network flow
classifier rule configuration.
Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
---
ethtool-bitops.h | 25 +++++++++++++++++++++++++
ethtool-util.h | 16 ++++++++++++++--
2 files changed, 39 insertions(+), 2 deletions(-)
create mode 100644 ethtool-bitops.h
diff --git a/ethtool-bitops.h b/ethtool-bitops.h
new file mode 100644
index 0000000..b1eb426
--- /dev/null
+++ b/ethtool-bitops.h
@@ -0,0 +1,25 @@
+#ifndef ETHTOOL_BITOPS_H__
+#define ETHTOOL_BITOPS_H__
+
+#define BITS_PER_BYTE 8
+#define BITS_PER_LONG (BITS_PER_BYTE * sizeof(long))
+#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
+#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_LONG)
+
+static inline void set_bit(int nr, unsigned long *addr)
+{
+ addr[nr / BITS_PER_LONG] |= 1UL << (nr % BITS_PER_LONG);
+}
+
+static inline void clear_bit(int nr, unsigned long *addr)
+{
+ addr[nr / BITS_PER_LONG] &= ~(1UL << (nr % BITS_PER_LONG));
+}
+
+static inline int test_bit(unsigned int nr, const unsigned long *addr)
+{
+ return !!((1UL << (nr % BITS_PER_LONG)) &
+ (((unsigned long *)addr)[nr / BITS_PER_LONG]));
+}
+
+#endif
diff --git a/ethtool-util.h b/ethtool-util.h
index 6a4f3f4..d8b621c 100644
--- a/ethtool-util.h
+++ b/ethtool-util.h
@@ -15,6 +15,7 @@
#ifndef HAVE_BE_TYPES
typedef __uint16_t __be16;
typedef __uint32_t __be32;
+typedef unsigned long long __be64;
#endif
typedef unsigned long long u64;
@@ -28,11 +29,15 @@ typedef __int32_t s32;
#if __BYTE_ORDER == __BIG_ENDIAN
static inline u16 cpu_to_be16(u16 value)
{
- return value;
+ return value;
}
static inline u32 cpu_to_be32(u32 value)
{
- return value;
+ return value;
+}
+static inline u64 cpu_to_be64(u64 value)
+{
+ return value;
}
#else
static inline u16 cpu_to_be16(u16 value)
@@ -43,8 +48,15 @@ static inline u32 cpu_to_be32(u32 value)
{
return cpu_to_be16(value >> 16) | (cpu_to_be16(value) << 16);
}
+static inline u64 cpu_to_be64(u64 value)
+{
+ return cpu_to_be32(value >> 32) | ((u64)cpu_to_be32(value) << 32);
+}
#endif
+#define ntohll cpu_to_be64
+#define htonll cpu_to_be64
+
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#endif
^ permalink raw reply related
* [ethtool PATCH 2/4] Cleanup defines and header includes to address several issues
From: Alexander Duyck @ 2011-05-03 16:12 UTC (permalink / raw)
To: davem, jeffrey.t.kirsher, bhutchings; +Cc: netdev
In-Reply-To: <20110503160547.29251.84333.stgit@gitlad.jf.intel.com>
This change is meant to address several issues. First it moves the check
for ethtool-config.h into ethtool-util.h the reason for this change is so
that any references to ethtool-util.h outside of ethtool.c will use the
correct defines for the endian types.
In addition I have pulled several headers that will be common to both
ethtool.c and rxclass.c into the ethtool-util.h header file. I am also
centralizing several macros that will be needed across multiple files when
I implement the network flow classifier rules.
Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
---
ethtool-util.h | 17 +++++++++++++++--
ethtool.c | 17 +----------------
2 files changed, 16 insertions(+), 18 deletions(-)
diff --git a/ethtool-util.h b/ethtool-util.h
index f053028..6a4f3f4 100644
--- a/ethtool-util.h
+++ b/ethtool-util.h
@@ -3,8 +3,13 @@
#ifndef ETHTOOL_UTIL_H__
#define ETHTOOL_UTIL_H__
+#ifdef HAVE_CONFIG_H
+#include "ethtool-config.h"
+#endif
#include <sys/types.h>
#include <endian.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
/* ethtool.h expects these to be defined by <linux/types.h> */
#ifndef HAVE_BE_TYPES
@@ -12,14 +17,14 @@ typedef __uint16_t __be16;
typedef __uint32_t __be32;
#endif
-#include "ethtool-copy.h"
-
typedef unsigned long long u64;
typedef __uint32_t u32;
typedef __uint16_t u16;
typedef __uint8_t u8;
typedef __int32_t s32;
+#include "ethtool-copy.h"
+
#if __BYTE_ORDER == __BIG_ENDIAN
static inline u16 cpu_to_be16(u16 value)
{
@@ -40,6 +45,14 @@ static inline u32 cpu_to_be32(u32 value)
}
#endif
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+
+#ifndef SIOCETHTOOL
+#define SIOCETHTOOL 0x8946
+#endif
+
/* National Semiconductor DP83815, DP83816 */
int natsemi_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
int natsemi_dump_eeprom(struct ethtool_drvinfo *info,
diff --git a/ethtool.c b/ethtool.c
index 9ad7000..24d4e4f 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -21,19 +21,12 @@
* * show settings for all devices
*/
-#ifdef HAVE_CONFIG_H
-# include "ethtool-config.h"
-#endif
-
-#include <sys/types.h>
+#include "ethtool-util.h"
#include <string.h>
#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/ioctl.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
-#include <net/if.h>
#include <sys/utsname.h>
#include <limits.h>
#include <ctype.h>
@@ -43,18 +36,10 @@
#include <arpa/inet.h>
#include <linux/sockios.h>
-#include "ethtool-util.h"
-
-#ifndef SIOCETHTOOL
-#define SIOCETHTOOL 0x8946
-#endif
#ifndef MAX_ADDR_LEN
#define MAX_ADDR_LEN 32
#endif
-#ifndef ARRAY_SIZE
-#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
-#endif
#ifndef HAVE_NETIF_MSG
enum {
^ permalink raw reply related
* [ethtool PATCH 1/4] ethtool: remove strings based approach for displaying n-tuple
From: Alexander Duyck @ 2011-05-03 16:12 UTC (permalink / raw)
To: davem, jeffrey.t.kirsher, bhutchings; +Cc: netdev
In-Reply-To: <20110503160547.29251.84333.stgit@gitlad.jf.intel.com>
This change is meant to remove the strings based approach for displaying
n-tuple filters. A follow-on patch will replace that functionality with a
network flow classification based approach that will get the number of
filters, get their locations, and then request and display them
individually.
Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
---
ethtool.c | 44 --------------------------------------------
1 files changed, 0 insertions(+), 44 deletions(-)
diff --git a/ethtool.c b/ethtool.c
index cfdac65..9ad7000 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -3194,50 +3194,6 @@ static int do_srxntuple(int fd, struct ifreq *ifr)
static int do_grxntuple(int fd, struct ifreq *ifr)
{
- struct ethtool_sset_info *sset_info;
- struct ethtool_gstrings *strings;
- int sz_str, n_strings, err, i;
-
- sset_info = malloc(sizeof(struct ethtool_sset_info) + sizeof(u32));
- sset_info->cmd = ETHTOOL_GSSET_INFO;
- sset_info->sset_mask = (1ULL << ETH_SS_NTUPLE_FILTERS);
- ifr->ifr_data = (caddr_t)sset_info;
- err = send_ioctl(fd, ifr);
-
- if ((err < 0) ||
- (!(sset_info->sset_mask & (1ULL << ETH_SS_NTUPLE_FILTERS)))) {
- perror("Cannot get driver strings info");
- return 100;
- }
-
- n_strings = sset_info->data[0];
- free(sset_info);
- sz_str = n_strings * ETH_GSTRING_LEN;
-
- strings = calloc(1, sz_str + sizeof(struct ethtool_gstrings));
- if (!strings) {
- fprintf(stderr, "no memory available\n");
- return 95;
- }
-
- strings->cmd = ETHTOOL_GRXNTUPLE;
- strings->string_set = ETH_SS_NTUPLE_FILTERS;
- strings->len = n_strings;
- ifr->ifr_data = (caddr_t) strings;
- err = send_ioctl(fd, ifr);
- if (err < 0) {
- perror("Cannot get Rx n-tuple information");
- free(strings);
- return 101;
- }
-
- n_strings = strings->len;
- fprintf(stdout, "Rx n-tuple filters:\n");
- for (i = 0; i < n_strings; i++)
- fprintf(stdout, "%s", &strings->data[i * ETH_GSTRING_LEN]);
-
- free(strings);
-
return 0;
}
^ permalink raw reply related
* [ethtool PATCH 0/4] Add support for network flow classifier
From: Alexander Duyck @ 2011-05-03 16:12 UTC (permalink / raw)
To: davem, jeffrey.t.kirsher, bhutchings; +Cc: netdev
This series is version 5 of the patches for adding network flow classifier
rules support to ethtool. The main changes are that I split a general header
cleanup off into a separate patch, I combined the documentation update into
the addition of the RX packet classification interface, and I generally
cleaned up the code further for when we need to maintain the rule table and
when we don't. As a result I dropped a few dozen lines of code related to
finding and deleting a rule from the table since we only need the table to
determine which rules are present.
I will be submitting a set of patches through Jeff Kirsher once these patches
are accepted to allow for ixgbe to make use of the network flow classifier
rules interface.
Thanks,
Alex
---
Alexander Duyck (3):
Add support for __be64 and bitops, centralize several needed macros
Cleanup defines and header includes to address several issues
ethtool: remove strings based approach for displaying n-tuple
Santwona Behera (1):
v5 Add RX packet classification interface
Makefile.am | 3
ethtool-bitops.h | 25 +
ethtool-util.h | 47 ++
ethtool.8.in | 194 +++++-----
ethtool.c | 449 ++++++++++++-----------
rxclass.c | 1039 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 1433 insertions(+), 324 deletions(-)
create mode 100644 ethtool-bitops.h
create mode 100644 rxclass.c
--
^ permalink raw reply
* Re: PATCH: add IP_USER_FLOW to rxntuple interface
From: Alexander Duyck @ 2011-05-03 15:59 UTC (permalink / raw)
To: Sebastian.Poehn@Belden.com; +Cc: netdev@vger.kernel.org, Ben Hutchings
In-Reply-To: <OF20F9D953.0F90C5C5-ON85257885.004A9AEF-85257885.004A9AF3@BeldenCDT.com>
On 5/3/2011 6:34 AM, Sebastian.Poehn@Belden.com wrote:
> I added IP_USER_FLOW to the rx ntuple interface of ethtool which will
> support raw IP traffic classification. If the patch is okay i will
> add the new parameters to the manpage.
>
> Signed-off-by: Sebastian Poehn<sebastian.poehn@belden.com>
>
> --- ethtool/ethtool.c.orig 2011-05-03 11:17:30.457012539 +0200
> +++ ethtool/ethtool.c 2011-05-03 13:24:52.709010424 +0200
> @@ -396,6 +396,12 @@ static int ntuple_psrc_seen = 0;
> static int ntuple_psrc_mask_seen = 0;
> static int ntuple_pdst_seen = 0;
> static int ntuple_pdst_mask_seen = 0;
> +static int ntuple_l4_4_bytes_seen = 0;
> +static int ntuple_l4_4_bytes_mask_seen = 0;
> +static int ntuple_ip_ver_seen = 0;
> +static int ntuple_ip_ver_mask_seen = 0;
> +static int ntuple_ip_proto_seen = 0;
> +static int ntuple_ip_proto_mask_seen = 0;
> static int ntuple_ether_dst_seen = 0;
> static int ntuple_ether_dst_mask_seen = 0;
> static int ntuple_ether_src_seen = 0;
> @@ -423,10 +429,12 @@ typedef enum {
> CMDL_NONE,
> CMDL_BOOL,
> CMDL_S32,
> + CMDL_U8,
> CMDL_U16,
> CMDL_U32,
> CMDL_U64,
> CMDL_BE16,
> + CMDL_BE32,
> CMDL_IP4,
> CMDL_STR,
> CMDL_FLAG,
> @@ -554,6 +562,38 @@ static struct cmdline_info cmdline_ntupl
> { "action", CMDL_S32,&ntuple_fs.action, NULL },
> };
>
> +static struct cmdline_info cmdline_ntuple_usr_ip4[] = {
> + { "src-ip", CMDL_IP4,&ntuple_fs.h_u.usr_ip4_spec.ip4src, NULL,
> + 0,&ntuple_ip4src_seen },
> + { "src-ip-mask", CMDL_IP4,&ntuple_fs.m_u.usr_ip4_spec.ip4src, NULL,
> + 0,&ntuple_ip4src_mask_seen },
> + { "dst-ip", CMDL_IP4,&ntuple_fs.h_u.usr_ip4_spec.ip4dst, NULL,
> + 0,&ntuple_ip4dst_seen },
> + { "dst-ip-mask", CMDL_IP4,&ntuple_fs.m_u.usr_ip4_spec.ip4dst, NULL,
> + 0,&ntuple_ip4dst_mask_seen },
> + { "l4-data", CMDL_BE32,&ntuple_fs.h_u.usr_ip4_spec.l4_4_bytes, NULL,
> + 0,&ntuple_l4_4_bytes_seen },
> + { "l4-data-mask", CMDL_BE32,&ntuple_fs.m_u.usr_ip4_spec.l4_4_bytes, NULL,
> + 0,&ntuple_l4_4_bytes_mask_seen },
> + { "version", CMDL_U8,&ntuple_fs.h_u.usr_ip4_spec.ip_ver, NULL,
> + 0,&ntuple_ip_ver_seen },
> + { "version-mask", CMDL_U8,&ntuple_fs.m_u.usr_ip4_spec.ip_ver, NULL,
> + 0,&ntuple_ip_ver_mask_seen },
> + { "iana", CMDL_U8,&ntuple_fs.h_u.usr_ip4_spec.proto, NULL,
> + 0,&ntuple_ip_proto_seen },
> + { "iana-mask", CMDL_U8,&ntuple_fs.m_u.usr_ip4_spec.proto, NULL,
> + 0,&ntuple_ip_proto_mask_seen },
> + { "vlan", CMDL_U16,&ntuple_fs.vlan_tag, NULL,
> + 0,&ntuple_vlan_tag_seen },
> + { "vlan-mask", CMDL_U16,&ntuple_fs.vlan_tag_mask, NULL,
> + 0,&ntuple_vlan_tag_mask_seen },
> + { "user-def", CMDL_U64,&ntuple_fs.data, NULL,
> + 0,&ntuple_user_def_seen },
> + { "user-def-mask", CMDL_U64,&ntuple_fs.data_mask, NULL,
> + 0,&ntuple_user_def_mask_seen },
> + { "action", CMDL_S32,&ntuple_fs.action, NULL },
> +};
> +
> static struct cmdline_info cmdline_ntuple_ether[] = {
> { "dst", CMDL_MAC, ntuple_fs.h_u.ether_spec.h_dest, NULL,
> 0,&ntuple_ether_dst_seen },
> @@ -689,6 +729,11 @@ static void parse_generic_cmdline(int ar
> 0x7fffffff);
> break;
> }
> + case CMDL_U8: {
> + u8 *p = info[idx].wanted_val;
> + *p = get_uint_range(argp[i], 0, 0xff);
> + break;
> + }
> case CMDL_U16: {
> u16 *p = info[idx].wanted_val;
> *p = get_uint_range(argp[i], 0, 0xffff);
> @@ -714,6 +759,13 @@ static void parse_generic_cmdline(int ar
> 0xffff));
> break;
> }
> + case CMDL_BE32: {
> + u32 *p = info[idx].wanted_val;
> + *p = cpu_to_be32(
> + get_uint_range(argp[i], 0,
> + 0xffffffff));
> + break;
> + }
> case CMDL_IP4: {
> u32 *p = info[idx].wanted_val;
> struct in_addr in;
> @@ -786,6 +838,8 @@ static int rxflow_str_to_type(const char
> flow_type = AH_ESP_V4_FLOW;
> else if (!strcmp(str, "sctp4"))
> flow_type = SCTP_V4_FLOW;
> + else if (!strcmp(str, "usr4"))
> + flow_type = IP_USER_FLOW;
> else if (!strcmp(str, "tcp6"))
> flow_type = TCP_V6_FLOW;
> else if (!strcmp(str, "udp6"))
> @@ -1648,6 +1702,23 @@ static void parse_rxntupleopts(int argc,
> if (!ntuple_ether_proto_seen)
> ntuple_fs.m_u.ether_spec.h_proto = 0xffff;
> break;
> + case IP_USER_FLOW:
> + parse_generic_cmdline(argc, argp, i + 1,
> +&sntuple_changed,
> + cmdline_ntuple_usr_ip4,
> + ARRAY_SIZE(cmdline_ntuple_usr_ip4));
> + if (!ntuple_ip4src_seen)
> + ntuple_fs.m_u.usr_ip4_spec.ip4src = 0xffffffff;
> + if (!ntuple_ip4dst_seen)
> + ntuple_fs.m_u.usr_ip4_spec.ip4dst = 0xffffffff;
> + if (!ntuple_l4_4_bytes_seen)
> + ntuple_fs.m_u.usr_ip4_spec.l4_4_bytes = 0xffffffff;
> + if (!ntuple_ip_ver_seen)
> + ntuple_fs.m_u.usr_ip4_spec.ip_ver = 0xff;
> + if (!ntuple_ip_proto_seen)
> + ntuple_fs.m_u.usr_ip4_spec.proto = 0xff;
> + ntuple_fs.m_u.tcp_ip4_spec.tos = 0xff;
> + break;
> default:
> fprintf(stderr, "Unsupported flow type \"%s\"\n", argp[i]);
> exit(106);
>
All of this code doesn't exist after the changes I am making to add
network flow classifier support to ethtool. I also believe this is all
redundant since the updated changes should allow ntuple to use
IP_USER_FLOW since it already supported in network flow classifier.
Thanks,
Alex
^ permalink raw reply
* Re: [RFC v3 01/10] lsm: add security_socket_closed()
From: Samir Bellabes @ 2011-05-03 15:41 UTC (permalink / raw)
To: Tetsuo Handa
Cc: linux-security-module, linux-kernel, netdev, netfilter-devel,
hadi, kaber, zbr, root
In-Reply-To: <201105040029.IGA98088.SHOVQFLOMJtOFF@I-love.SAKURA.ne.jp>
Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> writes:
> Samir Bellabes wrote:
>> Allow a module to update security informations when a socket is closed.
> Is security_inode_free() too late for doing it?
>
> static void ccs_inode_free_security(struct inode *inode)
> {
> if (inode->i_sb && inode->i_sb->s_magic == SOCKFS_MAGIC)
> ccs_update_socket_tag(inode, 0);
> }
I don't think it's too late.
But in sock_close() we are sure to deal with a inode which is a socket.
In ccs_inode_free_security(), we need to add small to check if the inode
is a socket or something else.
sam
^ permalink raw reply
* Re: [RFC v3 01/10] lsm: add security_socket_closed()
From: Tetsuo Handa @ 2011-05-03 15:29 UTC (permalink / raw)
To: sam, linux-security-module
Cc: linux-kernel, netdev, netfilter-devel, hadi, kaber, zbr, root
In-Reply-To: <1304432663-1575-2-git-send-email-sam@synack.fr>
Samir Bellabes wrote:
> Allow a module to update security informations when a socket is closed.
Is security_inode_free() too late for doing it?
static void ccs_inode_free_security(struct inode *inode)
{
if (inode->i_sb && inode->i_sb->s_magic == SOCKFS_MAGIC)
ccs_update_socket_tag(inode, 0);
}
^ permalink raw reply
* [RFC v3 02/10] Revert "lsm: Remove the socket_post_accept() hook"
From: Samir Bellabes @ 2011-05-03 14:24 UTC (permalink / raw)
To: linux-security-module
Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
Evgeniy Polyakov, Grzegorz Nosek, Samir Bellabes
In-Reply-To: <1304432663-1575-1-git-send-email-sam@synack.fr>
snet needs to reintroduce this hook, as it was designed to be: a hook for
updating security informations on objects.
Originally, This was a direct revert of commit
8651d5c0b1f874c5b8307ae2b858bc40f9f02482.
But from the comment of Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> :
> Please move security_socket_post_accept() to before fd_install().
> Otherwise, other threads which share fd tables can use
> security-informations-not-yet-updated accept()ed sockets.
Signed-off-by: Samir Bellabes <sam@synack.fr>
Acked-by: Serge Hallyn <serue@us.ibm.com>
snet needs to reintroduce this hook, as it was designed to be: a hook for
updating security informations on objects.
Signed-off-by: Samir Bellabes <sam@synack.fr>
---
include/linux/security.h | 13 +++++++++++++
net/socket.c | 2 ++
security/capability.c | 5 +++++
security/security.c | 5 +++++
4 files changed, 25 insertions(+), 0 deletions(-)
diff --git a/include/linux/security.h b/include/linux/security.h
index da0d59e..02effe5 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -875,6 +875,11 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* @sock contains the listening socket structure.
* @newsock contains the newly created server socket for connection.
* Return 0 if permission is granted.
+ * @socket_post_accept:
+ * This hook allows a security module to copy security
+ * information into the newly created socket's inode.
+ * @sock contains the listening socket structure.
+ * @newsock contains the newly created server socket for connection.
* @socket_sendmsg:
* Check permission before transmitting a message to another socket.
* @sock contains the socket structure.
@@ -1587,6 +1592,8 @@ struct security_operations {
struct sockaddr *address, int addrlen);
int (*socket_listen) (struct socket *sock, int backlog);
int (*socket_accept) (struct socket *sock, struct socket *newsock);
+ void (*socket_post_accept) (struct socket *sock,
+ struct socket *newsock);
int (*socket_sendmsg) (struct socket *sock,
struct msghdr *msg, int size);
int (*socket_recvmsg) (struct socket *sock,
@@ -2555,6 +2562,7 @@ int security_socket_bind(struct socket *sock, struct sockaddr *address, int addr
int security_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen);
int security_socket_listen(struct socket *sock, int backlog);
int security_socket_accept(struct socket *sock, struct socket *newsock);
+void security_socket_post_accept(struct socket *sock, struct socket *newsock);
int security_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size);
int security_socket_recvmsg(struct socket *sock, struct msghdr *msg,
int size, int flags);
@@ -2640,6 +2648,11 @@ static inline int security_socket_accept(struct socket *sock,
return 0;
}
+static inline void security_socket_post_accept(struct socket *sock,
+ struct socket *newsock)
+{
+}
+
static inline int security_socket_sendmsg(struct socket *sock,
struct msghdr *msg, int size)
{
diff --git a/net/socket.c b/net/socket.c
index d588e9e..7807904 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -1535,6 +1535,8 @@ SYSCALL_DEFINE4(accept4, int, fd, struct sockaddr __user *, upeer_sockaddr,
goto out_fd;
}
+ security_socket_post_accept(sock, newsock);
+
/* File flags are not inherited via accept() unlike another OSes. */
fd_install(newfd, newfile);
diff --git a/security/capability.c b/security/capability.c
index 1f8bbe2..da68c60 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -593,6 +593,10 @@ static int cap_socket_accept(struct socket *sock, struct socket *newsock)
return 0;
}
+static void cap_socket_post_accept(struct socket *sock, struct socket *newsock)
+{
+}
+
static int cap_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size)
{
return 0;
@@ -1022,6 +1026,7 @@ void __init security_fixup_ops(struct security_operations *ops)
set_to_cap_if_null(ops, socket_connect);
set_to_cap_if_null(ops, socket_listen);
set_to_cap_if_null(ops, socket_accept);
+ set_to_cap_if_null(ops, socket_post_accept);
set_to_cap_if_null(ops, socket_sendmsg);
set_to_cap_if_null(ops, socket_recvmsg);
set_to_cap_if_null(ops, socket_getsockname);
diff --git a/security/security.c b/security/security.c
index 84187d8..eda2b75 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1038,6 +1038,11 @@ int security_socket_accept(struct socket *sock, struct socket *newsock)
return security_ops->socket_accept(sock, newsock);
}
+void security_socket_post_accept(struct socket *sock, struct socket *newsock)
+{
+ security_ops->socket_post_accept(sock, newsock);
+}
+
int security_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size)
{
return security_ops->socket_sendmsg(sock, msg, size);
--
1.7.4.1
^ permalink raw reply related
* [RFC v3 10/10] snet: introduce security/snet, Makefile and Kconfig changes
From: Samir Bellabes @ 2011-05-03 14:24 UTC (permalink / raw)
To: linux-security-module
Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
Evgeniy Polyakov, Grzegorz Nosek, Samir Bellabes
In-Reply-To: <1304432663-1575-1-git-send-email-sam@synack.fr>
this patch creates folder security/snet and adds changes for Kconfig and Makefile
Signed-off-by: Samir Bellabes <sam@synack.fr>
---
security/Kconfig | 6 ++++++
security/Makefile | 2 ++
security/snet/Kconfig | 11 +++++++++++
security/snet/Makefile | 14 ++++++++++++++
4 files changed, 33 insertions(+), 0 deletions(-)
create mode 100644 security/snet/Kconfig
create mode 100644 security/snet/Makefile
diff --git a/security/Kconfig b/security/Kconfig
index 95accd4..7e393c4 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -184,6 +184,7 @@ source security/selinux/Kconfig
source security/smack/Kconfig
source security/tomoyo/Kconfig
source security/apparmor/Kconfig
+source security/snet/Kconfig
source security/integrity/ima/Kconfig
@@ -193,6 +194,7 @@ choice
default DEFAULT_SECURITY_SMACK if SECURITY_SMACK
default DEFAULT_SECURITY_TOMOYO if SECURITY_TOMOYO
default DEFAULT_SECURITY_APPARMOR if SECURITY_APPARMOR
+ default DEFAULT_SECURITY_SNET if SECURITY_SNET
default DEFAULT_SECURITY_DAC
help
@@ -211,6 +213,9 @@ choice
config DEFAULT_SECURITY_APPARMOR
bool "AppArmor" if SECURITY_APPARMOR=y
+ config DEFAULT_SECURITY_SNET
+ bool "snet" if SECURITY_SNET=y
+
config DEFAULT_SECURITY_DAC
bool "Unix Discretionary Access Controls"
@@ -222,6 +227,7 @@ config DEFAULT_SECURITY
default "smack" if DEFAULT_SECURITY_SMACK
default "tomoyo" if DEFAULT_SECURITY_TOMOYO
default "apparmor" if DEFAULT_SECURITY_APPARMOR
+ default "snet" if DEFAULT_SECURITY_SNET
default "" if DEFAULT_SECURITY_DAC
endmenu
diff --git a/security/Makefile b/security/Makefile
index 8bb0fe9..9e769b2 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -7,6 +7,7 @@ subdir-$(CONFIG_SECURITY_SELINUX) += selinux
subdir-$(CONFIG_SECURITY_SMACK) += smack
subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo
subdir-$(CONFIG_SECURITY_APPARMOR) += apparmor
+subdir-$(CONFIG_SECURITY_SNET) += snet
# always enable default capabilities
obj-y += commoncap.o
@@ -21,6 +22,7 @@ obj-$(CONFIG_SECURITY_SMACK) += smack/built-in.o
obj-$(CONFIG_AUDIT) += lsm_audit.o
obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/built-in.o
obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/built-in.o
+obj-$(CONFIG_SECURITY_SNET) += snet/built-in.o
obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o
# Object integrity file lists
diff --git a/security/snet/Kconfig b/security/snet/Kconfig
new file mode 100644
index 0000000..6dabd7d
--- /dev/null
+++ b/security/snet/Kconfig
@@ -0,0 +1,11 @@
+#
+# snet
+#
+
+config SECURITY_SNET
+ bool "snet - Security for NETwork syscalls"
+ depends on SECURITY_NETWORK
+ default n
+ ---help---
+ If this option is enabled, the kernel will include support for reporting
+ networking's syscalls to userspace and wait for a verdict
diff --git a/security/snet/Makefile b/security/snet/Makefile
new file mode 100644
index 0000000..e8f52f1
--- /dev/null
+++ b/security/snet/Makefile
@@ -0,0 +1,14 @@
+#
+# Makefile for building the Security Network Events module.
+#
+obj-$(CONFIG_SECURITY_SNET) := snet.o
+
+snet-y := snet_event.o \
+ snet_netlink_helper.o \
+ snet_netlink.o \
+ snet_verdict.o \
+ snet_ticket_helper.o \
+ snet_ticket.o \
+ snet_hooks.o \
+ snet_core.o \
+ snet_utils.o
--
1.7.4.1
^ permalink raw reply related
* [RFC v3 09/10] snet: introduce snet_utils
From: Samir Bellabes @ 2011-05-03 14:24 UTC (permalink / raw)
To: linux-security-module
Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
Evgeniy Polyakov, Grzegorz Nosek, Samir Bellabes
In-Reply-To: <1304432663-1575-1-git-send-email-sam@synack.fr>
This patch provides helper functions for other subsystems
Signed-off-by: Samir Bellabes <sam@synack.fr>
---
security/snet/snet_utils.c | 38 ++++++++++++++++++++++++++++++++++++++
security/snet/snet_utils.h | 9 +++++++++
2 files changed, 47 insertions(+), 0 deletions(-)
create mode 100644 security/snet/snet_utils.c
create mode 100644 security/snet/snet_utils.h
diff --git a/security/snet/snet_utils.c b/security/snet/snet_utils.c
new file mode 100644
index 0000000..e9178d7
--- /dev/null
+++ b/security/snet/snet_utils.c
@@ -0,0 +1,38 @@
+#include <linux/types.h>
+#include <linux/snet.h>
+
+const char *snet_verdict_name(const enum snet_verdict cmd)
+{
+ static const char *const verdict_name[] = {
+ [SNET_VERDICT_GRANT] = "Grant",
+ [SNET_VERDICT_DENY] = "Deny",
+ [SNET_VERDICT_PENDING] = "Pending",
+ [SNET_VERDICT_NONE] = "None",
+ };
+
+ if (cmd >= SNET_NR_VERDICT_TYPES)
+ return "INVALID";
+ else
+ return verdict_name[cmd];
+}
+
+const char *snet_syscall_name(const enum snet_syscall sys)
+{
+ static const char *const syscall_name[] = {
+ [SNET_SOCKET_CREATE] = "Create",
+ [SNET_SOCKET_BIND] = "Bind",
+ [SNET_SOCKET_CONNECT] = "Connect",
+ [SNET_SOCKET_LISTEN] = "Listen",
+ [SNET_SOCKET_ACCEPT] = "Accept",
+ [SNET_SOCKET_POST_ACCEPT] = "Post Accept",
+ [SNET_SOCKET_SENDMSG] = "Sendmsg",
+ [SNET_SOCKET_RECVMSG] = "Recvmsg",
+ [SNET_SOCKET_SOCK_RCV_SKB] = "Sock Rcv Skb",
+ [SNET_SOCKET_CLOSE] = "Close",
+ };
+
+ if (sys >= SNET_NR_SOCKET_TYPES)
+ return "INVALID";
+ else
+ return syscall_name[sys];
+}
diff --git a/security/snet/snet_utils.h b/security/snet/snet_utils.h
new file mode 100644
index 0000000..01e515f
--- /dev/null
+++ b/security/snet/snet_utils.h
@@ -0,0 +1,9 @@
+#ifndef _SNET_UTILS_H
+#define _SNET_UTILS_H
+
+#include <linux/skbuff.h>
+
+const char *snet_verdict_name(const enum snet_verdict cmd);
+const char *snet_syscall_name(const enum snet_syscall sys);
+
+#endif /* _SNET_UTILS_H */
--
1.7.4.1
^ permalink raw reply related
* [RFC v3 08/10] snet: introduce snet_ticket
From: Samir Bellabes @ 2011-05-03 14:24 UTC (permalink / raw)
To: linux-security-module
Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
Evgeniy Polyakov, Grzegorz Nosek, Samir Bellabes
In-Reply-To: <1304432663-1575-1-git-send-email-sam@synack.fr>
this patch adds the snet's subsystem managing granted-access tickets
snet is using the term 'ticket' for refering to a structure which keeps
informations about verdict, coming from userspace.
generic informations:
timeout
syscall
protocol
verdict
protocol-dependant informations : (so some infos may not be used)
address family
socket type
source address
source port
distant address
distant port
ticket are attached to the "void *security" pointer of task_struct
there are 3 modes:
0. no ticket - SNET_TICKET_OFF
every syscalls has to be verified by userspace.
1. timeout fixed - SNET_TICKET_FIX
for each response from the userspace, we are creating a ticket,
attached to the task_struct, with the filled informations, and a
fixed timeout value (10 secs by default).
then before asking userspace, kernel mecanism is checking existing
tickets for the task_struct, if there is a granted-access ticket, we
are using the verdict value attached.
after the timeout value, the ticket is destroyed.
2. timeout with extendable value - SNET_TICKET_EXTEND
this is the same mecanism as 1, but every time a ticket is matched
and used, the timeout value is reset to the default value, so its
life is extended.
Signed-off-by: Samir Bellabes <sam@synack.fr>
---
security/snet/snet_ticket.c | 195 ++++++++++++++++++++++++++++++++++++
security/snet/snet_ticket.h | 37 +++++++
security/snet/snet_ticket_helper.c | 127 +++++++++++++++++++++++
security/snet/snet_ticket_helper.h | 8 ++
4 files changed, 367 insertions(+), 0 deletions(-)
create mode 100644 security/snet/snet_ticket.c
create mode 100644 security/snet/snet_ticket.h
create mode 100644 security/snet/snet_ticket_helper.c
create mode 100644 security/snet/snet_ticket_helper.h
diff --git a/security/snet/snet_ticket.c b/security/snet/snet_ticket.c
new file mode 100644
index 0000000..a260412
--- /dev/null
+++ b/security/snet/snet_ticket.c
@@ -0,0 +1,195 @@
+#include <linux/slab.h>
+#include <linux/cred.h>
+#include <linux/jhash.h>
+#include <linux/security.h>
+#include <linux/snet.h>
+#include "snet_ticket.h"
+#include "snet_ticket_helper.h"
+
+#define HSIZE 16
+
+static struct kmem_cache *snet_ticket_cachep;
+static struct kmem_cache *snet_task_security_cachep;
+
+enum snet_verdict snet_ticket_check(struct snet_info *info)
+{
+ struct snet_ticket *st = NULL;
+ unsigned int h = 0, verdict = SNET_VERDICT_NONE;
+ struct list_head *l = NULL;
+ struct snet_task_security *tsec = NULL;
+
+ if (snet_ticket_mode == SNET_TICKET_OFF)
+ goto out;
+
+ tsec = (struct snet_task_security*) current_security();
+
+ h = jhash_2words(info->syscall, info->protocol, 0) % HSIZE;
+ l = &tsec->hash[h];
+
+ read_lock_bh(&tsec->lock);
+ list_for_each_entry(st, l, list) {
+ if (__ticket_check(st, info)) {
+ verdict = st->verdict;
+ pr_debug("snet_ticket found: ticket=%p tsec=%p\n",
+ st, st->tsec);
+ if (snet_ticket_mode == SNET_TICKET_EXTEND) {
+ mod_timer(&st->timeout,
+ jiffies + snet_ticket_delay * HZ);
+ }
+ break;
+ }
+ }
+ read_unlock_bh(&tsec->lock);
+out:
+ return verdict;
+}
+
+static void snet_ticket_timeout(unsigned long arg)
+{
+ struct snet_ticket *st = (struct snet_ticket*)arg;
+
+ pr_debug("snet_ticket_timeout: ticket=%p tsec=%p\n", st, st->tsec);
+
+ write_lock_bh(&st->tsec->lock);
+ list_del(&st->list);
+ write_unlock_bh(&st->tsec->lock);
+ kmem_cache_free(snet_ticket_cachep, st);
+ return;
+}
+
+static struct snet_ticket *snet_ticket_alloc(void)
+{
+ struct snet_ticket *st = NULL;
+
+ st = kmem_cache_zalloc(snet_ticket_cachep, GFP_KERNEL);
+ if (st == NULL)
+ goto out;
+
+ INIT_LIST_HEAD(&st->list);
+ init_timer(&st->timeout);
+ st->timeout.expires = snet_ticket_delay * HZ;
+out:
+ return st;
+}
+
+static void snet_ticket_insert(struct snet_ticket *st)
+{
+ unsigned int h;
+ struct list_head *l;
+
+ h = jhash_2words(st->syscall, st->protocol, 0) % HSIZE;
+ l = &(st->tsec->hash[h]);
+
+ st->timeout.expires += jiffies;
+ add_timer(&st->timeout);
+
+ write_lock_bh(&(st->tsec->lock));
+ list_add_tail(&st->list, l);
+ write_unlock_bh(&(st->tsec->lock));
+ return;
+}
+
+void snet_ticket_create(struct snet_info *info, enum snet_verdict verdict)
+{
+ struct snet_ticket *st;
+ struct snet_task_security *tsec = NULL;
+
+ if (snet_ticket_mode == SNET_TICKET_OFF)
+ goto out;
+
+ tsec = (struct snet_task_security*) current_security();
+
+ st = snet_ticket_alloc();
+ if (st == NULL)
+ goto out;
+
+ st->tsec = tsec;
+ snet_ticket_fill(st, info, verdict);
+ setup_timer(&st->timeout, snet_ticket_timeout, (unsigned long)st);
+ snet_ticket_insert(st);
+out:
+ return;
+}
+
+int snet_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp)
+{
+ unsigned int index = 0;
+ struct snet_task_security *tsec = NULL;
+
+ tsec = kmem_cache_zalloc(snet_task_security_cachep, gfp);
+ if (tsec == NULL)
+ return -ENOMEM;
+
+ pr_debug("ticket_prepare_creds: pid=%u tsec=%p\n", current->pid, tsec);
+ rwlock_init(&tsec->lock);
+ for (index = 0; index < HSIZE; index++)
+ INIT_LIST_HEAD(&tsec->hash[index]);
+
+ new->security = tsec;
+ return 0;
+}
+
+void snet_cred_free(struct cred *cred)
+{
+ struct snet_task_security *tsec = cred->security;
+ unsigned int index;
+
+ pr_debug("ticket_free_cred: pid=%u tsec=%p\n", current->pid, tsec);
+
+ write_lock_bh(&tsec->lock);
+ /* destroy all tickets */
+ for (index = 0; index < HSIZE; index++) {
+ struct snet_ticket *st, *tmp;
+ list_for_each_entry_safe(st, tmp, &tsec->hash[index], list) {
+ if (del_timer_sync(&st->timeout)) {
+ pr_debug("ticket_cred_free: [%u] ticket=%p tsec=%p\n",
+ index, st, st->tsec);
+ list_del(&st->list);
+ kmem_cache_free(snet_ticket_cachep, st);
+ }
+ }
+ }
+ cred->security = NULL;
+ write_unlock_bh(&tsec->lock);
+ kmem_cache_free(snet_task_security_cachep, tsec);
+ return;
+}
+
+int snet_ticket_init(void)
+{
+ unsigned int index = 0;
+ struct cred *cred = (struct cred *) current->real_cred;
+ struct snet_task_security *tsec = NULL;
+
+ if (snet_ticket_mode >= SNET_TICKET_INVALID) {
+ printk(KERN_ERR "snet: bad snet_ticket_mode\n");
+ return -EINVAL;
+ }
+
+ if ((snet_ticket_mode == SNET_TICKET_FIX ||
+ snet_ticket_mode == SNET_TICKET_EXTEND) &&
+ (snet_ticket_delay == 0)) {
+ printk(KERN_ERR "snet: bad snet_ticket_delay\n");
+ return -EINVAL;
+ }
+
+ /* snet_ticket_cachep is not destroyed */
+ snet_ticket_cachep = kmem_cache_create("snet_ticket",
+ sizeof(struct snet_ticket),
+ 0, SLAB_PANIC, NULL);
+ /* snet_task_security_cachep is not destroyed */
+ snet_task_security_cachep = kmem_cache_create("snet_task_security",
+ sizeof(struct snet_task_security),
+ 0, SLAB_PANIC, NULL);
+
+ tsec = kmem_cache_zalloc(snet_task_security_cachep, GFP_KERNEL);
+ if (tsec == NULL)
+ return -ENOMEM;
+
+ rwlock_init(&tsec->lock);
+ for (index = 0; index < HSIZE; index++)
+ INIT_LIST_HEAD(&tsec->hash[index]);
+
+ cred->security = tsec;
+ return 0;
+}
diff --git a/security/snet/snet_ticket.h b/security/snet/snet_ticket.h
new file mode 100644
index 0000000..b6d1020
--- /dev/null
+++ b/security/snet/snet_ticket.h
@@ -0,0 +1,37 @@
+#ifndef _SNET_TICKET_H
+#define _SNET_TICKET_H
+
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/cred.h>
+#include <linux/snet.h>
+
+struct snet_task_security {
+ struct list_head hash[16];
+ rwlock_t lock;
+};
+
+struct snet_ticket {
+ struct list_head list;
+ struct snet_task_security *tsec;
+ struct timer_list timeout;
+
+ enum snet_syscall syscall;
+ u8 protocol;
+ u8 family;
+ int type;
+ struct snet_sock_half src;
+ struct snet_sock_half dst;
+ enum snet_verdict verdict;
+};
+
+extern unsigned int snet_ticket_delay;
+extern unsigned int snet_ticket_mode;
+
+void snet_cred_free(struct cred *cred);
+int snet_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp);
+enum snet_verdict snet_ticket_check(struct snet_info *info);
+void snet_ticket_create(struct snet_info *info, enum snet_verdict verdict);
+int snet_ticket_init(void);
+
+#endif /* _SNET_TICKET_H */
diff --git a/security/snet/snet_ticket_helper.c b/security/snet/snet_ticket_helper.c
new file mode 100644
index 0000000..2dc9b94
--- /dev/null
+++ b/security/snet/snet_ticket_helper.c
@@ -0,0 +1,127 @@
+#include <linux/sched.h>
+#include <linux/socket.h>
+#include <linux/snet.h>
+#include "snet_ticket.h"
+#include "snet_utils.h"
+
+static int check_create(struct snet_ticket *st, struct snet_info *info)
+{
+ return (st->type == info->type);
+}
+
+static int check_src(struct snet_ticket *st, struct snet_info *info)
+{
+ switch (info->family) {
+ case AF_INET:
+ if ((st->src.u3.ip == info->src.u3.ip) &&
+ (st->src.u.port == info->src.u.port))
+ return 1;
+ break;
+ case AF_INET6:
+ if ((!memcmp(&st->src.u3.ip6, &info->src.u3.ip6,
+ sizeof(info->src.u3.ip6))) &&
+ (st->src.u.port == info->src.u.port))
+ return 1;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int check_dst(struct snet_ticket *st, struct snet_info *info)
+{
+ switch (info->family) {
+ case AF_INET:
+ if ((st->dst.u3.ip == info->dst.u3.ip) &&
+ (st->dst.u.port == info->dst.u.port))
+ return 1;
+ break;
+ case AF_INET6:
+ if ((!memcmp(&st->dst.u3.ip6, &info->dst.u3.ip6,
+ sizeof(info->dst.u3.ip6))) &&
+ (st->dst.u.port == info->dst.u.port))
+ return 1;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int check_src_and_dst(struct snet_ticket *st, struct snet_info *info)
+{
+ return (check_src(st, info) && check_dst(st, info));
+}
+
+static int check_none(struct snet_ticket *st, struct snet_info *info)
+{
+ return 0;
+}
+
+int __ticket_check(struct snet_ticket *st, struct snet_info *info)
+{
+ static int (*ticket_df[])(struct snet_ticket *, struct snet_info *) = {
+ [SNET_SOCKET_CREATE] = &check_create,
+ [SNET_SOCKET_BIND] = &check_src,
+ [SNET_SOCKET_CONNECT] = &check_dst,
+ [SNET_SOCKET_LISTEN] = &check_src,
+ [SNET_SOCKET_ACCEPT] = &check_src,
+ [SNET_SOCKET_POST_ACCEPT] = &check_none,
+ [SNET_SOCKET_SENDMSG] = &check_src_and_dst,
+ [SNET_SOCKET_RECVMSG] = &check_src_and_dst,
+ [SNET_SOCKET_SOCK_RCV_SKB] = &check_src_and_dst,
+ [SNET_SOCKET_CLOSE] = &check_none,
+ };
+
+ if (info->syscall >= SNET_NR_SOCKET_TYPES)
+ return 0;
+ else {
+ if ((st->syscall == info->syscall) &&
+ (st->protocol == info->protocol) &&
+ (st->family == info->family) &&
+ ticket_df[info->syscall](st, info))
+ return 1;
+ else
+ return 0;
+ }
+}
+
+void snet_ticket_fill(struct snet_ticket *st, struct snet_info *info,
+ enum snet_verdict verdict)
+{
+ st->syscall = info->syscall;
+ st->protocol = info->protocol;
+ st->family = info->family;
+ st->src.u.port = info->src.u.port;
+ st->dst.u.port = info->dst.u.port;
+ st->verdict = verdict;
+
+ switch (info->family) {
+ case AF_INET:
+ st->src.u3.ip = info->src.u3.ip;
+ st->dst.u3.ip = info->dst.u3.ip;
+ pr_debug("ticket=%p [syscall=%s protocol=%u "
+ "family=%u %pI4:%u->%pI4:%u] verdict=%s | tsec=%p pid=%u\n",
+ st, snet_syscall_name(st->syscall), st->protocol,
+ st->family, &st->src.u3.ip, st->src.u.port,
+ &st->dst.u3.ip, st->dst.u.port,
+ snet_verdict_name(st->verdict), st->tsec, current->pid);
+ break;
+ case AF_INET6:
+ memcpy(&st->src.u3.ip6, &info->src.u3.ip6,
+ sizeof(info->src.u3.ip6));
+ memcpy(&st->dst.u3.ip6, &info->dst.u3.ip6,
+ sizeof(info->dst.u3.ip6));
+ pr_debug("ticket=%p [syscall=%s protocol=%u "
+ "family=%u %pI6:%u->%pI6:%u] verdict=%s | tsec=%p pid=%u\n",
+ st, snet_syscall_name(st->syscall), st->protocol,
+ st->family, &st->src.u3.ip6, st->src.u.port,
+ &st->dst.u3.ip6, st->dst.u.port,
+ snet_verdict_name(st->verdict), st->tsec, current->pid);
+ break;
+ default:
+ break;
+ }
+ return;
+}
diff --git a/security/snet/snet_ticket_helper.h b/security/snet/snet_ticket_helper.h
new file mode 100644
index 0000000..177bca5
--- /dev/null
+++ b/security/snet/snet_ticket_helper.h
@@ -0,0 +1,8 @@
+#ifndef _SNET_TICKET_HELPER_H
+#define _SNET_TICKET_HELPER_H
+
+void snet_ticket_fill(struct snet_ticket *st, struct snet_info *info,
+ enum snet_verdict verdict);
+int __ticket_check(struct snet_ticket *st, struct snet_info *info);
+
+#endif /* _SNET_TICKET_HELPER_H */
--
1.7.4.1
^ permalink raw reply related
* [RFC v3 07/10] snet: introduce snet_verdict
From: Samir Bellabes @ 2011-05-03 14:24 UTC (permalink / raw)
To: linux-security-module
Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
Evgeniy Polyakov, Grzegorz Nosek, Samir Bellabes
In-Reply-To: <1304432663-1575-1-git-send-email-sam@synack.fr>
This patch adds the snet's subsystem responsive of managing verdicts
snet is using the word 'verdict' for the returning value of LSM hooks.
Different states exist (grant/deny/pending/none).
This patch introduces a hashtable 'verdict_hash' and operations (set/get/search..)
in order to manage verdicts. Syscalls are waiting, inside a classical waitqueue,
for theirs verdicts or for a timeout. Timeout value and the default verdict
policy are configurable at boot or by the snet_netlink subsystem.
With the help of the communication's subsystem, verdicts are coming from userspace
Signed-off-by: Samir Bellabes <sam@synack.fr>
---
security/snet/snet_verdict.c | 203 ++++++++++++++++++++++++++++++++++++++++++
security/snet/snet_verdict.h | 23 +++++
2 files changed, 226 insertions(+), 0 deletions(-)
create mode 100644 security/snet/snet_verdict.c
create mode 100644 security/snet/snet_verdict.h
diff --git a/security/snet/snet_verdict.c b/security/snet/snet_verdict.c
new file mode 100644
index 0000000..b0811ac
--- /dev/null
+++ b/security/snet/snet_verdict.c
@@ -0,0 +1,203 @@
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/random.h>
+#include <linux/wait.h>
+#include <asm/atomic.h>
+#include <linux/snet.h>
+#include <linux/slab.h>
+#include "snet_verdict.h"
+
+static struct list_head *snet_vdh;
+static rwlock_t snet_vdh_lock = __RW_LOCK_UNLOCKED();
+
+struct snet_verdict_entry {
+ struct list_head list;
+ u32 verdict_id;
+ enum snet_verdict verdict;
+};
+
+static atomic_t value = ATOMIC_INIT(1);
+
+/* when waiting for a verdict, process is added to this queue */
+static DECLARE_WAIT_QUEUE_HEAD(snet_wq);
+
+static struct kmem_cache *snet_verdict_entry_cachep;
+
+/* lookup for a verdict - before using this function, lock snet_vdh_lock */
+static struct snet_verdict_entry *__snet_verdict_lookup(const u32 verdict_id)
+{
+ unsigned int h = 0;
+ struct list_head *l = NULL;
+ struct snet_verdict_entry *s = NULL;
+
+ h = verdict_id % snet_vdh_size;
+ l = &snet_vdh[h];
+
+ list_for_each_entry(s, l, list) {
+ if (s->verdict_id == verdict_id) {
+ return s;
+ }
+ }
+ return NULL;
+}
+
+const enum snet_verdict snet_verdict_wait(const u32 verdict_id)
+{
+ enum snet_verdict verdict = SNET_VERDICT_NONE;
+ long ret = 0;
+
+ ret = wait_event_timeout(snet_wq,
+ (verdict = snet_verdict_get(verdict_id))
+ != SNET_VERDICT_PENDING,
+ snet_verdict_delay * HZ);
+ if (ret)
+ return snet_verdict_get(verdict_id);
+ else
+ return SNET_VERDICT_NONE;
+}
+
+const enum snet_verdict snet_verdict_get(const u32 verdict_id)
+{
+ enum snet_verdict v = SNET_VERDICT_NONE;
+ struct snet_verdict_entry *data = NULL;
+
+ read_lock_bh(&snet_vdh_lock);
+ data = __snet_verdict_lookup(verdict_id);
+ if (data != NULL)
+ v = data->verdict;
+
+ read_unlock_bh(&snet_vdh_lock);
+ return v;
+}
+
+int snet_verdict_set(const u32 verdict_id, const enum snet_verdict verdict)
+{
+ struct snet_verdict_entry *data = NULL;
+ int ret = -EINVAL;
+
+ if (verdict >= SNET_NR_VERDICT_TYPES)
+ goto out;
+
+ write_lock_bh(&snet_vdh_lock);
+ data = __snet_verdict_lookup(verdict_id);
+ if (data != NULL) {
+ /* if verdict is already set because of
+ timeout, we won't modify it */
+ if (data->verdict == SNET_VERDICT_PENDING) {
+ data->verdict = verdict;
+ ret = 0;
+ }
+ }
+ write_unlock_bh(&snet_vdh_lock);
+ wake_up(&snet_wq);
+out:
+ return ret;
+}
+
+int snet_verdict_remove(const u32 verdict_id)
+{
+ struct snet_verdict_entry *data = NULL;
+
+ write_lock_bh(&snet_vdh_lock);
+ data = __snet_verdict_lookup(verdict_id);
+ if (data == NULL) {
+ write_unlock_bh(&snet_vdh_lock);
+ return -EINVAL;
+ }
+ pr_debug("(verdict_id=%u)\n", data->verdict_id);
+ list_del(&data->list);
+ write_unlock_bh(&snet_vdh_lock);
+ kmem_cache_free(snet_verdict_entry_cachep, data);
+ return 0;
+}
+
+int snet_verdict_insert(void)
+{
+ struct snet_verdict_entry *data = NULL;
+ unsigned int h = 0;
+ u32 verdict_id = 0;
+
+ data = kmem_cache_zalloc(snet_verdict_entry_cachep, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ do {
+ verdict_id = atomic_inc_return(&value);
+ } while (verdict_id == 0);
+
+ data->verdict_id = verdict_id;
+ data->verdict = SNET_VERDICT_PENDING;
+ INIT_LIST_HEAD(&(data->list));
+ h = data->verdict_id % snet_vdh_size;
+
+ write_lock_bh(&snet_vdh_lock);
+ list_add_tail(&data->list, &snet_vdh[h]);
+ pr_debug("[%u]=(verdict_id=%u)\n", h, data->verdict_id);
+ write_unlock_bh(&snet_vdh_lock);
+
+ return verdict_id;
+}
+
+void snet_verdict_flush(void)
+{
+ unsigned int i = 0;
+
+ write_lock_bh(&snet_vdh_lock);
+ for (i = 0; i < snet_vdh_size; i++) {
+ struct snet_verdict_entry *data, *tmp;
+ list_for_each_entry_safe(data, tmp, &snet_vdh[i], list) {
+ list_del(&data->list);
+ kmem_cache_free(snet_verdict_entry_cachep, data);
+ }
+ }
+ write_unlock_bh(&snet_vdh_lock);
+ return;
+}
+
+/* init function */
+int snet_verdict_init(void)
+{
+ int err = 0, i = 0;
+
+ if (snet_vdh_size == 0) {
+ printk(KERN_ERR "snet: bad snet_vdh_size value\n");
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (snet_verdict_delay == 0) {
+ printk(KERN_ERR "snet: bad snet_verdict_delay value\n");
+ err = -EINVAL;
+ goto out;
+ }
+
+ snet_vdh = kzalloc(sizeof(struct list_head) * snet_vdh_size,
+ GFP_KERNEL);
+ if (!snet_vdh) {
+ printk(KERN_WARNING
+ "snet: can't alloc memory for verdict\n");
+ err = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < snet_vdh_size; i++)
+ INIT_LIST_HEAD(&snet_vdh[i]);
+
+ /* snet_verdict_entry_cachep is not destroyed */
+ snet_verdict_entry_cachep = kmem_cache_create("snet_verdict_entry",
+ sizeof(struct snet_verdict_entry),
+ 0, SLAB_PANIC, NULL);
+out:
+ return err;
+}
+
+/* exit function */
+void snet_verdict_exit(void)
+{
+ if (snet_vdh) {
+ kfree(snet_vdh);
+ snet_vdh = NULL;
+ }
+
+ return;
+}
diff --git a/security/snet/snet_verdict.h b/security/snet/snet_verdict.h
new file mode 100644
index 0000000..07e8638
--- /dev/null
+++ b/security/snet/snet_verdict.h
@@ -0,0 +1,23 @@
+#ifndef _SNET_VERDICT_H
+#define _SNET_VERDICT_H
+
+extern unsigned int snet_vdh_size;
+extern unsigned int snet_verdict_delay;
+
+/* helper functions */
+const enum snet_verdict snet_verdict_wait(const u32 verdict_id);
+
+/* manipulate the verdicts hash table */
+const enum snet_verdict snet_verdict_get(const u32 verdict_id);
+int snet_verdict_set(const u32 verdict_id, const enum snet_verdict verdict);
+int snet_verdict_insert(void);
+int snet_verdict_remove(const u32 verdict_id);
+int snet_verdict_insert(void);
+void snet_verdict_flush(void);
+
+/* init function */
+int snet_verdict_init(void);
+/* exit function */
+void snet_verdict_exit(void);
+
+#endif /* _SNET_VERDICT_H */
--
1.7.4.1
^ permalink raw reply related
* [RFC v3 06/10] snet: introduce snet_netlink
From: Samir Bellabes @ 2011-05-03 14:24 UTC (permalink / raw)
To: linux-security-module
Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
Evgeniy Polyakov, Grzegorz Nosek, Samir Bellabes
In-Reply-To: <1304432663-1575-1-git-send-email-sam@synack.fr>
this patch adds the snet communication's subsystem.
snet_netlink is using genetlink for sending/receiving messages to/from userspace.
the genetlink operations permit to receive orders to manage the list of events
- events are values [syscall, protocol] - which is used to know which syscall
and protocol have to be protected. genl operations are also used to manage
communication of events to userspace, and to receive the related verdict
Signed-off-by: Samir Bellabes <sam@synack.fr>
---
security/snet/snet_netlink.c | 442 +++++++++++++++++++++++++++++++++++
security/snet/snet_netlink.h | 17 ++
security/snet/snet_netlink_helper.c | 220 +++++++++++++++++
security/snet/snet_netlink_helper.h | 7 +
4 files changed, 686 insertions(+), 0 deletions(-)
create mode 100644 security/snet/snet_netlink.c
create mode 100644 security/snet/snet_netlink.h
create mode 100644 security/snet/snet_netlink_helper.c
create mode 100644 security/snet/snet_netlink_helper.h
diff --git a/security/snet/snet_netlink.c b/security/snet/snet_netlink.c
new file mode 100644
index 0000000..0d6cd8b
--- /dev/null
+++ b/security/snet/snet_netlink.c
@@ -0,0 +1,442 @@
+#include <linux/sched.h>
+#include <net/genetlink.h>
+#include <linux/in6.h>
+#include <linux/snet.h>
+#include "snet_netlink.h"
+#include "snet_netlink_helper.h"
+#include "snet_verdict.h"
+#include "snet_event.h"
+#include "snet_utils.h"
+
+atomic_t snet_nl_seq = ATOMIC_INIT(0);
+uint32_t snet_nl_pid;
+static struct genl_family snet_genl_family;
+
+/*
+ * snet genetlink
+ */
+int snet_nl_send_event(struct snet_info *info)
+{
+ struct sk_buff *skb_rsp;
+ void *msg_head;
+ int ret = 0, sbs = -1;
+ size_t size = 0;
+
+ sbs = snet_nl_size_by_syscall(info);
+ if (sbs < 0)
+ return -EINVAL;
+
+ size = sbs +
+ 2 * nla_total_size(sizeof(u8)) +
+ 1 * nla_total_size(sizeof(u16)) +
+ (info->verdict_id ? 3 : 2) * nla_total_size(sizeof(u32));
+
+ skb_rsp = genlmsg_new(size, GFP_KERNEL);
+ if (skb_rsp == NULL)
+ return -ENOMEM;
+
+ msg_head = genlmsg_put(skb_rsp, snet_nl_pid,
+ atomic_inc_return(&snet_nl_seq),
+ &snet_genl_family, 0, SNET_C_VERDICT);
+ if (msg_head == NULL)
+ goto nla_put_failure;
+
+ pr_debug("verdict_id=0x%x syscall=%s protocol=%u "
+ "family=%u uid=%u pid=%u\n",
+ info->verdict_id, snet_syscall_name(info->syscall),
+ info->protocol, info->family, current_uid(), current->pid);
+
+ if (info->verdict_id)
+ NLA_PUT_U32(skb_rsp, SNET_A_VERDICT_ID, info->verdict_id);
+ NLA_PUT_U16(skb_rsp, SNET_A_SYSCALL, info->syscall);
+ NLA_PUT_U8(skb_rsp, SNET_A_PROTOCOL, info->protocol);
+ NLA_PUT_U8(skb_rsp, SNET_A_FAMILY, info->family);
+ NLA_PUT_U32(skb_rsp, SNET_A_UID, current_uid());
+ NLA_PUT_U32(skb_rsp, SNET_A_PID, current->pid);
+
+ ret = snet_nl_fill_by_syscall(skb_rsp, info);
+ if (ret != 0)
+ goto nla_put_failure;
+
+ ret = genlmsg_end(skb_rsp, msg_head);
+ if (ret < 0)
+ goto nla_put_failure;
+
+ return genlmsg_unicast(&init_net, skb_rsp, snet_nl_pid);
+
+nla_put_failure:
+ kfree_skb(skb_rsp);
+ return -ECONNABORTED;
+}
+
+/*
+ * snet genetlink functions
+ */
+
+static struct genl_family snet_genl_family = {
+ .id = GENL_ID_GENERATE,
+ .hdrsize = 0,
+ .name = SNET_GENL_NAME,
+ .version = SNET_GENL_VERSION,
+ .maxattr = SNET_A_MAX,
+};
+
+static const struct nla_policy snet_genl_policy[SNET_A_MAX + 1] = {
+ [SNET_A_VERSION] = { .type = NLA_U32 },
+ [SNET_A_VERDICT_ID] = { .type = NLA_U32 },
+ [SNET_A_FAMILY] = { .type = NLA_U8 },
+ [SNET_A_SYSCALL] = { .type = NLA_U16 },
+ [SNET_A_PROTOCOL] = { .type = NLA_U8 },
+ [SNET_A_UID] = { .type = NLA_U32 },
+ [SNET_A_PID] = { .type = NLA_U32 },
+ [SNET_A_TYPE] = { .type = NLA_U32 },
+ [SNET_A_IPV4SADDR] = { .type = NLA_BINARY,
+ .len = sizeof(struct in_addr) },
+ [SNET_A_IPV6SADDR] = { .type = NLA_BINARY,
+ .len = sizeof(struct in6_addr) },
+ [SNET_A_IPV4DADDR] = { .type = NLA_BINARY,
+ .len = sizeof(struct in_addr) },
+ [SNET_A_IPV6DADDR] = { .type = NLA_BINARY,
+ .len = sizeof(struct in6_addr) },
+ [SNET_A_SPORT] = { .type = NLA_U16 },
+ [SNET_A_DPORT] = { .type = NLA_U16 },
+ [SNET_A_VERDICT] = { .type = NLA_U8 },
+ [SNET_A_VERDICT_DELAY] = { .type = NLA_U32 },
+ [SNET_A_TICKET_DELAY] = { .type = NLA_U32 },
+ [SNET_A_TICKET_MODE] = { .type = NLA_U8 },
+};
+
+/**
+ * snet_nl_version - Handle a VERSION message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Process a user generated VERSION message and respond accordingly.
+ * Returns zero on success, negative values on failure.
+ */
+static int snet_nl_version(struct sk_buff *skb, struct genl_info *info)
+{
+ int ret = 0;
+ struct sk_buff *skb_rsp = NULL;
+ void *msg_head;
+
+ atomic_set(&snet_nl_seq, info->snd_seq);
+
+ skb_rsp = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (skb_rsp == NULL)
+ return -ENOMEM;
+ msg_head = genlmsg_put_reply(skb_rsp, info, &snet_genl_family,
+ 0, SNET_C_VERSION);
+ if (msg_head == NULL)
+ goto nla_put_failure;
+
+ NLA_PUT_U32(skb_rsp, SNET_A_VERSION, SNET_VERSION);
+
+ ret = genlmsg_end(skb_rsp, msg_head);
+ if (ret < 0)
+ goto nla_put_failure;
+
+ ret = genlmsg_reply(skb_rsp, info);
+ if (ret != 0)
+ goto nla_put_failure;
+ return 0;
+
+nla_put_failure:
+ kfree_skb(skb_rsp);
+ return ret;
+}
+
+/**
+ * snet_nl_register - Handle a REGISTER message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Notify the kernel that an application is listening for events.
+ * Returns zero on success, negative values on failure.
+ */
+static int snet_nl_register(struct sk_buff *skb, struct genl_info *info)
+{
+ int ret = 0;
+ u32 version = 0;
+
+ atomic_set(&snet_nl_seq, info->snd_seq);
+
+ if (!info->attrs[SNET_A_VERSION]) {
+ ret = -EINVAL;
+ goto out;
+ }
+ version = nla_get_u32(info->attrs[SNET_A_VERSION]);
+
+ if (version != SNET_VERSION) {
+ ret = -EPERM;
+ goto out;
+ }
+
+ if (snet_nl_pid > 0) {
+ ret = -ECONNREFUSED;
+ goto out;
+ }
+ snet_nl_pid = info->snd_pid;
+ pr_debug("pid=%u\n", snet_nl_pid);
+out:
+ return ret;
+}
+
+/**
+ * snet_nl_unregister - Handle a UNREGISTER message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Notify the kernel that the application is no more listening for events.
+ * Returns zero on success, negative values on failure.
+ */
+static int snet_nl_unregister(struct sk_buff *skb, struct genl_info *info)
+{
+ int ret = 0;
+
+ atomic_set(&snet_nl_seq, info->snd_seq);
+
+ if (snet_nl_pid == 0) {
+ ret = -ENOTCONN;
+ goto out;
+ }
+
+ snet_nl_pid = 0;
+ pr_debug("pid=%u\n", snet_nl_pid);
+out:
+ return ret;
+}
+
+/**
+ * snet_nl_insert - Handle a INSERT message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Insert a new event to the events' hashtable. Returns zero on success,
+ * negative values on failure.
+ */
+static int snet_nl_insert(struct sk_buff *skb, struct genl_info *info)
+{
+ int ret = 0;
+ enum snet_syscall syscall;
+ u8 protocol;
+
+ atomic_set(&snet_nl_seq, info->snd_seq);
+
+ if (!info->attrs[SNET_A_SYSCALL] || !info->attrs[SNET_A_PROTOCOL]) {
+ ret = -EINVAL;
+ goto out;
+ }
+ syscall = nla_get_u16(info->attrs[SNET_A_SYSCALL]);
+ protocol = nla_get_u8(info->attrs[SNET_A_PROTOCOL]);
+ ret = snet_event_insert(syscall, protocol);
+ pr_debug("syscall=%s protocol=%u insert=%s\n",
+ snet_syscall_name(syscall), protocol,
+ (ret == 0) ? "success" : "failed");
+out:
+ return ret;
+}
+
+/**
+ * snet_nl_remove - Handle a REMOVE message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Remove a event from the events' hastable. Returns zero on success,
+ * negative values on failure.
+ */
+static int snet_nl_remove(struct sk_buff *skb, struct genl_info *info)
+{
+ int ret = 0;
+ enum snet_syscall syscall;
+ u8 protocol;
+
+ atomic_set(&snet_nl_seq, info->snd_seq);
+
+ if (!info->attrs[SNET_A_SYSCALL] || !info->attrs[SNET_A_PROTOCOL]) {
+ ret = -EINVAL;
+ goto out;
+ }
+ syscall = nla_get_u16(info->attrs[SNET_A_SYSCALL]);
+ protocol = nla_get_u8(info->attrs[SNET_A_PROTOCOL]);
+ ret = snet_event_remove(syscall, protocol);
+ pr_debug("syscall=%s protocol=%u remove=%s\n",
+ snet_syscall_name(syscall), protocol,
+ (ret == 0) ? "success" : "failed");
+out:
+ return ret;
+}
+
+/**
+ * snet_nl_flush - Handle a FLUSH message
+ * @skb: the NETLINK buffer
+ * @info: the Generic NETLINK info block
+ *
+ * Description:
+ * Remove all events from the hashtable. Returns zero on success,
+ * negative values on failure.
+ */
+static int snet_nl_flush(struct sk_buff *skb, struct genl_info *info)
+{
+ atomic_set(&snet_nl_seq, info->snd_seq);
+ snet_event_flush();
+ return 0;
+}
+
+int snet_nl_list_fill_info(struct sk_buff *skb, u32 pid, u32 seq,
+ u32 flags, u8 protocol, enum snet_syscall syscall)
+{
+ void *hdr;
+
+ hdr = genlmsg_put(skb, pid, seq, &snet_genl_family, flags, SNET_C_LIST);
+ if (hdr == NULL)
+ return -1;
+
+ NLA_PUT_U16(skb, SNET_A_SYSCALL, syscall);
+ NLA_PUT_U8(skb, SNET_A_PROTOCOL, protocol);
+
+ return genlmsg_end(skb, hdr);
+
+nla_put_failure:
+ genlmsg_cancel(skb, hdr);
+ return -EMSGSIZE;
+}
+/**
+ * snet_nl_list - Handle a LIST message
+ * @skb: the NETLINK buffer
+ * @cb:
+ *
+ * Description:
+ * Process a user LIST message and respond. Returns zero on success,
+ * and negative values on error.
+ */
+static int snet_nl_list(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ unsigned int len = 0;
+
+ atomic_set(&snet_nl_seq, cb->nlh->nlmsg_seq);
+ len = snet_event_fill_info(skb, cb);
+ return len;
+}
+
+/**
+ * snet_nl_verdict - Handle a VERDICT message
+ * @skb: the NETLINK buffer
+ * @info the Generic NETLINK info block
+ *
+ * Description:
+ * Provides userspace with a VERDICT message, ie we are sending informations
+ * with this command. Userspace is sending the appropriate verdict for the
+ * event. Returns zero on success,and negative values on error.
+ */
+static int snet_nl_verdict(struct sk_buff *skb, struct genl_info *info)
+{
+ int ret = 0;
+ u32 verdict_id;
+ enum snet_verdict verdict;
+
+ atomic_set(&snet_nl_seq, info->snd_seq);
+
+ if (snet_nl_pid == 0) {
+ ret = -ENOTCONN;
+ goto out;
+ }
+
+ if (!info->attrs[SNET_A_VERDICT_ID] || !info->attrs[SNET_A_VERDICT]) {
+ ret = -EINVAL;
+ goto out;
+ }
+ verdict_id = nla_get_u32(info->attrs[SNET_A_VERDICT_ID]);
+ verdict = nla_get_u8(info->attrs[SNET_A_VERDICT]);
+ ret = snet_verdict_set(verdict_id, verdict);
+out:
+ return ret;
+}
+
+static int snet_nl_config(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ int ret = 0;
+
+ atomic_set(&snet_nl_seq, info->snd_seq);
+
+ if (info->attrs[SNET_A_VERDICT_DELAY]) {
+ unsigned int new = nla_get_u32(info->attrs[SNET_A_VERDICT_DELAY]);
+ if (new == 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+ snet_verdict_delay = new;
+ pr_debug("snet_nl_config: verdict_delay=%u\n", snet_verdict_delay);
+ }
+ if (info->attrs[SNET_A_TICKET_DELAY]) {
+ unsigned int new = nla_get_u32(info->attrs[SNET_A_TICKET_DELAY]);
+ if (new == 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+ snet_ticket_delay = new;
+ pr_debug("snet_nl_config: ticket_delay=%u\n", snet_ticket_delay);
+ }
+ if (info->attrs[SNET_A_TICKET_MODE]) {
+ unsigned int new = nla_get_u32(info->attrs[SNET_A_TICKET_MODE]);
+ if (new >= SNET_TICKET_INVALID) {
+ ret = -EINVAL;
+ goto out;
+ }
+ snet_ticket_mode = new;
+ pr_debug("snet_nl_config: ticket_mode=%u\n", snet_ticket_mode);
+ }
+out:
+ return ret;
+}
+
+#define SNET_GENL_OPS(_cmd, _flags, _policy, _op, _opname) \
+ { \
+ .cmd = _cmd, \
+ .flags = _flags, \
+ .policy = _policy, \
+ ._op = snet_nl_##_opname, \
+ }
+
+static struct genl_ops snet_genl_ops[] = {
+ SNET_GENL_OPS(SNET_C_VERSION, GENL_ADMIN_PERM, snet_genl_policy,
+ doit, version),
+ SNET_GENL_OPS(SNET_C_REGISTER, GENL_ADMIN_PERM, snet_genl_policy,
+ doit, register),
+ SNET_GENL_OPS(SNET_C_UNREGISTER, GENL_ADMIN_PERM, snet_genl_policy,
+ doit, unregister),
+ SNET_GENL_OPS(SNET_C_INSERT, GENL_ADMIN_PERM, snet_genl_policy,
+ doit, insert),
+ SNET_GENL_OPS(SNET_C_REMOVE, GENL_ADMIN_PERM, snet_genl_policy,
+ doit, remove),
+ SNET_GENL_OPS(SNET_C_FLUSH, GENL_ADMIN_PERM, snet_genl_policy,
+ doit, flush),
+ SNET_GENL_OPS(SNET_C_LIST, GENL_ADMIN_PERM, snet_genl_policy,
+ dumpit, list),
+ SNET_GENL_OPS(SNET_C_VERDICT, GENL_ADMIN_PERM, snet_genl_policy,
+ doit, verdict),
+ SNET_GENL_OPS(SNET_C_CONFIG, GENL_ADMIN_PERM, snet_genl_policy,
+ doit, config),
+};
+
+#undef SNET_GENL_OPS
+
+static __init int snet_netlink_init(void)
+{
+ return genl_register_family_with_ops(&snet_genl_family,
+ snet_genl_ops,
+ ARRAY_SIZE(snet_genl_ops));
+}
+
+void snet_netlink_exit(void)
+{
+ genl_unregister_family(&snet_genl_family);
+}
+
+__initcall(snet_netlink_init);
diff --git a/security/snet/snet_netlink.h b/security/snet/snet_netlink.h
new file mode 100644
index 0000000..5e80d7b
--- /dev/null
+++ b/security/snet/snet_netlink.h
@@ -0,0 +1,17 @@
+#ifndef _SNET_NETLINK_H
+#define _SNET_NETLINK_H
+
+#include <linux/in6.h>
+
+extern unsigned int snet_verdict_delay;
+extern unsigned int snet_ticket_delay;
+extern unsigned int snet_ticket_mode;
+
+int snet_nl_send_event(struct snet_info *info);
+
+int snet_nl_list_fill_info(struct sk_buff *skb, u32 pid, u32 seq,
+ u32 flags, u8 protocol, enum snet_syscall syscall);
+
+void snet_netlink_exit(void);
+
+#endif /* _SNET_NETLINK_H */
diff --git a/security/snet/snet_netlink_helper.c b/security/snet/snet_netlink_helper.c
new file mode 100644
index 0000000..d7d743c
--- /dev/null
+++ b/security/snet/snet_netlink_helper.c
@@ -0,0 +1,220 @@
+#include <net/netlink.h>
+#include <net/genetlink.h>
+#include <linux/in6.h>
+#include <linux/snet.h>
+
+static int fill_src(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+ int ret;
+ switch (info->family) {
+ case PF_INET:
+ ret = nla_put(skb_rsp, SNET_A_IPV4SADDR,
+ sizeof(struct in_addr), &(info->src.u3.ip));
+ if (ret != 0)
+ goto out;
+ break;
+ case PF_INET6:
+ ret = nla_put(skb_rsp, SNET_A_IPV6SADDR,
+ sizeof(struct in6_addr), &(info->src.u3.ip6));
+ if (ret != 0)
+ goto out;
+ break;
+ default:
+ break;
+ }
+ ret = nla_put_u16(skb_rsp, SNET_A_SPORT, info->src.u.port);
+out:
+ return ret;
+
+}
+
+static int fill_dst(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+ int ret;
+ switch (info->family) {
+ case PF_INET:
+ ret = nla_put(skb_rsp, SNET_A_IPV4DADDR,
+ sizeof(struct in_addr), &(info->dst.u3.ip));
+ if (ret != 0)
+ goto out;
+ break;
+ case PF_INET6:
+ ret = nla_put(skb_rsp, SNET_A_IPV6DADDR,
+ sizeof(struct in6_addr), &(info->dst.u3.ip6));
+ if (ret != 0)
+ goto out;
+ break;
+ default:
+ break;
+ }
+ ret = nla_put_u16(skb_rsp, SNET_A_DPORT, info->dst.u.port);
+out:
+ return ret;
+}
+
+static int snet_fill_create(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+ int ret;
+ ret = nla_put_u8(skb_rsp, SNET_A_TYPE, info->type);
+ return ret;
+}
+
+static int snet_fill_bind(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+ return fill_src(skb_rsp, info);
+}
+
+static int snet_fill_connect(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+ int ret;
+
+ ret = fill_src(skb_rsp, info);
+ if (ret != 0)
+ goto out;
+ ret = fill_dst(skb_rsp, info);
+out:
+ return ret;
+}
+
+static int snet_fill_listen(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+ return fill_src(skb_rsp, info);
+}
+
+static int snet_fill_accept(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+ return fill_src(skb_rsp, info);
+}
+
+static int snet_fill_post_accept(struct sk_buff *skb_rsp,
+ struct snet_info *info)
+{
+ int ret;
+
+ ret = fill_src(skb_rsp, info);
+ if (ret != 0)
+ goto out;
+ ret = fill_dst(skb_rsp, info);
+out:
+ return ret;
+}
+
+static int snet_fill_sendmsg(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+ int ret;
+
+ ret = fill_src(skb_rsp, info);
+ if (ret != 0)
+ goto out;
+ ret = fill_dst(skb_rsp, info);
+out:
+ return ret;
+}
+
+static int snet_fill_recvmsg(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+ int ret;
+
+ ret = fill_src(skb_rsp, info);
+ if (ret != 0)
+ goto out;
+ ret = fill_dst(skb_rsp, info);
+out:
+ return ret;
+}
+
+static int snet_fill_sock_rcv_skb(struct sk_buff *skb_rsp,
+ struct snet_info *info)
+{
+ int ret;
+
+ ret = fill_src(skb_rsp, info);
+ if (ret != 0)
+ goto out;
+ ret = fill_dst(skb_rsp, info);
+out:
+ return ret;
+}
+
+static int snet_fill_close(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+ int ret;
+
+ ret = fill_src(skb_rsp, info);
+ if (ret != 0)
+ goto out;
+ ret = fill_dst(skb_rsp, info);
+out:
+ return ret;
+}
+
+int snet_nl_fill_by_syscall(struct sk_buff *skb_rsp, struct snet_info *info)
+{
+ static int (*snet_df[])(struct sk_buff *, struct snet_info *) = {
+ [SNET_SOCKET_CREATE] = &snet_fill_create,
+ [SNET_SOCKET_BIND] = &snet_fill_bind,
+ [SNET_SOCKET_CONNECT] = &snet_fill_connect,
+ [SNET_SOCKET_LISTEN] = &snet_fill_listen,
+ [SNET_SOCKET_ACCEPT] = &snet_fill_accept,
+ [SNET_SOCKET_POST_ACCEPT] = &snet_fill_post_accept,
+ [SNET_SOCKET_SENDMSG] = &snet_fill_sendmsg,
+ [SNET_SOCKET_RECVMSG] = &snet_fill_recvmsg,
+ [SNET_SOCKET_SOCK_RCV_SKB] = &snet_fill_sock_rcv_skb,
+ [SNET_SOCKET_CLOSE] = &snet_fill_close,
+ };
+
+ if (info->syscall >= SNET_NR_SOCKET_TYPES)
+ return -EINVAL;
+ else
+ return snet_df[info->syscall](skb_rsp, info);
+}
+
+int snet_nl_size_by_syscall(struct snet_info *info)
+{
+ unsigned int size_addr4_port = nla_total_size(sizeof(struct in_addr)) +
+ nla_total_size(sizeof(u16));
+ unsigned int size_addr6_port = nla_total_size(sizeof(struct in6_addr)) +
+ nla_total_size(sizeof(u16));
+
+ unsigned int sbs4[] = {
+ [SNET_SOCKET_CREATE] = nla_total_size(sizeof(u8)),
+ [SNET_SOCKET_BIND] = size_addr4_port,
+ [SNET_SOCKET_CONNECT] = 2 * size_addr4_port,
+ [SNET_SOCKET_LISTEN] = size_addr4_port,
+ [SNET_SOCKET_ACCEPT] = size_addr4_port,
+ [SNET_SOCKET_POST_ACCEPT] = 2 * size_addr4_port,
+ [SNET_SOCKET_SENDMSG] = 2 * size_addr4_port,
+ [SNET_SOCKET_RECVMSG] = 2 * size_addr4_port,
+ [SNET_SOCKET_SOCK_RCV_SKB] = 2 * size_addr4_port,
+ [SNET_SOCKET_CLOSE] = 2 * size_addr4_port,
+ };
+
+ unsigned int sbs6[] = {
+ [SNET_SOCKET_CREATE] = nla_total_size(sizeof(u8)),
+ [SNET_SOCKET_BIND] = size_addr6_port,
+ [SNET_SOCKET_CONNECT] = 2 * size_addr6_port,
+ [SNET_SOCKET_LISTEN] = size_addr6_port,
+ [SNET_SOCKET_ACCEPT] = size_addr6_port,
+ [SNET_SOCKET_POST_ACCEPT] = 2 * size_addr6_port,
+ [SNET_SOCKET_SENDMSG] = 2 * size_addr6_port,
+ [SNET_SOCKET_RECVMSG] = 2 * size_addr6_port,
+ [SNET_SOCKET_SOCK_RCV_SKB] = 2 * size_addr6_port,
+ [SNET_SOCKET_CLOSE] = 2 * size_addr6_port,
+ };
+
+ if (info->syscall >= SNET_NR_SOCKET_TYPES)
+ return -EINVAL;
+ else {
+ switch (info->family) {
+ case AF_INET:
+ return sbs4[info->syscall];
+ break;
+ case AF_INET6:
+ return sbs6[info->syscall];
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ }
+}
diff --git a/security/snet/snet_netlink_helper.h b/security/snet/snet_netlink_helper.h
new file mode 100644
index 0000000..d12e563
--- /dev/null
+++ b/security/snet/snet_netlink_helper.h
@@ -0,0 +1,7 @@
+#ifndef _SNET_NETLINK_HELPER_H
+#define _SNET_NETLINK_HELPER_H
+
+int snet_nl_fill_by_syscall(struct sk_buff *skb_rsp, struct snet_info *info);
+int snet_nl_size_by_syscall(struct snet_info *info);
+
+#endif /* SNET_NETLINK_HELPER_H */
--
1.7.4.1
^ permalink raw reply related
* [RFC v3 05/10] snet: introduce snet_hooks
From: Samir Bellabes @ 2011-05-03 14:24 UTC (permalink / raw)
To: linux-security-module
Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
Evgeniy Polyakov, Grzegorz Nosek, Samir Bellabes
In-Reply-To: <1304432663-1575-1-git-send-email-sam@synack.fr>
This patch adds the snet LSM's subsystem
snet_hooks provides the security hook's functions and the security_operations
structure. Currently hook functions are only related to network stack.
For each hook function, there is a generic mecanism:
0. check if the event [syscall, protocol] is registered
1. prepare informations for userspace
2. send informations to userspace (snet_netlink)
3. wait for verdict from userspace (snet_verdict)
4. apply verdict for the syscall
steps 3 and 4 are only valid for LSM hooks which are returning a value (a way to
'filter' the syscall). For hooks returning 'void', steps 3 and 4 don't exist,
but snet sends security informations to userspace (step 2) to update the global
security policy.
Signed-off-by: Samir Bellabes <sam@synack.fr>
---
security/snet/snet_hooks.c | 722 ++++++++++++++++++++++++++++++++++++++++++++
security/snet/snet_hooks.h | 10 +
2 files changed, 732 insertions(+), 0 deletions(-)
create mode 100644 security/snet/snet_hooks.c
create mode 100644 security/snet/snet_hooks.h
diff --git a/security/snet/snet_hooks.c b/security/snet/snet_hooks.c
new file mode 100644
index 0000000..84ea5fc
--- /dev/null
+++ b/security/snet/snet_hooks.c
@@ -0,0 +1,722 @@
+/*
+ * snet_hook.c
+ *
+ * here are interesting informations which can be picked up from hooks.
+ *
+ *
+ * SOCKET_CREATE:
+ * family, type, protocol
+ * SOCKET_BIND:
+ * family, protocol, saddr, sport
+ * SOCKET_CONNECT:
+ * family, protocol, saddr, sport, daddr, dport
+ * SOCKET_LISTEN:
+ * family, protocol, saddr, sport
+ * SOCKET_ACCEPT:
+ * family, protocol, saddr, sport
+ *
+ * SOCKET_SENDMSG:
+ * SOCKET_RECVMSG:
+ * SOCKET_SOCK_RCV_SKB:
+ *
+ *
+ */
+
+#include <linux/skbuff.h>
+#include <linux/security.h>
+#include <linux/net.h>
+#include <net/sock.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <net/inet_sock.h>
+#include <linux/ipv6.h>
+#include <linux/snet.h>
+#include <linux/uio.h>
+#include "snet_hooks.h"
+#include "snet_verdict.h"
+#include "snet_netlink.h"
+#include "snet_event.h"
+#include "snet_ticket.h"
+
+static inline void snet_pr_tuple(struct snet_info *info)
+{
+ switch (info->family) {
+ case AF_INET:
+ pr_debug("%pI4:%u->%pI4:%u\n",
+ &info->src.u3.ip, info->src.u.port,
+ &info->dst.u3.ip, info->dst.u.port);
+ break;
+ case AF_INET6:
+ pr_debug("%pI6:%u->%pI6:%u\n",
+ &info->src.u3.ip6, info->src.u.port,
+ &info->dst.u3.ip6, info->dst.u.port);
+ break;
+ default:
+ break;
+ }
+ return;
+}
+
+static inline int snet_check_listeners(enum snet_verdict *verdict)
+{
+ if (snet_nl_pid == 0 || snet_nl_pid == current->pid ) {
+ if (verdict != NULL)
+ *verdict = SNET_VERDICT_GRANT;
+ return -1;
+ }
+ return 0;
+}
+
+static int snet_do_verdict(enum snet_verdict *verdict, struct snet_info *info)
+{
+ if (info->verdict_id == 0)
+ return -1;
+ /* sending networking informations to userspace */
+ if (snet_nl_send_event(info) == 0)
+ /* waiting for userspace reply or timeout */
+ *verdict = snet_verdict_wait(info->verdict_id);
+ /* removing verdict */
+ snet_verdict_remove(info->verdict_id);
+ return 0;
+}
+
+static void snet_do_send_event(struct snet_info *info)
+{
+ snet_nl_send_event(info);
+ return;
+}
+
+/*
+ * security operations helper functions
+ */
+
+/*
+ * security operations functions members
+ */
+
+static int snet_socket_create(int family, int type, int protocol, int kern)
+{
+ enum snet_verdict verdict = SNET_VERDICT_NONE;
+
+ /* if (kern) */
+ /* ; /\* do something smart *\/ */
+
+ if (snet_check_listeners(&verdict) < 0)
+ goto out;
+
+ if (snet_event_is_registered(SNET_SOCKET_CREATE, protocol)) {
+ struct snet_info info;
+
+ /* inserting verdict PENDING */
+ info.verdict_id = snet_verdict_insert();
+
+ /* prepare networking informations for userspace */
+ info.syscall = SNET_SOCKET_CREATE;
+ info.protocol = protocol;
+ info.family = family;
+ info.type = type;
+
+ pr_debug("family=%u type=%u protocol=%u kern=%u\n",
+ family, type, protocol, kern);
+
+ snet_do_verdict(&verdict, &info);
+
+ } else {
+ verdict = SNET_VERDICT_GRANT;
+ }
+
+ if (verdict == SNET_VERDICT_NONE)
+ verdict = snet_verdict_policy;
+
+out:
+ return verdict;
+}
+
+static int snet_socket_bind(struct socket *sock,
+ struct sockaddr *address, int addrlen)
+{
+ enum snet_verdict verdict = SNET_VERDICT_NONE;
+ u8 protocol = 0;
+
+ if (snet_check_listeners(&verdict) < 0)
+ goto out;
+
+ protocol = sock->sk->sk_protocol;
+
+ if (snet_event_is_registered(SNET_SOCKET_BIND, protocol)) {
+ struct snet_info info;
+ struct inet_sock *inet = inet_sk(sock->sk);
+ struct sockaddr_in *a = (struct sockaddr_in *) address;
+ struct sockaddr_in6 *a6 = (struct sockaddr_in6 *) address;
+
+ /* prepare networking informations for userspace */
+ info.syscall = SNET_SOCKET_BIND;
+ info.protocol = protocol;
+ info.family = sock->sk->sk_family;
+ info.dst.u.port = ntohs(inet->inet_dport);
+
+ switch (sock->sk->sk_family) {
+ case PF_INET:
+ info.src.u3.ip = a->sin_addr.s_addr;
+ info.dst.u3.ip = inet->inet_daddr;
+ info.src.u.port = ntohs(a->sin_port);
+ /* check tickets */
+ verdict = snet_ticket_check(&info) ;
+ if (verdict != SNET_VERDICT_NONE)
+ goto out;
+ /* inserting verdict PENDING */
+ info.verdict_id = snet_verdict_insert();
+ break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case PF_INET6:
+ memcpy(&info.src.u3.ip6, (void *)&a6->sin6_addr,
+ sizeof(info.src.u3.ip6));
+ memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+ sizeof(info.dst.u3.ip6));
+ info.src.u.port = ntohs(a6->sin6_port);
+ /* check tickets */
+ verdict = snet_ticket_check(&info);
+ if (verdict != SNET_VERDICT_NONE)
+ goto out;
+ /* inserting verdict PENDING */
+ info.verdict_id = snet_verdict_insert();
+ break;
+#endif
+ default:
+ verdict = SNET_VERDICT_NONE;
+ goto skip_send_wait;
+ break;
+ }
+ snet_pr_tuple(&info);
+ snet_do_verdict(&verdict, &info);
+ /* create ticket */
+ snet_ticket_create(&info, verdict);
+ } else {
+ verdict = SNET_VERDICT_GRANT;
+ }
+
+skip_send_wait:
+ if (verdict == SNET_VERDICT_NONE)
+ verdict = snet_verdict_policy;
+out:
+ return verdict;
+}
+
+static int snet_socket_connect(struct socket *sock,
+ struct sockaddr *address, int addrlen)
+{
+ enum snet_verdict verdict = SNET_VERDICT_NONE;
+ u8 protocol = 0;
+
+ if (snet_check_listeners(&verdict) < 0)
+ goto out;
+
+ protocol = sock->sk->sk_protocol;
+
+ if (snet_event_is_registered(SNET_SOCKET_CONNECT, protocol)) {
+ struct snet_info info;
+ struct inet_sock *inet = inet_sk(sock->sk);
+ struct sockaddr_in *a = (struct sockaddr_in *) address;
+ struct sockaddr_in6 *a6 = (struct sockaddr_in6 *) address;
+
+ /* prepare networking informations for userspace */
+ memset(&info, 0, sizeof(struct snet_info));
+ info.syscall = SNET_SOCKET_CONNECT;
+ info.protocol = protocol;
+ info.family = sock->sk->sk_family;
+ info.src.u.port = ntohs(inet->inet_sport);
+
+ switch (sock->sk->sk_family) {
+ case PF_INET:
+ info.src.u3.ip = inet->inet_saddr;
+ info.dst.u3.ip = a->sin_addr.s_addr;
+ info.dst.u.port = ntohs(a->sin_port);
+ /* check tickets */
+ verdict = snet_ticket_check(&info);
+ if (verdict != SNET_VERDICT_NONE)
+ goto out;
+ /* inserting verdict PENDING */
+ info.verdict_id = snet_verdict_insert();
+ break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case PF_INET6:
+ memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+ sizeof(info.src.u3.ip6));
+ memcpy(&info.dst.u3.ip6, (void *)&a6->sin6_addr,
+ sizeof(info.dst.u3.ip6));
+ info.dst.u.port = ntohs(a6->sin6_port);
+ /* check tickets */
+ verdict = snet_ticket_check(&info);
+ if (verdict != SNET_VERDICT_NONE)
+ goto out;
+ /* inserting verdict PENDING */
+ info.verdict_id = snet_verdict_insert();
+ break;
+#endif
+ default:
+ verdict = SNET_VERDICT_NONE;
+ goto skip_send_wait;
+ break;
+ }
+ snet_pr_tuple(&info);
+ snet_do_verdict(&verdict, &info);
+ /* create ticket */
+ snet_ticket_create(&info, verdict);
+ } else {
+ verdict = SNET_VERDICT_GRANT;
+ }
+
+skip_send_wait:
+ if (verdict == SNET_VERDICT_NONE)
+ verdict = snet_verdict_policy;
+out:
+ return verdict;
+}
+
+static int snet_socket_listen(struct socket *sock, int backlog)
+{
+ enum snet_verdict verdict = SNET_VERDICT_NONE;
+ u8 protocol = 0;
+
+ if (snet_check_listeners(&verdict) < 0)
+ goto out;
+
+ protocol = sock->sk->sk_protocol;
+
+ if (snet_event_is_registered(SNET_SOCKET_LISTEN, protocol)) {
+ struct snet_info info;
+ struct inet_sock *inet = inet_sk(sock->sk);
+
+ /* prepare networking informations for userspace */
+ memset(&info, 0, sizeof(struct snet_info));
+ info.syscall = SNET_SOCKET_LISTEN;
+ info.protocol = protocol;
+ info.family = sock->sk->sk_family;
+ info.src.u.port = ntohs(inet->inet_sport);
+ info.dst.u.port = ntohs(inet->inet_dport);
+
+ switch (sock->sk->sk_family) {
+ case PF_INET:
+ info.src.u3.ip = inet->inet_saddr;
+ info.dst.u3.ip = inet->inet_daddr;
+ /* check tickets */
+ verdict = snet_ticket_check(&info);
+ if (verdict != SNET_VERDICT_NONE)
+ goto out;
+ /* inserting verdict PENDING */
+ info.verdict_id = snet_verdict_insert();
+ break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case PF_INET6:
+ memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+ sizeof(info.src.u3.ip6));
+ memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+ sizeof(info.dst.u3.ip6));
+ /* check tickets */
+ verdict = snet_ticket_check(&info);
+ if (verdict != SNET_VERDICT_NONE)
+ goto out;
+ /* inserting verdict PENDING */
+ info.verdict_id = snet_verdict_insert();
+ break;
+#endif
+ default:
+ verdict = SNET_VERDICT_NONE;
+ goto skip_send_wait;
+ break;
+ }
+ snet_pr_tuple(&info);
+ snet_do_verdict(&verdict, &info);
+ /* create ticket */
+ snet_ticket_create(&info, verdict);
+ } else {
+ verdict = SNET_VERDICT_GRANT;
+ }
+
+skip_send_wait:
+ if (verdict == SNET_VERDICT_NONE)
+ verdict = snet_verdict_policy;
+out:
+ return verdict;
+}
+
+static int snet_socket_accept(struct socket *sock, struct socket *newsock)
+{
+ enum snet_verdict verdict = SNET_VERDICT_NONE;
+ u8 protocol = 0;
+
+ if (snet_check_listeners(&verdict) < 0)
+ goto out;
+
+ protocol = sock->sk->sk_protocol;
+
+ if (snet_event_is_registered(SNET_SOCKET_ACCEPT, protocol)) {
+ struct snet_info info;
+ struct inet_sock *inet = inet_sk(sock->sk);
+
+ /* prepare networking informations for userspace */
+ info.syscall = SNET_SOCKET_ACCEPT;
+ info.protocol = protocol;
+ info.family = sock->sk->sk_family;
+ info.src.u.port = ntohs(inet->inet_sport);
+ info.dst.u.port = ntohs(inet->inet_dport);
+
+ switch (sock->sk->sk_family) {
+ case PF_INET:
+ info.src.u3.ip = inet->inet_saddr;
+ info.dst.u3.ip = inet->inet_daddr;
+ /* check tickets */
+ verdict = snet_ticket_check(&info);
+ if (verdict != SNET_VERDICT_NONE)
+ goto out;
+ /* inserting verdict PENDING */
+ info.verdict_id = snet_verdict_insert();
+ break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case PF_INET6:
+ memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+ sizeof(info.src.u3.ip6));
+ memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+ sizeof(info.dst.u3.ip6));
+ /* check tickets */
+ verdict = snet_ticket_check(&info);
+ if (verdict != SNET_VERDICT_NONE)
+ goto out;
+ /* inserting verdict PENDING */
+ info.verdict_id = snet_verdict_insert();
+ break;
+#endif
+ default:
+ verdict = SNET_VERDICT_NONE;
+ goto skip_send_wait;
+ break;
+ }
+ snet_pr_tuple(&info);
+ snet_do_verdict(&verdict, &info);
+ /* create ticket */
+ snet_ticket_create(&info, verdict);
+ } else {
+ verdict = SNET_VERDICT_GRANT;
+ }
+
+skip_send_wait:
+ if (verdict == SNET_VERDICT_NONE)
+ verdict = snet_verdict_policy;
+out:
+ return verdict;
+}
+
+static void snet_socket_post_accept(struct socket *sock, struct socket *newsock)
+{
+ u8 protocol = 0;
+
+ if (snet_check_listeners(NULL) < 0)
+ goto out;
+
+ protocol = sock->sk->sk_protocol;
+
+ if (snet_event_is_registered(SNET_SOCKET_POST_ACCEPT, protocol)) {
+ struct snet_info info;
+ struct inet_sock *inet = inet_sk(newsock->sk);
+
+ /* prepare networking informations for userspace */
+ info.syscall = SNET_SOCKET_POST_ACCEPT;
+ info.protocol = protocol;
+ info.family = sock->sk->sk_family;
+ info.verdict_id = 0;
+ info.src.u.port = ntohs(inet->inet_sport);
+ info.dst.u.port = ntohs(inet->inet_dport);
+
+ switch (sock->sk->sk_family) {
+ case PF_INET:
+ info.src.u3.ip = inet->inet_saddr;
+ info.dst.u3.ip = inet->inet_daddr;
+ break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case PF_INET6:
+ memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+ sizeof(info.src.u3.ip6));
+ memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+ sizeof(info.dst.u3.ip6));
+ break;
+#endif
+ default:
+ goto out;
+ break;
+ }
+ snet_pr_tuple(&info);
+ snet_do_send_event(&info);
+ }
+out:
+ return;
+}
+
+static int snet_socket_sendmsg(struct socket *sock,
+ struct msghdr *msg, int size)
+{
+ enum snet_verdict verdict = SNET_VERDICT_NONE;
+ u8 protocol = 0;
+
+ if (snet_check_listeners(&verdict) < 0)
+ goto out;
+
+ protocol = sock->sk->sk_protocol;
+
+ if (snet_event_is_registered(SNET_SOCKET_SENDMSG, protocol)) {
+ struct snet_info info;
+ struct inet_sock *inet = inet_sk(sock->sk);
+
+ /* prepare networking informations for userspace */
+ info.syscall = SNET_SOCKET_SENDMSG;
+ info.protocol = protocol;
+ info.family = sock->sk->sk_family;
+ info.src.u.port = ntohs(inet->inet_sport);
+ info.dst.u.port = ntohs(inet->inet_dport);
+
+ switch (sock->sk->sk_family) {
+ case PF_INET:
+ info.src.u3.ip = inet->inet_saddr;
+ info.dst.u3.ip = inet->inet_daddr;
+ /* check tickets */
+ verdict = snet_ticket_check(&info);
+ if (verdict != SNET_VERDICT_NONE)
+ goto out;
+ /* inserting verdict PENDING */
+ info.verdict_id = snet_verdict_insert();
+ break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case PF_INET6:
+ memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+ sizeof(info.src.u3.ip6));
+ memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+ sizeof(info.dst.u3.ip6));
+ /* check tickets */
+ verdict = snet_ticket_check(&info);
+ if (verdict != SNET_VERDICT_NONE)
+ goto out;
+ /* inserting verdict PENDING */
+ info.verdict_id = snet_verdict_insert();
+ break;
+#endif
+ default:
+ verdict = SNET_VERDICT_NONE;
+ goto skip_send_wait;
+ break;
+ }
+ snet_pr_tuple(&info);
+ snet_do_verdict(&verdict, &info);
+ /* create ticket */
+ snet_ticket_create(&info, verdict);
+ } else {
+ verdict = SNET_VERDICT_GRANT;
+ }
+
+skip_send_wait:
+ if (verdict == SNET_VERDICT_NONE)
+ verdict = snet_verdict_policy;
+out:
+ return verdict;
+}
+
+static int snet_socket_recvmsg(struct socket *sock,
+ struct msghdr *msg, int size, int flags)
+{
+ enum snet_verdict verdict = SNET_VERDICT_NONE;
+ u8 protocol = 0;
+
+ if (snet_check_listeners(&verdict) < 0)
+ goto out;
+
+ protocol = sock->sk->sk_protocol;
+
+ if (snet_event_is_registered(SNET_SOCKET_RECVMSG, protocol)) {
+ struct snet_info info;
+ struct inet_sock *inet = inet_sk(sock->sk);
+
+ /* prepare networking informations for userspace */
+ info.syscall = SNET_SOCKET_RECVMSG;
+ info.protocol = protocol;
+ info.family = sock->sk->sk_family;
+ info.src.u.port = ntohs(inet->inet_sport);
+ info.dst.u.port = ntohs(inet->inet_dport);
+
+ switch (sock->sk->sk_family) {
+ case PF_INET:
+ info.src.u3.ip = inet->inet_saddr;
+ info.dst.u3.ip = inet->inet_daddr;
+ /* check tickets */
+ verdict = snet_ticket_check(&info);
+ if (verdict != SNET_VERDICT_NONE)
+ goto out;
+ /* inserting verdict PENDING */
+ info.verdict_id = snet_verdict_insert();
+ break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case PF_INET6:
+ memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+ sizeof(info.src.u3.ip6));
+ memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+ sizeof(info.dst.u3.ip6));
+ /* check tickets */
+ verdict = snet_ticket_check(&info);
+ if (verdict != SNET_VERDICT_NONE)
+ goto out;
+ /* inserting verdict PENDING */
+ info.verdict_id = snet_verdict_insert();
+ break;
+#endif
+ default:
+ verdict = SNET_VERDICT_NONE;
+ goto skip_send_wait;
+ break;
+ }
+ snet_pr_tuple(&info);
+ snet_do_verdict(&verdict, &info);
+ /* create ticket */
+ snet_ticket_create(&info, verdict);
+ } else {
+ verdict = SNET_VERDICT_GRANT;
+ }
+
+skip_send_wait:
+ if (verdict == SNET_VERDICT_NONE)
+ verdict = snet_verdict_policy;
+out:
+ return verdict;
+}
+
+static int snet_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+ enum snet_verdict verdict = SNET_VERDICT_NONE;
+ u8 protocol = 0;
+
+ if (snet_check_listeners(&verdict) < 0)
+ goto out;
+
+ protocol = sk->sk_protocol;
+
+ if (snet_event_is_registered(SNET_SOCKET_SOCK_RCV_SKB, protocol)) {
+ struct snet_info info;
+ struct inet_sock *inet = inet_sk(sk);
+
+ /* prepare networking informations for userspace */
+ info.syscall = SNET_SOCKET_SOCK_RCV_SKB;
+ info.protocol = protocol;
+ info.family = sk->sk_family;
+ info.src.u.port = ntohs(inet->inet_sport);
+ info.dst.u.port = ntohs(inet->inet_dport);
+
+ switch (sk->sk_family) {
+ case PF_INET:
+ /* inserting verdict PENDING */
+ /* info.verdict_id = snet_verdict_insert(); */
+
+ info.src.u3.ip = inet->inet_saddr;
+ info.dst.u3.ip = inet->inet_daddr;
+ break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case PF_INET6:
+ /* inserting verdict PENDING */
+ /* info.verdict_id = snet_verdict_insert(); */
+
+ memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+ sizeof(info.src.u3.ip6));
+ memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+ sizeof(info.dst.u3.ip6));
+ break;
+#endif
+ default:
+ verdict = SNET_VERDICT_NONE;
+ goto skip_send_wait;
+ break;
+ }
+ snet_pr_tuple(&info);
+ /* SNET_DOC_VERDICT(info); */
+ } else {
+ verdict = SNET_VERDICT_GRANT;
+ }
+
+skip_send_wait:
+ if (verdict == SNET_VERDICT_NONE)
+ verdict = snet_verdict_policy;
+out:
+ return verdict;
+}
+
+static void snet_socket_close(struct socket *sock)
+{
+ u8 protocol = 0;
+
+ if (sock == NULL || sock->sk == NULL) {
+ goto out;
+ }
+
+ if (snet_check_listeners(NULL) < 0)
+ goto out;
+
+ protocol = sock->sk->sk_protocol;
+
+ if (snet_event_is_registered(SNET_SOCKET_CLOSE, protocol)) {
+ struct snet_info info;
+ struct inet_sock *inet = inet_sk(sock->sk);
+
+ /* prepare networking informations for userspace */
+ info.syscall = SNET_SOCKET_CLOSE;
+ info.protocol = protocol;
+ info.family = sock->sk->sk_family;
+ info.verdict_id = 0;
+ info.src.u.port = ntohs(inet->inet_sport);
+ info.dst.u.port = ntohs(inet->inet_dport);
+
+ switch (sock->sk->sk_family) {
+ case PF_INET:
+ info.src.u3.ip = inet->inet_saddr;
+ info.dst.u3.ip = inet->inet_daddr;
+ break;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ case PF_INET6:
+ memcpy(&info.src.u3.ip6, (void *)&inet->pinet6->saddr,
+ sizeof(info.src.u3.ip6));
+ memcpy(&info.dst.u3.ip6, (void *)&inet->pinet6->daddr,
+ sizeof(info.dst.u3.ip6));
+ break;
+#endif
+ default:
+ goto out;
+ break;
+ }
+ snet_pr_tuple(&info);
+ snet_do_send_event(&info);
+ }
+out:
+ return;
+}
+
+static struct security_operations snet_security_ops = {
+ .name = "snet",
+
+ .socket_create = snet_socket_create,
+ .socket_bind = snet_socket_bind,
+ .socket_connect = snet_socket_connect,
+ .socket_listen = snet_socket_listen,
+ .socket_accept = snet_socket_accept,
+ .socket_post_accept = snet_socket_post_accept,
+ .socket_sendmsg = snet_socket_sendmsg,
+ .socket_recvmsg = snet_socket_recvmsg,
+ .socket_sock_rcv_skb = snet_socket_sock_rcv_skb,
+ .socket_close = snet_socket_close,
+
+ .cred_prepare = snet_prepare_creds,
+ .cred_free = snet_cred_free,
+};
+
+int snet_hooks_init(void)
+{
+ if (!security_module_enable(&snet_security_ops))
+ return 0;
+
+ if (register_security(&snet_security_ops))
+ panic("snet: failed to register security_ops\n");
+
+ return 0;
+}
diff --git a/security/snet/snet_hooks.h b/security/snet/snet_hooks.h
new file mode 100644
index 0000000..05fe5e8
--- /dev/null
+++ b/security/snet/snet_hooks.h
@@ -0,0 +1,10 @@
+#ifndef _SNET_HOOKS_H
+#define _SNET_HOOKS_H
+
+extern uint32_t snet_nl_pid;
+extern unsigned int snet_verdict_policy;
+
+/* init function */
+int snet_hooks_init(void);
+
+#endif /* _SNET_HOOK_H */
--
1.7.4.1
^ permalink raw reply related
* [RFC v3 04/10] snet: introduce snet_event
From: Samir Bellabes @ 2011-05-03 14:24 UTC (permalink / raw)
To: linux-security-module
Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
Evgeniy Polyakov, Grzegorz Nosek, Samir Bellabes
In-Reply-To: <1304432663-1575-1-git-send-email-sam@synack.fr>
This patch adds the snet's subsystem responsive of managing events
snet is using the word 'event' for a couple of values [syscall, protocol].
For example, [listen, tcp] or [sendmsg, dccp] are events.
This patch introduces a hastable 'event_hash' and operations (add/remove/search..)
in order to manage which events have to be protected.
With the help of the communication's subsystem, managing orders are coming from
userspace.
Signed-off-by: Samir Bellabes <sam@synack.fr>
---
security/snet/snet_event.c | 201 ++++++++++++++++++++++++++++++++++++++++++++
security/snet/snet_event.h | 21 +++++
2 files changed, 222 insertions(+), 0 deletions(-)
create mode 100644 security/snet/snet_event.c
create mode 100644 security/snet/snet_event.h
diff --git a/security/snet/snet_event.c b/security/snet/snet_event.c
new file mode 100644
index 0000000..7146d5a
--- /dev/null
+++ b/security/snet/snet_event.c
@@ -0,0 +1,201 @@
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/jhash.h>
+#include <linux/slab.h>
+#include <linux/netlink.h>
+#include <linux/snet.h>
+#include "snet_event.h"
+#include "snet_netlink.h"
+#include "snet_utils.h"
+
+static struct list_head *snet_evh;
+static DEFINE_RWLOCK(snet_evh_lock);
+
+struct snet_event_entry {
+ struct list_head list;
+ struct snet_event se;
+};
+
+static struct kmem_cache *snet_event_entry_cachep;
+
+/* lookup for a snet_evh - before using this function, lock snet_evh_lock */
+static struct snet_event_entry *__snet_event_lookup(const enum snet_syscall syscall,
+ const u8 protocol)
+{
+ unsigned int h = 0;
+ struct list_head *l;
+ struct snet_event_entry *s;
+
+ /* computing its hash value */
+ h = jhash_2words(syscall, protocol, 0) % snet_evh_size;
+ l = &snet_evh[h];
+
+ list_for_each_entry(s, l, list) {
+ if ((s->se.protocol == protocol) &&
+ (s->se.syscall == syscall)) {
+ return s;
+ }
+ }
+ return NULL;
+}
+
+int snet_event_fill_info(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ unsigned int i = 0, n = 0;
+ int ret = -1;
+ unsigned hashs_to_skip = cb->args[0];
+ unsigned events_to_skip = cb->args[1];
+ struct list_head *l;
+ struct snet_event_entry *s;
+
+ read_lock_bh(&snet_evh_lock);
+
+ for (i = 0; i < snet_evh_size; i++) {
+ if (i < hashs_to_skip)
+ continue;
+ l = &snet_evh[i];
+ n = 0;
+ list_for_each_entry(s, l, list) {
+ if (++n < events_to_skip)
+ continue;
+ ret = snet_nl_list_fill_info(skb,
+ NETLINK_CB(cb->skb).pid,
+ cb->nlh->nlmsg_seq,
+ NLM_F_MULTI,
+ s->se.protocol,
+ s->se.syscall);
+ if (ret < 0)
+ goto errout;
+ }
+ }
+
+errout:
+ read_unlock_bh(&snet_evh_lock);
+
+ cb->args[0] = i;
+ cb->args[1] = n;
+ return skb->len;
+}
+
+/*
+ * check if a event is registered or not
+ * return 1 if event is registered, 0 if not
+ */
+int snet_event_is_registered(const enum snet_syscall syscall, const u8 protocol)
+{
+ int ret = 0;
+
+ read_lock_bh(&snet_evh_lock);
+ if (__snet_event_lookup(syscall, protocol) != NULL)
+ ret = 1;
+ read_unlock_bh(&snet_evh_lock);
+ return ret;
+}
+
+/* adding a event */
+int snet_event_insert(const enum snet_syscall syscall, const u8 protocol)
+{
+ struct snet_event_entry *data = NULL;
+ unsigned int h = 0;
+ int err = 0;
+
+ data = kmem_cache_zalloc(snet_event_entry_cachep, GFP_KERNEL);
+ if (!data) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ write_lock_bh(&snet_evh_lock);
+ /* check if event is already registered */
+ if (__snet_event_lookup(syscall, protocol) != NULL) {
+ write_unlock_bh(&snet_evh_lock);
+ kmem_cache_free(snet_event_entry_cachep, data);
+ err = -EINVAL;
+ goto out;
+ }
+
+ data->se.syscall = syscall;
+ data->se.protocol = protocol;
+ INIT_LIST_HEAD(&(data->list));
+ h = jhash_2words(data->se.syscall, data->se.protocol, 0) % snet_evh_size;
+ list_add_tail(&data->list, &snet_evh[h]);
+ write_unlock_bh(&snet_evh_lock);
+ pr_debug("[%u]=(syscall=%s, protocol=%u)\n",
+ h, snet_syscall_name(syscall), protocol);
+out:
+ return err;
+}
+
+/* removing a event */
+int snet_event_remove(const enum snet_syscall syscall, const u8 protocol)
+{
+ struct snet_event_entry *data = NULL;
+
+ write_lock_bh(&snet_evh_lock);
+ data = __snet_event_lookup(syscall, protocol);
+ if (data == NULL) {
+ write_unlock_bh(&snet_evh_lock);
+ return -EINVAL;
+ }
+ pr_debug("(syscall=%s, protocol=%u)\n",
+ snet_syscall_name(syscall), protocol);
+ list_del(&data->list);
+ write_unlock_bh(&snet_evh_lock);
+ kmem_cache_free(snet_event_entry_cachep, data);
+ return 0;
+}
+
+/* flushing all events */
+void snet_event_flush(void)
+{
+ unsigned int i = 0;
+
+ write_lock_bh(&snet_evh_lock);
+ for (i = 0; i < snet_evh_size; i++) {
+ struct snet_event_entry *data, *tmp;
+ list_for_each_entry_safe(data, tmp, &snet_evh[i], list) {
+ list_del(&data->list);
+ kmem_cache_free(snet_event_entry_cachep, data);
+ }
+ }
+ write_unlock_bh(&snet_evh_lock);
+ return;
+}
+
+/* init function */
+int snet_event_init(void)
+{
+ int err = 0, i = 0;
+
+ if (snet_evh_size == 0) {
+ printk(KERN_ERR "snet: bad snet_evh_size value\n");
+ err = -EINVAL;
+ goto out;
+ }
+
+ snet_evh = kzalloc(sizeof(struct list_head) * snet_evh_size,
+ GFP_KERNEL);
+ if (!snet_evh) {
+ printk(KERN_WARNING
+ "snet: can't alloc memory for snet_evh\n");
+ err = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < snet_evh_size; i++)
+ INIT_LIST_HEAD(&snet_evh[i]);
+
+ /* snet_event_entry_cachep is not destroyed */
+ snet_event_entry_cachep = kmem_cache_create("snet_event_entry",
+ sizeof(struct snet_event_entry),
+ 0, SLAB_PANIC, NULL);
+out:
+ return err;
+}
+
+/* exit function */
+void snet_event_exit(void)
+{
+ kfree(snet_evh);
+ snet_evh = NULL;
+}
diff --git a/security/snet/snet_event.h b/security/snet/snet_event.h
new file mode 100644
index 0000000..fa991c7
--- /dev/null
+++ b/security/snet/snet_event.h
@@ -0,0 +1,21 @@
+#ifndef _SNET_EVENT_H
+#define _SNET_EVENT_H
+
+#include <linux/skbuff.h>
+
+extern unsigned int snet_evh_size;
+
+/* manipulate the events hash table */
+int snet_event_fill_info(struct sk_buff *skb, struct netlink_callback *cb);
+int snet_event_is_registered(const enum snet_syscall syscall, const u8 protocol);
+int snet_event_insert(const enum snet_syscall syscall, const u8 protocol);
+int snet_event_remove(const enum snet_syscall syscall, const u8 protocol);
+void snet_event_flush(void);
+void snet_event_dumpall(void);
+
+/* init function */
+int snet_event_init(void);
+/* exit funtion */
+void snet_event_exit(void);
+
+#endif /* _SNET_EVENT_H */
--
1.7.4.1
^ permalink raw reply related
* [RFC v3 03/10] snet: introduce snet_core
From: Samir Bellabes @ 2011-05-03 14:24 UTC (permalink / raw)
To: linux-security-module
Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
Evgeniy Polyakov, Grzegorz Nosek, Samir Bellabes
In-Reply-To: <1304432663-1575-1-git-send-email-sam@synack.fr>
This patch introduce snet_core.c, which provides functions to start and stop
snet's subsystems, and include/linux/snet.h, which provides interface with
userspace.
subsytems are:
- snet_hooks : LSM hooks
- snet_netlink : kernel-user communication (genetlink)
- snet_event : manages the list of protected syscalls
- snet_verdict : provides a waitqueue for syscalls and manage verdicts
from userspace
- snet_ticket : provides a granted-access ticket mecanism
Signed-off-by: Samir Bellabes <sam@synack.fr>
---
include/linux/snet.h | 117 +++++++++++++++++++++++++++++++++++++++++++++
security/snet/snet_core.c | 82 +++++++++++++++++++++++++++++++
2 files changed, 199 insertions(+), 0 deletions(-)
create mode 100644 include/linux/snet.h
create mode 100644 security/snet/snet_core.c
diff --git a/include/linux/snet.h b/include/linux/snet.h
new file mode 100644
index 0000000..580b6b6
--- /dev/null
+++ b/include/linux/snet.h
@@ -0,0 +1,117 @@
+#ifndef _LINUX_SNET_H
+#define _LINUX_SNET_H
+
+#include <linux/in6.h>
+
+#define SNET_VERSION 0x1
+#define SNET_NAME "snet"
+
+enum snet_syscall {
+ SNET_SOCKET_CREATE = 0,
+ SNET_SOCKET_BIND,
+ SNET_SOCKET_CONNECT,
+ SNET_SOCKET_LISTEN,
+ SNET_SOCKET_ACCEPT,
+ SNET_SOCKET_POST_ACCEPT,
+ SNET_SOCKET_SENDMSG,
+ SNET_SOCKET_RECVMSG,
+ SNET_SOCKET_SOCK_RCV_SKB,
+ SNET_SOCKET_CLOSE,
+ SNET_SOCKET_INVALID,
+};
+
+#define SNET_NR_SOCKET_TYPES SNET_SOCKET_INVALID
+
+struct snet_event {
+ enum snet_syscall syscall;
+ u8 protocol;
+};
+
+enum snet_verdict {
+ SNET_VERDICT_GRANT = 0, /* grant the syscall */
+ SNET_VERDICT_DENY, /* deny the syscall */
+ SNET_VERDICT_PENDING, /* waiting for a decision */
+ SNET_VERDICT_NONE, /* no decision can be set */
+ SNET_VERDICT_INVALID,
+};
+
+#define SNET_NR_VERDICT_TYPES SNET_VERDICT_INVALID
+
+enum snet_ticket_mode {
+ SNET_TICKET_OFF = 0,
+ SNET_TICKET_FIX,
+ SNET_TICKET_EXTEND,
+ SNET_TICKET_INVALID,
+};
+
+/* genetlink commands */
+enum {
+ SNET_C_UNSPEC,
+ SNET_C_VERSION,
+ SNET_C_REGISTER,
+ SNET_C_UNREGISTER,
+ SNET_C_INSERT,
+ SNET_C_REMOVE,
+ SNET_C_FLUSH,
+ SNET_C_LIST,
+ SNET_C_VERDICT,
+ SNET_C_CONFIG,
+ __SNET_C_MAX,
+};
+
+#define SNET_C_MAX (__SNET_C_MAX - 1)
+
+/* genetlink attributes */
+enum {
+ SNET_A_UNSPEC,
+ SNET_A_VERSION, /* (NLA_U32) the snet protocol version */
+ SNET_A_VERDICT_ID,
+ SNET_A_FAMILY,
+ SNET_A_SYSCALL, /* (NLA_U8) a syscall identifier */
+ SNET_A_PROTOCOL, /* (NLA_U8) a protocol identifier */
+ SNET_A_UID,
+ SNET_A_PID,
+ SNET_A_TYPE,
+ SNET_A_IPV4SADDR,
+ SNET_A_IPV6SADDR,
+ SNET_A_IPV4DADDR,
+ SNET_A_IPV6DADDR,
+ SNET_A_SPORT,
+ SNET_A_DPORT,
+ SNET_A_VERDICT,
+ SNET_A_VERDICT_DELAY,
+ SNET_A_TICKET_DELAY,
+ SNET_A_TICKET_MODE,
+ __SNET_A_MAX,
+};
+
+#define SNET_A_MAX (__SNET_A_MAX - 1)
+
+#define SNET_GENL_NAME "SNET"
+#define SNET_GENL_VERSION SNET_VERSION
+
+struct snet_sock_half {
+ struct {
+ union {
+ __be32 ip;
+ struct in6_addr ip6;
+ };
+ } u3;
+ struct {
+ __be16 port;
+ } u;
+};
+
+struct snet_info {
+ u32 verdict_id;
+
+ enum snet_syscall syscall;
+ u8 protocol;
+ u8 family;
+
+ int type;
+ struct snet_sock_half src;
+ struct snet_sock_half dst;
+};
+
+#endif /* _LINUX_SNET_H */
diff --git a/security/snet/snet_core.c b/security/snet/snet_core.c
new file mode 100644
index 0000000..949ecaa
--- /dev/null
+++ b/security/snet/snet_core.c
@@ -0,0 +1,82 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <net/genetlink.h>
+#include <linux/snet.h>
+#include "snet_hooks.h"
+#include "snet_event.h"
+#include "snet_verdict.h"
+#include "snet_ticket.h"
+#include "snet_utils.h"
+
+unsigned int snet_evh_size = 16;
+module_param(snet_evh_size, uint, 0400);
+MODULE_PARM_DESC(snet_evh_size, "Set the size of the event hash table");
+
+unsigned int snet_vdh_size = 16;
+module_param(snet_vdh_size, uint, 0400);
+MODULE_PARM_DESC(snet_vdh_size, "Set the size of the verdict hash table");
+
+unsigned int snet_verdict_delay = 5;
+module_param(snet_verdict_delay, uint, 0600);
+MODULE_PARM_DESC(snet_verdict_delay, "Set the timeout for verdicts in secs");
+
+unsigned int snet_verdict_policy = SNET_VERDICT_GRANT; /* permissive by default */
+module_param(snet_verdict_policy, uint, 0400);
+MODULE_PARM_DESC(snet_verdict_policy, "Set the default verdict");
+
+unsigned int snet_ticket_delay = 15;
+module_param(snet_ticket_delay, uint, 0600);
+MODULE_PARM_DESC(snet_ticket_delay, "Set the timeout for tickets in secs");
+
+unsigned int snet_ticket_mode = SNET_TICKET_FIX;
+module_param(snet_ticket_mode, uint, 0600);
+MODULE_PARM_DESC(snet_ticket_mode, "Set the mode for tickets");
+
+static __init int snet_init(void)
+{
+ int ret;
+
+ pr_debug("initializing: event_hash_size=%u "
+ "verdict_hash_size=%u verdict_delay=%usecs "
+ "default_policy=%s\n",
+ snet_evh_size, snet_vdh_size, snet_verdict_delay,
+ snet_verdict_name(snet_verdict_policy));
+
+ if (snet_verdict_policy >= SNET_VERDICT_INVALID) {
+ printk(KERN_ERR "snet: bad snet_verdict_policy\n");
+ ret = -EINVAL;
+ goto event_failed;
+ }
+
+ ret = snet_event_init();
+ if (ret < 0)
+ goto event_failed;
+
+ ret = snet_verdict_init();
+ if (ret < 0)
+ goto verdict_failed;
+
+ ret = snet_ticket_init();
+ if (ret < 0)
+ goto ticket_failed;
+
+ /* snet_hooks_init() returns 0 or execute panic() */
+ snet_hooks_init();
+
+ pr_debug("started\n");
+ return 0;
+
+ticket_failed:
+ snet_verdict_exit();
+verdict_failed:
+ snet_event_exit();
+event_failed:
+ pr_debug("stopped\n");
+ return ret;
+}
+
+security_initcall(snet_init);
+
+MODULE_DESCRIPTION("snet - Security for NETwork syscalls");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Samir Bellabes <sam@synack.fr>");
--
1.7.4.1
^ permalink raw reply related
* [RFC v3 01/10] lsm: add security_socket_closed()
From: Samir Bellabes @ 2011-05-03 14:24 UTC (permalink / raw)
To: linux-security-module
Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
Evgeniy Polyakov, Grzegorz Nosek, Samir Bellabes
In-Reply-To: <1304432663-1575-1-git-send-email-sam@synack.fr>
Allow a module to update security informations when a socket is closed.
Signed-off-by: Samir Bellabes <sam@synack.fr>
---
include/linux/security.h | 10 ++++++++++
net/socket.c | 1 +
security/capability.c | 5 +++++
security/security.c | 5 +++++
4 files changed, 21 insertions(+), 0 deletions(-)
diff --git a/include/linux/security.h b/include/linux/security.h
index ca02f17..da0d59e 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -918,6 +918,9 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* @sock contains the socket structure.
* @how contains the flag indicating how future sends and receives are handled.
* Return 0 if permission is granted.
+ * @socket_close:
+ * Allow a module to update security informations when a socket is closed
+ * @sock is closed.
* @socket_sock_rcv_skb:
* Check permissions on incoming network packets. This hook is distinct
* from Netfilter's IP input hooks since it is the first time that the
@@ -1593,6 +1596,7 @@ struct security_operations {
int (*socket_getsockopt) (struct socket *sock, int level, int optname);
int (*socket_setsockopt) (struct socket *sock, int level, int optname);
int (*socket_shutdown) (struct socket *sock, int how);
+ void (*socket_close) (struct socket *sock);
int (*socket_sock_rcv_skb) (struct sock *sk, struct sk_buff *skb);
int (*socket_getpeersec_stream) (struct socket *sock, char __user *optval, int __user *optlen, unsigned len);
int (*socket_getpeersec_dgram) (struct socket *sock, struct sk_buff *skb, u32 *secid);
@@ -2559,6 +2563,7 @@ int security_socket_getpeername(struct socket *sock);
int security_socket_getsockopt(struct socket *sock, int level, int optname);
int security_socket_setsockopt(struct socket *sock, int level, int optname);
int security_socket_shutdown(struct socket *sock, int how);
+void security_socket_close(struct socket *sock);
int security_sock_rcv_skb(struct sock *sk, struct sk_buff *skb);
int security_socket_getpeersec_stream(struct socket *sock, char __user *optval,
int __user *optlen, unsigned len);
@@ -2674,6 +2679,11 @@ static inline int security_socket_shutdown(struct socket *sock, int how)
{
return 0;
}
+
+static inline void security_socket_close(struct socket *sock)
+{
+}
+
static inline int security_sock_rcv_skb(struct sock *sk,
struct sk_buff *skb)
{
diff --git a/net/socket.c b/net/socket.c
index 310d16b..d588e9e 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -1093,6 +1093,7 @@ static int sock_close(struct inode *inode, struct file *filp)
printk(KERN_DEBUG "sock_close: NULL inode\n");
return 0;
}
+ security_socket_close(SOCKET_I(inode));
sock_release(SOCKET_I(inode));
return 0;
}
diff --git a/security/capability.c b/security/capability.c
index 2984ea4..1f8bbe2 100644
--- a/security/capability.c
+++ b/security/capability.c
@@ -629,6 +629,10 @@ static int cap_socket_shutdown(struct socket *sock, int how)
return 0;
}
+static void cap_socket_close(struct socket *sock)
+{
+}
+
static int cap_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
{
return 0;
@@ -1025,6 +1029,7 @@ void __init security_fixup_ops(struct security_operations *ops)
set_to_cap_if_null(ops, socket_setsockopt);
set_to_cap_if_null(ops, socket_getsockopt);
set_to_cap_if_null(ops, socket_shutdown);
+ set_to_cap_if_null(ops, socket_close);
set_to_cap_if_null(ops, socket_sock_rcv_skb);
set_to_cap_if_null(ops, socket_getpeersec_stream);
set_to_cap_if_null(ops, socket_getpeersec_dgram);
diff --git a/security/security.c b/security/security.c
index 1011423..84187d8 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1074,6 +1074,11 @@ int security_socket_shutdown(struct socket *sock, int how)
return security_ops->socket_shutdown(sock, how);
}
+void security_socket_close(struct socket *sock)
+{
+ return security_ops->socket_close(sock);
+}
+
int security_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
{
return security_ops->socket_sock_rcv_skb(sk, skb);
--
1.7.4.1
^ permalink raw reply related
* [RFC v3 00/10] snet: Security for NETwork syscalls
From: Samir Bellabes @ 2011-05-03 14:24 UTC (permalink / raw)
To: linux-security-module
Cc: linux-kernel, netdev, netfilter-devel, jamal, Patrick McHardy,
Evgeniy Polyakov, Grzegorz Nosek, Samir Bellabes
Hello lsm and netdev people,
This set of patches is the version 3 of snet, which I would like to submit as a
RFC.
snet is a linux security module. It provides a mecanism defering syscall
security hooks and decision (verdict) to userspace.
snet has some subsystems :
- snet_core : init and exit the system
- snet_hooks : LSM hooks
- snet_netlink : kernel-user communication (genetlink)
- snet_event : manages the list of protected syscalls
- snet_verdict : provides a waitqueue for syscalls and manage verdicts
- snet_ticket : provides a granted-access ticket mecanism
I believe that snet will help to get over the classical configuration
complexity of others security modules, by providing interactivity to users.
I also think that monolithic strategy is broken with snet, as we can provide
security for others syscall's categories:
- sfs : security for filesystem,
- stask: security for task,
- smem : security for memory
..
In this way, and by putting abstraction on how this subsystems can talk to each
others, we may use the security combinaison we want: choose to run sfs,
stask, but not snet nor smem. Better, developpers may investigated how to build
another security subsystem for tasks, and use others existing (smem, snet..)
which they don't want to modify
I think that interactivity is very usefull for users, as they may be notify when
something is wrong and take decision, and from userspace, the decision may be
defered to another box. In this way, snet also have a advantage for mobile
devices as the policy decision will be push to a distant server, mobile device
will then wait for verdicts and as policy strategies are centralized.
Interactivity is *not* only clicking a Yes/No question, as said, we
can centralised previous locals LSM security subsytems, and make the
network aware of events occuring on it.
Finally, and a important point: snet integration respects the LSM framework idea
of using LSM hooks.
New feature from the previous version:
* Building a ticket mecanism for each task_struct using pointer void *security
Use the pointer (void*) security related to task_struct to provides
granted-acces tickets: if two identical requests are coming, ask the user
for the first one, store the result in a ticket and for the second request,
just look in the tickets owned by the task-struct
* send data buffer of sendmsg to userspace
this may provide a way to look inside the data (as a anti-virus do)
roadmap:
* find a way to send data buffer of sendmsg to userspace (using netfilter)
* adding other security systems
we can think about adding fork(), exec(), open(), close()..
I'm Ccing netfilter-devel, as snet may be seen as a way to do filtering.
Last devel patchs were:
* using kmem_cache instead of kmalloc
* remove attend to send buffer socker to userspace
Samir Bellabes (10):
lsm: add security_socket_closed()
Revert "lsm: Remove the socket_post_accept() hook"
snet: introduce snet_core
snet: introduce snet_event
snet: introduce snet_hooks
snet: introduce snet_netlink
snet: introduce snet_verdict
snet: introduce snet_ticket
snet: introduce snet_utils
snet: introduce security/snet, Makefile and Kconfig changes
include/linux/security.h | 23 ++
include/linux/snet.h | 117 ++++++
net/socket.c | 3 +
security/Kconfig | 6 +
security/Makefile | 2 +
security/capability.c | 10 +
security/security.c | 10 +
security/snet/Kconfig | 11 +
security/snet/Makefile | 14 +
security/snet/snet_core.c | 82 ++++
security/snet/snet_event.c | 201 ++++++++++
security/snet/snet_event.h | 21 +
security/snet/snet_hooks.c | 722 +++++++++++++++++++++++++++++++++++
security/snet/snet_hooks.h | 10 +
security/snet/snet_netlink.c | 442 +++++++++++++++++++++
security/snet/snet_netlink.h | 17 +
security/snet/snet_netlink_helper.c | 220 +++++++++++
security/snet/snet_netlink_helper.h | 7 +
security/snet/snet_ticket.c | 195 ++++++++++
security/snet/snet_ticket.h | 37 ++
security/snet/snet_ticket_helper.c | 127 ++++++
security/snet/snet_ticket_helper.h | 8 +
security/snet/snet_utils.c | 38 ++
security/snet/snet_utils.h | 9 +
security/snet/snet_verdict.c | 203 ++++++++++
security/snet/snet_verdict.h | 23 ++
26 files changed, 2558 insertions(+), 0 deletions(-)
create mode 100644 include/linux/snet.h
create mode 100644 security/snet/Kconfig
create mode 100644 security/snet/Makefile
create mode 100644 security/snet/snet_core.c
create mode 100644 security/snet/snet_event.c
create mode 100644 security/snet/snet_event.h
create mode 100644 security/snet/snet_hooks.c
create mode 100644 security/snet/snet_hooks.h
create mode 100644 security/snet/snet_netlink.c
create mode 100644 security/snet/snet_netlink.h
create mode 100644 security/snet/snet_netlink_helper.c
create mode 100644 security/snet/snet_netlink_helper.h
create mode 100644 security/snet/snet_ticket.c
create mode 100644 security/snet/snet_ticket.h
create mode 100644 security/snet/snet_ticket_helper.c
create mode 100644 security/snet/snet_ticket_helper.h
create mode 100644 security/snet/snet_utils.c
create mode 100644 security/snet/snet_utils.h
create mode 100644 security/snet/snet_verdict.c
create mode 100644 security/snet/snet_verdict.h
--
1.7.4.1
^ permalink raw reply
* [net-next 1/5] bnx2x: link report improvements
From: Dmitry Kravkov @ 2011-05-03 13:38 UTC (permalink / raw)
To: davem, netdev@vger.kernel.org; +Cc: Vladislav Zolotarov, Eilon Greenstein
From: Vladislav Zolotarov <vladz@broadcom.com>
To avoid link notification duplications
Signed-off-by: Dmitry Kravkov <dmitry@broadcom.com>
Signed-off-by: Vladislav Zolotarov <vladz@broadcom.com>
Signed-off-by: Eilon Greenstein <eilong@broadcom.com>
---
drivers/net/bnx2x/bnx2x.h | 21 ++++++++
drivers/net/bnx2x/bnx2x_cmn.c | 99 +++++++++++++++++++++++++++++++--------
drivers/net/bnx2x/bnx2x_cmn.h | 2 -
drivers/net/bnx2x/bnx2x_main.c | 21 ++++-----
4 files changed, 108 insertions(+), 35 deletions(-)
diff --git a/drivers/net/bnx2x/bnx2x.h b/drivers/net/bnx2x/bnx2x.h
index b7ff87b..6f3d48f 100644
--- a/drivers/net/bnx2x/bnx2x.h
+++ b/drivers/net/bnx2x/bnx2x.h
@@ -893,6 +893,22 @@ typedef enum {
(&bp->def_status_blk->sp_sb.\
index_values[HC_SP_INDEX_EQ_CONS])
+/* This is a data that will be used to create a link report message.
+ * We will keep the data used for the last link report in order
+ * to prevent reporting the same link parameters twice.
+ */
+struct bnx2x_link_report_data {
+ u16 line_speed; /* Effective line speed */
+ unsigned long link_report_flags;/* BNX2X_LINK_REPORT_XXX flags */
+};
+
+enum {
+ BNX2X_LINK_REPORT_FD, /* Full DUPLEX */
+ BNX2X_LINK_REPORT_LINK_DOWN,
+ BNX2X_LINK_REPORT_RX_FC_ON,
+ BNX2X_LINK_REPORT_TX_FC_ON,
+};
+
struct bnx2x {
/* Fields used in the tx and intr/napi performance paths
* are grouped together in the beginning of the structure
@@ -1026,6 +1042,9 @@ struct bnx2x {
struct link_params link_params;
struct link_vars link_vars;
+ u32 link_cnt;
+ struct bnx2x_link_report_data last_reported_link;
+
struct mdio_if_info mdio;
struct bnx2x_common common;
@@ -1442,6 +1461,8 @@ struct bnx2x_func_init_params {
#define WAIT_RAMROD_POLL 0x01
#define WAIT_RAMROD_COMMON 0x02
+void bnx2x_read_mf_cfg(struct bnx2x *bp);
+
/* dmae */
void bnx2x_read_dmae(struct bnx2x *bp, u32 src_addr, u32 len32);
void bnx2x_write_dmae(struct bnx2x *bp, dma_addr_t dma_addr, u32 dst_addr,
diff --git a/drivers/net/bnx2x/bnx2x_cmn.c b/drivers/net/bnx2x/bnx2x_cmn.c
index e83ac6d..267177b 100644
--- a/drivers/net/bnx2x/bnx2x_cmn.c
+++ b/drivers/net/bnx2x/bnx2x_cmn.c
@@ -758,35 +758,91 @@ u16 bnx2x_get_mf_speed(struct bnx2x *bp)
return line_speed;
}
+/* Fill Link report data according to the current link
+ * configuration.
+ *
+ * @param bp
+ * @param data
+ */
+static inline void bnx2x_fill_report_data(struct bnx2x *bp,
+ struct bnx2x_link_report_data *data)
+{
+ u16 line_speed = bnx2x_get_mf_speed(bp);
+
+ memset(data, 0, sizeof(*data));
+
+ /* Fill the report data: efective line speed */
+ data->line_speed = line_speed;
+
+ /* Link is down */
+ if (!bp->link_vars.link_up || (bp->flags & MF_FUNC_DIS))
+ set_bit(BNX2X_LINK_REPORT_LINK_DOWN, &data->link_report_flags);
+
+ /* Full DUPLEX */
+ if (bp->link_vars.duplex == DUPLEX_FULL)
+ set_bit(BNX2X_LINK_REPORT_FD, &data->link_report_flags);
+
+ /* Rx Flow Control is ON */
+ if (bp->link_vars.flow_ctrl & BNX2X_FLOW_CTRL_RX)
+ set_bit(BNX2X_LINK_REPORT_RX_FC_ON, &data->link_report_flags);
+
+ /* Tx Flow Control is ON */
+ if (bp->link_vars.flow_ctrl & BNX2X_FLOW_CTRL_TX)
+ set_bit(BNX2X_LINK_REPORT_TX_FC_ON, &data->link_report_flags);
+}
+
void bnx2x_link_report(struct bnx2x *bp)
{
- if (bp->flags & MF_FUNC_DIS) {
- netif_carrier_off(bp->dev);
- netdev_err(bp->dev, "NIC Link is Down\n");
- return;
- }
+ struct bnx2x_link_report_data cur_data;
- if (bp->link_vars.link_up) {
- u16 line_speed;
+ /* reread mf_cfg */
+ if (!CHIP_IS_E1(bp))
+ bnx2x_read_mf_cfg(bp);
- if (bp->state == BNX2X_STATE_OPEN)
- netif_carrier_on(bp->dev);
- netdev_info(bp->dev, "NIC Link is Up, ");
+ /* Read the current link report info */
+ bnx2x_fill_report_data(bp, &cur_data);
+
+ /* Don't report link down or exactly the same link status twice */
+ if (!memcmp(&cur_data, &bp->last_reported_link, sizeof(cur_data)) ||
+ (test_bit(BNX2X_LINK_REPORT_LINK_DOWN,
+ &bp->last_reported_link.link_report_flags) &&
+ test_bit(BNX2X_LINK_REPORT_LINK_DOWN,
+ &cur_data.link_report_flags)))
+ return;
- line_speed = bnx2x_get_mf_speed(bp);
+ bp->link_cnt++;
- pr_cont("%d Mbps ", line_speed);
+ /* We are going to report a new link parameters now -
+ * remember the current data for the next time.
+ */
+ memcpy(&bp->last_reported_link, &cur_data, sizeof(cur_data));
- if (bp->link_vars.duplex == DUPLEX_FULL)
+ if (test_bit(BNX2X_LINK_REPORT_LINK_DOWN,
+ &cur_data.link_report_flags)) {
+ netif_carrier_off(bp->dev);
+ netdev_err(bp->dev, "NIC Link is Down\n");
+ return;
+ } else {
+ netif_carrier_on(bp->dev);
+ netdev_info(bp->dev, "NIC Link is Up, ");
+ pr_cont("%d Mbps ", cur_data.line_speed);
+
+ if (test_and_clear_bit(BNX2X_LINK_REPORT_FD,
+ &cur_data.link_report_flags))
pr_cont("full duplex");
else
pr_cont("half duplex");
- if (bp->link_vars.flow_ctrl != BNX2X_FLOW_CTRL_NONE) {
- if (bp->link_vars.flow_ctrl & BNX2X_FLOW_CTRL_RX) {
+ /* Handle the FC at the end so that only these flags would be
+ * possibly set. This way we may easily check if there is no FC
+ * enabled.
+ */
+ if (cur_data.link_report_flags) {
+ if (test_bit(BNX2X_LINK_REPORT_RX_FC_ON,
+ &cur_data.link_report_flags)) {
pr_cont(", receive ");
- if (bp->link_vars.flow_ctrl &
- BNX2X_FLOW_CTRL_TX)
+ if (test_bit(BNX2X_LINK_REPORT_TX_FC_ON,
+ &cur_data.link_report_flags))
pr_cont("& transmit ");
} else {
pr_cont(", transmit ");
@@ -794,10 +850,6 @@ void bnx2x_link_report(struct bnx2x *bp)
pr_cont("flow control ON");
}
pr_cont("\n");
-
- } else { /* link_down */
- netif_carrier_off(bp->dev);
- netdev_err(bp->dev, "NIC Link is Down\n");
}
}
@@ -1345,6 +1397,11 @@ int bnx2x_nic_load(struct bnx2x *bp, int load_mode)
bp->state = BNX2X_STATE_OPENING_WAIT4_LOAD;
+ /* Set the initial link reported state to link down */
+ memset(&bp->last_reported_link, 0, sizeof(bp->last_reported_link));
+ set_bit(BNX2X_LINK_REPORT_LINK_DOWN,
+ &bp->last_reported_link.link_report_flags);
+
/* must be called before memory allocation and HW init */
bnx2x_ilt_set_info(bp);
diff --git a/drivers/net/bnx2x/bnx2x_cmn.h b/drivers/net/bnx2x/bnx2x_cmn.h
index ef37b98..10cf462 100644
--- a/drivers/net/bnx2x/bnx2x_cmn.h
+++ b/drivers/net/bnx2x/bnx2x_cmn.h
@@ -67,8 +67,6 @@ void bnx2x__link_status_update(struct bnx2x *bp);
* Report link status to upper layer
*
* @param bp
- *
- * @return int
*/
void bnx2x_link_report(struct bnx2x *bp);
diff --git a/drivers/net/bnx2x/bnx2x_main.c b/drivers/net/bnx2x/bnx2x_main.c
index 9d48659..bc847b1 100644
--- a/drivers/net/bnx2x/bnx2x_main.c
+++ b/drivers/net/bnx2x/bnx2x_main.c
@@ -2043,7 +2043,7 @@ static int bnx2x_get_cmng_fns_mode(struct bnx2x *bp)
return CMNG_FNS_NONE;
}
-static void bnx2x_read_mf_cfg(struct bnx2x *bp)
+void bnx2x_read_mf_cfg(struct bnx2x *bp)
{
int vn, n = (CHIP_MODE_IS_4_PORT(bp) ? 2 : 1);
@@ -2130,7 +2130,6 @@ static inline void bnx2x_link_sync_notify(struct bnx2x *bp)
/* This function is called upon link interrupt */
static void bnx2x_link_attn(struct bnx2x *bp)
{
- u32 prev_link_status = bp->link_vars.link_status;
/* Make sure that we are synced with the current statistics */
bnx2x_stats_handle(bp, STATS_EVENT_STOP);
@@ -2175,17 +2174,15 @@ static void bnx2x_link_attn(struct bnx2x *bp)
"single function mode without fairness\n");
}
+ bnx2x_link_report(bp);
+
if (IS_MF(bp))
bnx2x_link_sync_notify(bp);
-
- /* indicate link status only if link status actually changed */
- if (prev_link_status != bp->link_vars.link_status)
- bnx2x_link_report(bp);
}
void bnx2x__link_status_update(struct bnx2x *bp)
{
- if ((bp->state != BNX2X_STATE_OPEN) || (bp->flags & MF_FUNC_DIS))
+ if (bp->state != BNX2X_STATE_OPEN)
return;
bnx2x_link_status_update(&bp->link_params, &bp->link_vars);
@@ -2195,10 +2192,6 @@ void bnx2x__link_status_update(struct bnx2x *bp)
else
bnx2x_stats_handle(bp, STATS_EVENT_STOP);
- /* the link status update could be the result of a DCC event
- hence re-read the shmem mf configuration */
- bnx2x_read_mf_cfg(bp);
-
/* indicate link status */
bnx2x_link_report(bp);
}
@@ -3127,10 +3120,14 @@ static inline void bnx2x_attn_int_deasserted3(struct bnx2x *bp, u32 attn)
if (val & DRV_STATUS_SET_MF_BW)
bnx2x_set_mf_bw(bp);
- bnx2x__link_status_update(bp);
if ((bp->port.pmf == 0) && (val & DRV_STATUS_PMF))
bnx2x_pmf_update(bp);
+ /* Always call it here: bnx2x_link_report() will
+ * prevent the link indication duplication.
+ */
+ bnx2x__link_status_update(bp);
+
if (bp->port.pmf &&
(val & DRV_STATUS_DCBX_NEGOTIATION_RESULTS) &&
bp->dcbx_enabled > 0)
--
1.7.2.2
^ permalink raw reply related
* [net-next 0/5] bnx2x enhancements and date update
From: Dmitry Kravkov @ 2011-05-03 13:37 UTC (permalink / raw)
To: davem, netdev@vger.kernel.org; +Cc: Eilon Greenstein, Vladislav Zolotarov
Hi Dave,
This is the re-spin of previously submitted series.
Please consider applying it to the "net-next".
Thank you,
Dmitry
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox