Re: [RFC PATCH v2 net-next 07/12] net: ipv4: listified version of ip_rcv

2018-06-27 Thread Florian Westphal
Edward Cree  wrote:
> Also involved adding a way to run a netfilter hook over a list of packets.
>  Rather than attempting to make netfilter know about lists (which would be
>  a major project in itself) we just let it call the regular okfn (in this
>  case ip_rcv_finish()) for any packets it steals, and have it give us back
>  a list of packets it's synchronously accepted (which normally NF_HOOK
>  would automatically call okfn() on, but we want to be able to potentially
>  pass the list to a listified version of okfn().)

okfn() is only used during async reinject in NFQUEUE case,
skb is queued in kernel and we'll wait for a verdict from a userspace
process.  If thats ACCEPT, then okfn() gets called to reinject the skb
into the network stack.

A normal -j ACCEPT doesn't call okfn in the netfilter core, which is why
this occurs on '1' retval in NF_HOOK().

Only other user of okfn() is bridge netfilter, so listified version of
okfn() doesn't make too much sense to me, its not used normally
(unless such listified version makes the code simpler of course).

AFAICS its fine to unlink/free skbs from the list to handle
drops/queueing etc. so a future version of nf_hook() could propagate the
list into nf_hook_slow and mangle the list there to deal with hooks
that steal/drop/queue skbs.

Later on we can pass the list to the hook functions themselves.

We'll have to handle non-accept verdicts in-place in the hook functions
for this, but fortunately most hookfns only return NF_ACCEPT so I think
it is manageable.

I'll look into this once the series makes it to net-next.


[RFC PATCH v2 net-next 07/12] net: ipv4: listified version of ip_rcv

2018-06-26 Thread Edward Cree
Also involved adding a way to run a netfilter hook over a list of packets.
 Rather than attempting to make netfilter know about lists (which would be
 a major project in itself) we just let it call the regular okfn (in this
 case ip_rcv_finish()) for any packets it steals, and have it give us back
 a list of packets it's synchronously accepted (which normally NF_HOOK
 would automatically call okfn() on, but we want to be able to potentially
 pass the list to a listified version of okfn().)
The netfilter hooks themselves are indirect calls that still happen per-
 packet (see nf_hook_entry_hookfn()), but again, changing that can be left
 for future work.

There is potential for out-of-order receives if the netfilter hook ends up
 synchronously stealing packets, as they will be processed before any
 accepts earlier in the list.  However, it was already possible for an
 asynchronous accept to cause out-of-order receives, so presumably this is
 considered OK.

Signed-off-by: Edward Cree 
---
 include/linux/netdevice.h |  3 ++
 include/linux/netfilter.h | 27 +
 include/net/ip.h  |  2 ++
 net/core/dev.c| 11 +--
 net/ipv4/af_inet.c|  1 +
 net/ipv4/ip_input.c   | 75 ++-
 6 files changed, 110 insertions(+), 9 deletions(-)

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 105087369779..5296354fa621 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -2290,6 +2290,9 @@ struct packet_type {
 struct net_device *,
 struct packet_type *,
 struct net_device *);
+   void(*list_func) (struct sk_buff_head *,
+ struct packet_type *,
+ struct net_device *);
bool(*id_match)(struct packet_type *ptype,
struct sock *sk);
void*af_packet_priv;
diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h
index dd2052f0efb7..42395a8a6e70 100644
--- a/include/linux/netfilter.h
+++ b/include/linux/netfilter.h
@@ -288,6 +288,22 @@ NF_HOOK(uint8_t pf, unsigned int hook, struct net *net, 
struct sock *sk, struct
return ret;
 }
 
+static inline void
+NF_HOOK_LIST(uint8_t pf, unsigned int hook, struct net *net, struct sock *sk,
+struct sk_buff_head *list, struct sk_buff_head *sublist,
+struct net_device *in, struct net_device *out,
+int (*okfn)(struct net *, struct sock *, struct sk_buff *))
+{
+   struct sk_buff *skb;
+
+   __skb_queue_head_init(sublist); /* list of synchronously ACCEPTed skbs 
*/
+   while ((skb = __skb_dequeue(list)) != NULL) {
+   int ret = nf_hook(pf, hook, net, sk, skb, in, out, okfn);
+   if (ret == 1)
+   __skb_queue_tail(sublist, skb);
+   }
+}
+
 /* Call setsockopt() */
 int nf_setsockopt(struct sock *sk, u_int8_t pf, int optval, char __user *opt,
  unsigned int len);
@@ -369,6 +385,17 @@ NF_HOOK(uint8_t pf, unsigned int hook, struct net *net, 
struct sock *sk,
return okfn(net, sk, skb);
 }
 
+static inline void
+NF_HOOK_LIST(uint8_t pf, unsigned int hook, struct net *net, struct sock *sk,
+struct sk_buff_head *list, struct sk_buff_head *sublist,
+struct net_device *in, struct net_device *out,
+int (*okfn)(struct net *, struct sock *, struct sk_buff *))
+{
+   __skb_queue_head_init(sublist);
+   /* Move everything to the sublist */
+   skb_queue_splice_init(list, sublist);
+}
+
 static inline int nf_hook(u_int8_t pf, unsigned int hook, struct net *net,
  struct sock *sk, struct sk_buff *skb,
  struct net_device *indev, struct net_device *outdev,
diff --git a/include/net/ip.h b/include/net/ip.h
index 0d2281b4b27a..fb3dfed537c0 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -138,6 +138,8 @@ int ip_build_and_send_pkt(struct sk_buff *skb, const struct 
sock *sk,
  struct ip_options_rcu *opt);
 int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt,
   struct net_device *orig_dev);
+void ip_list_rcv(struct sk_buff_head *list, struct packet_type *pt,
+struct net_device *orig_dev);
 int ip_local_deliver(struct sk_buff *skb);
 int ip_mr_input(struct sk_buff *skb);
 int ip_output(struct net *net, struct sock *sk, struct sk_buff *skb);
diff --git a/net/core/dev.c b/net/core/dev.c
index 2f46ed07c8d8..f0eb00e9fb57 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -4684,8 +4684,15 @@ static inline void __netif_receive_skb_list_ptype(struct 
sk_buff_head *list,
 {
struct sk_buff *skb;
 
-   while ((skb = __skb_dequeue(list)) != NULL)
-