![Eric W. Biederman](/assets/img/avatar_default.png)
Add code to nf_unregister_hook to flush the nf_queue when a hook is unregistered. This guarantees that the pointer that the nf_queue code retains into the nf_hook list will remain valid while a packet is queued. I tested what would happen if we do not flush queued packets and was trivially able to obtain the oops below. All that was required was to stop the nf_queue listening process, to delete all of the nf_tables, and to awaken the nf_queue listening process. > BUG: unable to handle kernel paging request at 0000000100000001 > IP: [<0000000100000001>] 0x100000001 > PGD b9c35067 PUD 0 > Oops: 0010 [#1] SMP > Modules linked in: > CPU: 0 PID: 519 Comm: lt-nfqnl_test Not tainted > task: ffff8800b9c8c050 ti: ffff8800ba9d8000 task.ti: ffff8800ba9d8000 > RIP: 0010:[<0000000100000001>] [<0000000100000001>] 0x100000001 > RSP: 0018:ffff8800ba9dba40 EFLAGS: 00010a16 > RAX: ffff8800bab48a00 RBX: ffff8800ba9dba90 RCX: ffff8800ba9dba90 > RDX: ffff8800b9c10128 RSI: ffff8800ba940900 RDI: ffff8800bab48a00 > RBP: ffff8800b9c10128 R08: ffffffff82976660 R09: ffff8800ba9dbb28 > R10: dead000000100100 R11: dead000000200200 R12: ffff8800ba940900 > R13: ffffffff8313fd50 R14: ffff8800b9c95200 R15: 0000000000000000 > FS: 00007fb91fc34700(0000) GS:ffff8800bfa00000(0000) knlGS:0000000000000000 > CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 > CR2: 0000000100000001 CR3: 00000000babfb000 CR4: 00000000000007f0 > Stack: > ffffffff8206ab0f ffffffff82982240 ffff8800bab48a00 ffff8800b9c100a8 > ffff8800b9c10100 0000000000000001 ffff8800ba940900 ffff8800b9c10128 > ffffffff8206bd65 ffff8800bfb0d5e0 ffff8800bab48a00 0000000000014dc0 > Call Trace: > [<ffffffff8206ab0f>] ? nf_iterate+0x4f/0xa0 > [<ffffffff8206bd65>] ? nf_reinject+0x125/0x190 > [<ffffffff8206dee5>] ? nfqnl_recv_verdict+0x255/0x360 > [<ffffffff81386290>] ? nla_parse+0x80/0xf0 > [<ffffffff8206c42c>] ? nfnetlink_rcv_msg+0x13c/0x240 > [<ffffffff811b2fec>] ? __memcg_kmem_get_cache+0x4c/0x150 > [<ffffffff8206c2f0>] ? nfnl_lock+0x20/0x20 > [<ffffffff82068159>] ? netlink_rcv_skb+0xa9/0xc0 > [<ffffffff820677bf>] ? netlink_unicast+0x12f/0x1c0 > [<ffffffff82067ade>] ? netlink_sendmsg+0x28e/0x650 > [<ffffffff81fdd814>] ? sock_sendmsg+0x44/0x50 > [<ffffffff81fde07b>] ? ___sys_sendmsg+0x2ab/0x2c0 > [<ffffffff810e8f73>] ? __wake_up+0x43/0x70 > [<ffffffff8141a134>] ? tty_write+0x1c4/0x2a0 > [<ffffffff81fde9f4>] ? __sys_sendmsg+0x44/0x80 > [<ffffffff823ff8d7>] ? system_call_fastpath+0x12/0x6a > Code: Bad RIP value. > RIP [<0000000100000001>] 0x100000001 > RSP <ffff8800ba9dba40> > CR2: 0000000100000001 > ---[ end trace 08eb65d42362793f ]--- Cc: stable@vger.kernel.org Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com> Signed-off-by: David S. Miller <davem@davemloft.net>
97 lines
2.6 KiB
C
97 lines
2.6 KiB
C
#ifndef _NF_QUEUE_H
|
|
#define _NF_QUEUE_H
|
|
|
|
#include <linux/ip.h>
|
|
#include <linux/ipv6.h>
|
|
#include <linux/jhash.h>
|
|
|
|
/* Each queued (to userspace) skbuff has one of these. */
|
|
struct nf_queue_entry {
|
|
struct list_head list;
|
|
struct sk_buff *skb;
|
|
unsigned int id;
|
|
|
|
struct nf_hook_ops *elem;
|
|
struct nf_hook_state state;
|
|
u16 size; /* sizeof(entry) + saved route keys */
|
|
|
|
/* extra space to store route keys */
|
|
};
|
|
|
|
#define nf_queue_entry_reroute(x) ((void *)x + sizeof(struct nf_queue_entry))
|
|
|
|
/* Packet queuing */
|
|
struct nf_queue_handler {
|
|
int (*outfn)(struct nf_queue_entry *entry,
|
|
unsigned int queuenum);
|
|
void (*nf_hook_drop)(struct net *net,
|
|
struct nf_hook_ops *ops);
|
|
};
|
|
|
|
void nf_register_queue_handler(const struct nf_queue_handler *qh);
|
|
void nf_unregister_queue_handler(void);
|
|
void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict);
|
|
|
|
bool nf_queue_entry_get_refs(struct nf_queue_entry *entry);
|
|
void nf_queue_entry_release_refs(struct nf_queue_entry *entry);
|
|
|
|
static inline void init_hashrandom(u32 *jhash_initval)
|
|
{
|
|
while (*jhash_initval == 0)
|
|
*jhash_initval = prandom_u32();
|
|
}
|
|
|
|
static inline u32 hash_v4(const struct sk_buff *skb, u32 jhash_initval)
|
|
{
|
|
const struct iphdr *iph = ip_hdr(skb);
|
|
|
|
/* packets in either direction go into same queue */
|
|
if ((__force u32)iph->saddr < (__force u32)iph->daddr)
|
|
return jhash_3words((__force u32)iph->saddr,
|
|
(__force u32)iph->daddr, iph->protocol, jhash_initval);
|
|
|
|
return jhash_3words((__force u32)iph->daddr,
|
|
(__force u32)iph->saddr, iph->protocol, jhash_initval);
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
|
|
static inline u32 hash_v6(const struct sk_buff *skb, u32 jhash_initval)
|
|
{
|
|
const struct ipv6hdr *ip6h = ipv6_hdr(skb);
|
|
u32 a, b, c;
|
|
|
|
if ((__force u32)ip6h->saddr.s6_addr32[3] <
|
|
(__force u32)ip6h->daddr.s6_addr32[3]) {
|
|
a = (__force u32) ip6h->saddr.s6_addr32[3];
|
|
b = (__force u32) ip6h->daddr.s6_addr32[3];
|
|
} else {
|
|
b = (__force u32) ip6h->saddr.s6_addr32[3];
|
|
a = (__force u32) ip6h->daddr.s6_addr32[3];
|
|
}
|
|
|
|
if ((__force u32)ip6h->saddr.s6_addr32[1] <
|
|
(__force u32)ip6h->daddr.s6_addr32[1])
|
|
c = (__force u32) ip6h->saddr.s6_addr32[1];
|
|
else
|
|
c = (__force u32) ip6h->daddr.s6_addr32[1];
|
|
|
|
return jhash_3words(a, b, c, jhash_initval);
|
|
}
|
|
#endif
|
|
|
|
static inline u32
|
|
nfqueue_hash(const struct sk_buff *skb, u16 queue, u16 queues_total, u8 family,
|
|
u32 jhash_initval)
|
|
{
|
|
if (family == NFPROTO_IPV4)
|
|
queue += ((u64) hash_v4(skb, jhash_initval) * queues_total) >> 32;
|
|
#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
|
|
else if (family == NFPROTO_IPV6)
|
|
queue += ((u64) hash_v6(skb, jhash_initval) * queues_total) >> 32;
|
|
#endif
|
|
|
|
return queue;
|
|
}
|
|
|
|
#endif /* _NF_QUEUE_H */
|