All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] netfilter: ebtables: fix a NULL pointer dereference in ebt_do_table()
@ 2022-08-20  7:03 ` Harshit Mogalapalli
  0 siblings, 0 replies; 17+ messages in thread
From: Harshit Mogalapalli @ 2022-08-20  7:03 UTC (permalink / raw)
  To: syzkaller
  Cc: harshit.m.mogalapalli, george.kennedy, vegard.nossum,
	john.p.donnelly, Pablo Neira Ayuso, Jozsef Kadlecsik,
	Florian Westphal, Roopa Prabhu, Nikolay Aleksandrov,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	netfilter-devel, coreteam, bridge, netdev, linux-kernel

In ebt_do_table() function dereferencing 'private->hook_entry[hook]'
can lead to NULL pointer dereference. So add a check to prevent that.

Kernel panic:

[  119.229105][   T31] general protection fault, probably for
non-canonical address 0xdffffc0000000005: 0000 [#1] PREEMPT SMP KASAN
[  119.230280][   T31] KASAN: null-ptr-deref in range
[0x0000000000000028-0x000000000000002f]
[  119.231043][   T31] CPU: 3 PID: 31 Comm: kworker/3:0 Not tainted
6.0.0-rc1 #1
[  119.231652][   T31] Hardware name: QEMU Standard PC (i440FX + PIIX,
1996), BIOS 1.11.0-2.el7 04/01/2014
[  119.232440][   T31] Workqueue: mld mld_ifc_work
[  119.232846][   T31] RIP: 0010:ebt_do_table+0x1dc/0x1ce0
[  119.233291][   T31] Code: 89 fa 48 c1 ea 03 80 3c 02 00 0f 85 5c 16
00 00 48 b8 00 00 00 00 00 fc ff df 49 8b 6c df 08 48 8d 7d 2c 48 89 fa
48 c1 ea 03 <0f> b6 14 02 48 89 f8 83 e0 07 83 c0 03 38 d0 7c 08 84 d2
0f 85 88
[  119.234920][   T31] RSP: 0018:ffffc90000347178 EFLAGS: 00010217
[  119.235425][   T31] RAX: dffffc0000000000 RBX: 0000000000000003 RCX:
ffffffff8158677b
[  119.236097][   T31] RDX: 0000000000000005 RSI: ffffffff889a7b80 RDI:
000000000000002c
[  119.236764][   T31] RBP: 0000000000000000 R08: 0000000000000001 R09:
ffff888101a78443
[  119.237425][   T31] R10: ffffed102034f088 R11: 000200100040dd86 R12:
ffffc90001111018
[  119.238100][   T31] R13: ffffc90001103080 R14: ffffc90001111000 R15:
ffffc90001103000
[  119.238769][   T31] FS:  0000000000000000(0000)
GS:ffff88811a380000(0000) knlGS:0000000000000000
[  119.239592][   T31] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  119.240221][   T31] CR2: 00007f6a9adda000 CR3: 0000000100be3000 CR4:
00000000000006e0
[  119.240970][   T31] Call Trace:
[  119.241253][   T31]  <TASK>
[  119.241495][   T31]  ? ip6_output+0x1f4/0x6e0
[  119.241877][   T31]  ? NF_HOOK.constprop.0+0xe6/0x5f0
[  119.242309][   T31]  ? mld_ifc_work+0x564/0xaa0
[  119.242708][   T31]  ? kthread+0x297/0x340
[  119.243060][   T31]  ? ret_from_fork+0x22/0x30
[  119.243454][   T31]  ? br_multicast_count+0xbf/0x8d0
[  119.243896][   T31]  ? rcu_read_lock_sched_held+0xd/0xa0
[  119.244356][   T31]  ? compat_copy_ebt_replace_from_user+0x380/0x380
[  119.244907][   T31]  ? compat_copy_ebt_replace_from_user+0x380/0x380
[  119.245454][   T31]  nf_hook_slow+0xb1/0x170
[  119.245835][   T31]  __br_forward+0x289/0x730
[  119.246219][   T31]  ? br_forward_finish+0x320/0x320
[  119.246656][   T31]  ? br_dev_queue_push_xmit+0x650/0x650
[  119.247118][   T31]  ? memcpy+0x39/0x60
[  119.247444][   T31]  ? __skb_clone+0x56c/0x750
[  119.247845][   T31]  maybe_deliver+0x24b/0x380
[  119.248234][   T31]  br_flood+0xc6/0x390
[  119.248577][   T31]  br_dev_xmit+0xa2e/0x12c0
[  119.248975][   T31]  ? rcu_read_lock_sched_held+0xd/0xa0
[  119.249498][   T31]  ? br_netpoll_setup+0x170/0x170
[  119.249987][   T31]  ? fib_rules_lookup+0x2eb/0x9d0
[  119.250462][   T31]  ? lock_repin_lock+0x30/0x420
[  119.250922][   T31]  ? rcu_read_lock_sched_held+0xd/0xa0
[  119.251376][   T31]  ? lock_acquire+0x510/0x630
[  119.251773][   T31]  ? netif_inherit_tso_max+0x1e0/0x1e0
[  119.252228][   T31]  dev_hard_start_xmit+0x151/0x660
[  119.252663][   T31]  __dev_queue_xmit+0x240e/0x3200
[  119.253080][   T31]  ? ip6mr_rtm_dumproute+0x530/0x530
[  119.253522][   T31]  ? netdev_core_pick_tx+0x2a0/0x2a0
[  119.253968][   T31]  ? unwind_next_frame+0x3de/0x1c90
[  119.254403][   T31]  ? rcu_read_lock_sched_held+0xd/0xa0
[  119.254872][   T31]  ? lock_acquire+0x510/0x630
[  119.255266][   T31]  ? rcu_read_lock_sched_held+0xd/0xa0
[  119.255724][   T31]  ? lock_release+0x5a2/0x840
[  119.256119][   T31]  ? mroute6_is_socket+0x14d/0x220
[  119.256549][   T31]  ? rcu_read_lock_sched_held+0xd/0xa0
[  119.257064][   T31]  ? lock_acquire+0x510/0x630
[  119.257507][   T31]  ? rcu_read_lock_sched_held+0xd/0xa0
[  119.258016][   T31]  ? lock_release+0x5a2/0x840
[  119.258410][   T31]  ? lock_release+0x840/0x840
[  119.258812][   T31]  ? ip6_finish_output+0x779/0x1190
[  119.259304][   T31]  ? lock_downgrade+0x7b0/0x7b0
[  119.259778][   T31]  ? rcu_read_lock_bh_held+0xd/0x90
[  119.260275][   T31]  ? ___neigh_lookup_noref.constprop.0+0x266/0x6d0
[  119.260896][   T31]  ? ip6_finish_output2+0x861/0x1690
[  119.261343][   T31]  ip6_finish_output2+0x861/0x1690
[  119.261776][   T31]  ? lock_release+0x5a2/0x840
[  119.262176][   T31]  ? __kasan_kmalloc+0x7f/0xa0
[  119.262574][   T31]  ? ip6_mtu+0x139/0x320
[  119.262942][   T31]  ? ip6_frag_next+0xcc0/0xcc0
[  119.263341][   T31]  ? rcu_read_lock_sched_held+0xd/0xa0
[  119.263816][   T31]  ? nf_ct_netns_get+0xe0/0xe0
[  119.264214][   T31]  ? rcu_read_lock_sched_held+0xd/0xa0
[  119.264679][   T31]  ? lock_release+0x5a2/0x840
[  119.265066][   T31]  ? nf_conntrack_in.cold+0x9b/0xe2
[  119.265504][   T31]  ? ip6_output+0x4c9/0x6e0
[  119.265891][   T31]  ip6_finish_output+0x779/0x1190
[  119.266315][   T31]  ? nf_hook_slow+0xb1/0x170
[  119.266711][   T31]  ip6_output+0x1f4/0x6e0
[  119.267069][   T31]  ? rcu_read_lock_sched_held+0xd/0xa0
[  119.267520][   T31]  ? ip6_finish_output+0x1190/0x1190
[  119.267969][   T31]  ? NF_HOOK.constprop.0+0x1b3/0x5f0
[  119.268415][   T31]  ? lock_pin_lock+0x184/0x380
[  119.268817][   T31]  ? ip6_fragment+0x2910/0x2910
[  119.269269][   T31]  ? nf_ct_netns_do_get+0x6c0/0x6c0
[  119.269773][   T31]  ? nf_hook_slow+0xb1/0x170
[  119.270211][   T31]  NF_HOOK.constprop.0+0xe6/0x5f0
[  119.270697][   T31]  ? igmp6_mcf_seq_next+0x460/0x460
[  119.271159][   T31]  ? igmp6_mcf_seq_stop+0x130/0x130
[  119.271600][   T31]  ? icmp6_dst_alloc+0x3dc/0x610
[  119.272030][   T31]  mld_sendpack+0x619/0xb50
[  119.272413][   T31]  ? NF_HOOK.constprop.0+0x5f0/0x5f0
[  119.272867][   T31]  ? lock_release+0x840/0x840
[  119.273254][   T31]  mld_ifc_work+0x564/0xaa0
[  119.273642][   T31]  ? pwq_activate_inactive_work+0xb2/0x190
[  119.274126][   T31]  process_one_work+0x875/0x1440
[  119.274543][   T31]  ? lock_release+0x840/0x840
[  119.274936][   T31]  ? pwq_dec_nr_in_flight+0x230/0x230
[  119.275381][   T31]  ? rwlock_bug.part.0+0x90/0x90
[  119.275801][   T31]  worker_thread+0x598/0xec0
[  119.276187][   T31]  ? process_one_work+0x1440/0x1440
[  119.276613][   T31]  kthread+0x297/0x340
[  119.276958][   T31]  ? kthread_complete_and_exit+0x20/0x20
[  119.277422][   T31]  ret_from_fork+0x22/0x30
[  119.277796][   T31]  </TASK>
[  119.278048][   T31] Modules linked in:
[  119.278377][   T31] Dumping ftrace buffer:
[  119.278733][   T31]    (ftrace buffer empty)
[  119.279151][   T31] ---[ end trace 0000000000000000 ]---
[  119.279679][   T31] RIP: 0010:ebt_do_table+0x1dc/0x1ce0
[  119.280187][   T31] Code: 89 fa 48 c1 ea 03 80 3c 02 00 0f 85 5c 16
00 00 48 b8 00 00 00 00 00 fc ff df 49 8b 6c df 08 48 8d 7d 2c 48 89 fa
48 c1 ea 03 <0f> b6 14 02 48 89 f8 83 e0 07 83 c0 03 38 d0 7c 08 84 d2
0f 85 88
[  119.282006][   T31] RSP: 0018:ffffc90000347178 EFLAGS: 00010217
[  119.282569][   T31] RAX: dffffc0000000000 RBX: 0000000000000003 RCX:
ffffffff8158677b
[  119.283308][   T31] RDX: 0000000000000005 RSI: ffffffff889a7b80 RDI:
000000000000002c
[  119.284056][   T31] RBP: 0000000000000000 R08: 0000000000000001 R09:
ffff888101a78443
[  119.284811][   T31] R10: ffffed102034f088 R11: 000200100040dd86 R12:
ffffc90001111018
[  119.285549][   T31] R13: ffffc90001103080 R14: ffffc90001111000 R15:
ffffc90001103000
[  119.286284][   T31] FS:  0000000000000000(0000)
GS:ffff88811a380000(0000) knlGS:0000000000000000
[  119.287119][   T31] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  119.287741][   T31] CR2: 00007f6a9adda000 CR3: 0000000100be3000 CR4:
00000000000006e0
[  119.288489][   T31] Kernel panic - not syncing: Fatal exception in
interrupt
[  119.298556][   T31] Dumping ftrace buffer:
[  119.298974][   T31]    (ftrace buffer empty)
[  119.299399][   T31] Kernel Offset: disabled
[  119.299823][   T31] Rebooting in 86400 seconds..

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Reported-by: syzkaller <syzkaller@googlegroups.com>
Signed-off-by: Harshit Mogalapalli <harshit.m.mogalapalli@oracle.com>
---
Testing is only done with reproducer.

 net/bridge/netfilter/ebtables.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
index f2dbefb61ce8..d19d439a66c5 100644
--- a/net/bridge/netfilter/ebtables.c
+++ b/net/bridge/netfilter/ebtables.c
@@ -217,6 +217,11 @@ unsigned int ebt_do_table(void *priv, struct sk_buff *skb,
 	else
 		cs = NULL;
 	chaininfo = private->hook_entry[hook];
+	if (!chaininfo) {
+		read_unlock_bh(&table->lock);
+		return NF_DROP;
+	}
+
 	nentries = private->hook_entry[hook]->nentries;
 	point = (struct ebt_entry *)(private->hook_entry[hook]->data);
 	counter_base = cb_base + private->hook_entry[hook]->counter_offset;
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Bridge] [PATCH] netfilter: ebtables: fix a NULL pointer dereference in ebt_do_table()
@ 2022-08-20  7:03 ` Harshit Mogalapalli
  0 siblings, 0 replies; 17+ messages in thread
From: Harshit Mogalapalli @ 2022-08-20  7:03 UTC (permalink / raw)
  To: syzkaller
  Cc: john.p.donnelly, vegard.nossum, netdev, Nikolay Aleksandrov,
	bridge, Florian Westphal, linux-kernel, Jozsef Kadlecsik,
	george.kennedy, Eric Dumazet, coreteam, netfilter-devel,
	harshit.m.mogalapalli, Roopa Prabhu, Jakub Kicinski, Paolo Abeni,
	David S. Miller, Pablo Neira Ayuso

In ebt_do_table() function dereferencing 'private->hook_entry[hook]'
can lead to NULL pointer dereference. So add a check to prevent that.

Kernel panic:

[  119.229105][   T31] general protection fault, probably for
non-canonical address 0xdffffc0000000005: 0000 [#1] PREEMPT SMP KASAN
[  119.230280][   T31] KASAN: null-ptr-deref in range
[0x0000000000000028-0x000000000000002f]
[  119.231043][   T31] CPU: 3 PID: 31 Comm: kworker/3:0 Not tainted
6.0.0-rc1 #1
[  119.231652][   T31] Hardware name: QEMU Standard PC (i440FX + PIIX,
1996), BIOS 1.11.0-2.el7 04/01/2014
[  119.232440][   T31] Workqueue: mld mld_ifc_work
[  119.232846][   T31] RIP: 0010:ebt_do_table+0x1dc/0x1ce0
[  119.233291][   T31] Code: 89 fa 48 c1 ea 03 80 3c 02 00 0f 85 5c 16
00 00 48 b8 00 00 00 00 00 fc ff df 49 8b 6c df 08 48 8d 7d 2c 48 89 fa
48 c1 ea 03 <0f> b6 14 02 48 89 f8 83 e0 07 83 c0 03 38 d0 7c 08 84 d2
0f 85 88
[  119.234920][   T31] RSP: 0018:ffffc90000347178 EFLAGS: 00010217
[  119.235425][   T31] RAX: dffffc0000000000 RBX: 0000000000000003 RCX:
ffffffff8158677b
[  119.236097][   T31] RDX: 0000000000000005 RSI: ffffffff889a7b80 RDI:
000000000000002c
[  119.236764][   T31] RBP: 0000000000000000 R08: 0000000000000001 R09:
ffff888101a78443
[  119.237425][   T31] R10: ffffed102034f088 R11: 000200100040dd86 R12:
ffffc90001111018
[  119.238100][   T31] R13: ffffc90001103080 R14: ffffc90001111000 R15:
ffffc90001103000
[  119.238769][   T31] FS:  0000000000000000(0000)
GS:ffff88811a380000(0000) knlGS:0000000000000000
[  119.239592][   T31] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  119.240221][   T31] CR2: 00007f6a9adda000 CR3: 0000000100be3000 CR4:
00000000000006e0
[  119.240970][   T31] Call Trace:
[  119.241253][   T31]  <TASK>
[  119.241495][   T31]  ? ip6_output+0x1f4/0x6e0
[  119.241877][   T31]  ? NF_HOOK.constprop.0+0xe6/0x5f0
[  119.242309][   T31]  ? mld_ifc_work+0x564/0xaa0
[  119.242708][   T31]  ? kthread+0x297/0x340
[  119.243060][   T31]  ? ret_from_fork+0x22/0x30
[  119.243454][   T31]  ? br_multicast_count+0xbf/0x8d0
[  119.243896][   T31]  ? rcu_read_lock_sched_held+0xd/0xa0
[  119.244356][   T31]  ? compat_copy_ebt_replace_from_user+0x380/0x380
[  119.244907][   T31]  ? compat_copy_ebt_replace_from_user+0x380/0x380
[  119.245454][   T31]  nf_hook_slow+0xb1/0x170
[  119.245835][   T31]  __br_forward+0x289/0x730
[  119.246219][   T31]  ? br_forward_finish+0x320/0x320
[  119.246656][   T31]  ? br_dev_queue_push_xmit+0x650/0x650
[  119.247118][   T31]  ? memcpy+0x39/0x60
[  119.247444][   T31]  ? __skb_clone+0x56c/0x750
[  119.247845][   T31]  maybe_deliver+0x24b/0x380
[  119.248234][   T31]  br_flood+0xc6/0x390
[  119.248577][   T31]  br_dev_xmit+0xa2e/0x12c0
[  119.248975][   T31]  ? rcu_read_lock_sched_held+0xd/0xa0
[  119.249498][   T31]  ? br_netpoll_setup+0x170/0x170
[  119.249987][   T31]  ? fib_rules_lookup+0x2eb/0x9d0
[  119.250462][   T31]  ? lock_repin_lock+0x30/0x420
[  119.250922][   T31]  ? rcu_read_lock_sched_held+0xd/0xa0
[  119.251376][   T31]  ? lock_acquire+0x510/0x630
[  119.251773][   T31]  ? netif_inherit_tso_max+0x1e0/0x1e0
[  119.252228][   T31]  dev_hard_start_xmit+0x151/0x660
[  119.252663][   T31]  __dev_queue_xmit+0x240e/0x3200
[  119.253080][   T31]  ? ip6mr_rtm_dumproute+0x530/0x530
[  119.253522][   T31]  ? netdev_core_pick_tx+0x2a0/0x2a0
[  119.253968][   T31]  ? unwind_next_frame+0x3de/0x1c90
[  119.254403][   T31]  ? rcu_read_lock_sched_held+0xd/0xa0
[  119.254872][   T31]  ? lock_acquire+0x510/0x630
[  119.255266][   T31]  ? rcu_read_lock_sched_held+0xd/0xa0
[  119.255724][   T31]  ? lock_release+0x5a2/0x840
[  119.256119][   T31]  ? mroute6_is_socket+0x14d/0x220
[  119.256549][   T31]  ? rcu_read_lock_sched_held+0xd/0xa0
[  119.257064][   T31]  ? lock_acquire+0x510/0x630
[  119.257507][   T31]  ? rcu_read_lock_sched_held+0xd/0xa0
[  119.258016][   T31]  ? lock_release+0x5a2/0x840
[  119.258410][   T31]  ? lock_release+0x840/0x840
[  119.258812][   T31]  ? ip6_finish_output+0x779/0x1190
[  119.259304][   T31]  ? lock_downgrade+0x7b0/0x7b0
[  119.259778][   T31]  ? rcu_read_lock_bh_held+0xd/0x90
[  119.260275][   T31]  ? ___neigh_lookup_noref.constprop.0+0x266/0x6d0
[  119.260896][   T31]  ? ip6_finish_output2+0x861/0x1690
[  119.261343][   T31]  ip6_finish_output2+0x861/0x1690
[  119.261776][   T31]  ? lock_release+0x5a2/0x840
[  119.262176][   T31]  ? __kasan_kmalloc+0x7f/0xa0
[  119.262574][   T31]  ? ip6_mtu+0x139/0x320
[  119.262942][   T31]  ? ip6_frag_next+0xcc0/0xcc0
[  119.263341][   T31]  ? rcu_read_lock_sched_held+0xd/0xa0
[  119.263816][   T31]  ? nf_ct_netns_get+0xe0/0xe0
[  119.264214][   T31]  ? rcu_read_lock_sched_held+0xd/0xa0
[  119.264679][   T31]  ? lock_release+0x5a2/0x840
[  119.265066][   T31]  ? nf_conntrack_in.cold+0x9b/0xe2
[  119.265504][   T31]  ? ip6_output+0x4c9/0x6e0
[  119.265891][   T31]  ip6_finish_output+0x779/0x1190
[  119.266315][   T31]  ? nf_hook_slow+0xb1/0x170
[  119.266711][   T31]  ip6_output+0x1f4/0x6e0
[  119.267069][   T31]  ? rcu_read_lock_sched_held+0xd/0xa0
[  119.267520][   T31]  ? ip6_finish_output+0x1190/0x1190
[  119.267969][   T31]  ? NF_HOOK.constprop.0+0x1b3/0x5f0
[  119.268415][   T31]  ? lock_pin_lock+0x184/0x380
[  119.268817][   T31]  ? ip6_fragment+0x2910/0x2910
[  119.269269][   T31]  ? nf_ct_netns_do_get+0x6c0/0x6c0
[  119.269773][   T31]  ? nf_hook_slow+0xb1/0x170
[  119.270211][   T31]  NF_HOOK.constprop.0+0xe6/0x5f0
[  119.270697][   T31]  ? igmp6_mcf_seq_next+0x460/0x460
[  119.271159][   T31]  ? igmp6_mcf_seq_stop+0x130/0x130
[  119.271600][   T31]  ? icmp6_dst_alloc+0x3dc/0x610
[  119.272030][   T31]  mld_sendpack+0x619/0xb50
[  119.272413][   T31]  ? NF_HOOK.constprop.0+0x5f0/0x5f0
[  119.272867][   T31]  ? lock_release+0x840/0x840
[  119.273254][   T31]  mld_ifc_work+0x564/0xaa0
[  119.273642][   T31]  ? pwq_activate_inactive_work+0xb2/0x190
[  119.274126][   T31]  process_one_work+0x875/0x1440
[  119.274543][   T31]  ? lock_release+0x840/0x840
[  119.274936][   T31]  ? pwq_dec_nr_in_flight+0x230/0x230
[  119.275381][   T31]  ? rwlock_bug.part.0+0x90/0x90
[  119.275801][   T31]  worker_thread+0x598/0xec0
[  119.276187][   T31]  ? process_one_work+0x1440/0x1440
[  119.276613][   T31]  kthread+0x297/0x340
[  119.276958][   T31]  ? kthread_complete_and_exit+0x20/0x20
[  119.277422][   T31]  ret_from_fork+0x22/0x30
[  119.277796][   T31]  </TASK>
[  119.278048][   T31] Modules linked in:
[  119.278377][   T31] Dumping ftrace buffer:
[  119.278733][   T31]    (ftrace buffer empty)
[  119.279151][   T31] ---[ end trace 0000000000000000 ]---
[  119.279679][   T31] RIP: 0010:ebt_do_table+0x1dc/0x1ce0
[  119.280187][   T31] Code: 89 fa 48 c1 ea 03 80 3c 02 00 0f 85 5c 16
00 00 48 b8 00 00 00 00 00 fc ff df 49 8b 6c df 08 48 8d 7d 2c 48 89 fa
48 c1 ea 03 <0f> b6 14 02 48 89 f8 83 e0 07 83 c0 03 38 d0 7c 08 84 d2
0f 85 88
[  119.282006][   T31] RSP: 0018:ffffc90000347178 EFLAGS: 00010217
[  119.282569][   T31] RAX: dffffc0000000000 RBX: 0000000000000003 RCX:
ffffffff8158677b
[  119.283308][   T31] RDX: 0000000000000005 RSI: ffffffff889a7b80 RDI:
000000000000002c
[  119.284056][   T31] RBP: 0000000000000000 R08: 0000000000000001 R09:
ffff888101a78443
[  119.284811][   T31] R10: ffffed102034f088 R11: 000200100040dd86 R12:
ffffc90001111018
[  119.285549][   T31] R13: ffffc90001103080 R14: ffffc90001111000 R15:
ffffc90001103000
[  119.286284][   T31] FS:  0000000000000000(0000)
GS:ffff88811a380000(0000) knlGS:0000000000000000
[  119.287119][   T31] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  119.287741][   T31] CR2: 00007f6a9adda000 CR3: 0000000100be3000 CR4:
00000000000006e0
[  119.288489][   T31] Kernel panic - not syncing: Fatal exception in
interrupt
[  119.298556][   T31] Dumping ftrace buffer:
[  119.298974][   T31]    (ftrace buffer empty)
[  119.299399][   T31] Kernel Offset: disabled
[  119.299823][   T31] Rebooting in 86400 seconds..

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Reported-by: syzkaller <syzkaller@googlegroups.com>
Signed-off-by: Harshit Mogalapalli <harshit.m.mogalapalli@oracle.com>
---
Testing is only done with reproducer.

 net/bridge/netfilter/ebtables.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
index f2dbefb61ce8..d19d439a66c5 100644
--- a/net/bridge/netfilter/ebtables.c
+++ b/net/bridge/netfilter/ebtables.c
@@ -217,6 +217,11 @@ unsigned int ebt_do_table(void *priv, struct sk_buff *skb,
 	else
 		cs = NULL;
 	chaininfo = private->hook_entry[hook];
+	if (!chaininfo) {
+		read_unlock_bh(&table->lock);
+		return NF_DROP;
+	}
+
 	nentries = private->hook_entry[hook]->nentries;
 	point = (struct ebt_entry *)(private->hook_entry[hook]->data);
 	counter_base = cb_base + private->hook_entry[hook]->counter_offset;
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH] netfilter: ebtables: fix a NULL pointer dereference in ebt_do_table()
  2022-08-20  7:03 ` [Bridge] " Harshit Mogalapalli
@ 2022-08-20 16:26   ` Florian Westphal
  -1 siblings, 0 replies; 17+ messages in thread
From: Florian Westphal @ 2022-08-20 16:26 UTC (permalink / raw)
  To: Harshit Mogalapalli
  Cc: syzkaller, george.kennedy, vegard.nossum, john.p.donnelly,
	Pablo Neira Ayuso, Jozsef Kadlecsik, Roopa Prabhu,
	Nikolay Aleksandrov, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, netfilter-devel, coreteam, bridge,
	netdev, linux-kernel

Harshit Mogalapalli <harshit.m.mogalapalli@oracle.com> wrote:
> In ebt_do_table() function dereferencing 'private->hook_entry[hook]'
> can lead to NULL pointer dereference. So add a check to prevent that.

This looks incorrect, i.e. paperimg over the problem.

If hook_entry[hook] is NULL, how did this make it to the eval loop?

I guess ebtables lacks a sanity check on incoming ruleset?

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [Bridge] [PATCH] netfilter: ebtables: fix a NULL pointer dereference in ebt_do_table()
@ 2022-08-20 16:26   ` Florian Westphal
  0 siblings, 0 replies; 17+ messages in thread
From: Florian Westphal @ 2022-08-20 16:26 UTC (permalink / raw)
  To: Harshit Mogalapalli
  Cc: john.p.donnelly, vegard.nossum, coreteam, netdev,
	Nikolay Aleksandrov, bridge, linux-kernel, Jozsef Kadlecsik,
	george.kennedy, Eric Dumazet, syzkaller, netfilter-devel,
	Roopa Prabhu, Jakub Kicinski, Paolo Abeni, David S. Miller,
	Pablo Neira Ayuso

Harshit Mogalapalli <harshit.m.mogalapalli@oracle.com> wrote:
> In ebt_do_table() function dereferencing 'private->hook_entry[hook]'
> can lead to NULL pointer dereference. So add a check to prevent that.

This looks incorrect, i.e. paperimg over the problem.

If hook_entry[hook] is NULL, how did this make it to the eval loop?

I guess ebtables lacks a sanity check on incoming ruleset?

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH nf] netfilter: ebtables: reject blobs that don't provide all entry points
  2022-08-20  7:03 ` [Bridge] " Harshit Mogalapalli
@ 2022-08-20 17:35   ` Florian Westphal
  -1 siblings, 0 replies; 17+ messages in thread
From: Florian Westphal @ 2022-08-20 17:35 UTC (permalink / raw)
  To: netfilter-devel
  Cc: syzkaller, george.kennedy, vegard.nossum, john.p.donnelly,
	bridge, netdev, linux-kernel, Florian Westphal,
	Harshit Mogalapalli

For some reason ebtables reject blobs that provide entry points that are
not supported by the table.

What it should instead reject is the opposite, i.e. rulesets that
DO NOT provide an entry point that is supported by the table.

t->valid_hooks is the bitmask of hooks (input, forward ...) that will
see packets.  So, providing an entry point that is not support is
harmless (never called/used), but the reverse is NOT, this will cause
crash because the ebtables traverser doesn't expect a NULL blob for
a location its receiving packets for.

Instead of fixing all the individual checks, do what iptables is doing and
reject all blobs that doesn't provide the expected hooks.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Reported-by: Harshit Mogalapalli <harshit.m.mogalapalli@oracle.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
---
 Harshit, can you check if this also silences your reproducer?

 Thanks!

 include/linux/netfilter_bridge/ebtables.h | 4 ----
 net/bridge/netfilter/ebtable_broute.c     | 8 --------
 net/bridge/netfilter/ebtable_filter.c     | 8 --------
 net/bridge/netfilter/ebtable_nat.c        | 8 --------
 net/bridge/netfilter/ebtables.c           | 8 +-------
 5 files changed, 1 insertion(+), 35 deletions(-)

diff --git a/include/linux/netfilter_bridge/ebtables.h b/include/linux/netfilter_bridge/ebtables.h
index a13296d6c7ce..fd533552a062 100644
--- a/include/linux/netfilter_bridge/ebtables.h
+++ b/include/linux/netfilter_bridge/ebtables.h
@@ -94,10 +94,6 @@ struct ebt_table {
 	struct ebt_replace_kernel *table;
 	unsigned int valid_hooks;
 	rwlock_t lock;
-	/* e.g. could be the table explicitly only allows certain
-	 * matches, targets, ... 0 == let it in */
-	int (*check)(const struct ebt_table_info *info,
-	   unsigned int valid_hooks);
 	/* the data used by the kernel */
 	struct ebt_table_info *private;
 	struct nf_hook_ops *ops;
diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c
index 1a11064f9990..8f19253024b0 100644
--- a/net/bridge/netfilter/ebtable_broute.c
+++ b/net/bridge/netfilter/ebtable_broute.c
@@ -36,18 +36,10 @@ static struct ebt_replace_kernel initial_table = {
 	.entries	= (char *)&initial_chain,
 };
 
-static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
-{
-	if (valid_hooks & ~(1 << NF_BR_BROUTING))
-		return -EINVAL;
-	return 0;
-}
-
 static const struct ebt_table broute_table = {
 	.name		= "broute",
 	.table		= &initial_table,
 	.valid_hooks	= 1 << NF_BR_BROUTING,
-	.check		= check,
 	.me		= THIS_MODULE,
 };
 
diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c
index cb949436bc0e..278f324e6752 100644
--- a/net/bridge/netfilter/ebtable_filter.c
+++ b/net/bridge/netfilter/ebtable_filter.c
@@ -43,18 +43,10 @@ static struct ebt_replace_kernel initial_table = {
 	.entries	= (char *)initial_chains,
 };
 
-static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
-{
-	if (valid_hooks & ~FILTER_VALID_HOOKS)
-		return -EINVAL;
-	return 0;
-}
-
 static const struct ebt_table frame_filter = {
 	.name		= "filter",
 	.table		= &initial_table,
 	.valid_hooks	= FILTER_VALID_HOOKS,
-	.check		= check,
 	.me		= THIS_MODULE,
 };
 
diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c
index 5ee0531ae506..9066f7f376d5 100644
--- a/net/bridge/netfilter/ebtable_nat.c
+++ b/net/bridge/netfilter/ebtable_nat.c
@@ -43,18 +43,10 @@ static struct ebt_replace_kernel initial_table = {
 	.entries	= (char *)initial_chains,
 };
 
-static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
-{
-	if (valid_hooks & ~NAT_VALID_HOOKS)
-		return -EINVAL;
-	return 0;
-}
-
 static const struct ebt_table frame_nat = {
 	.name		= "nat",
 	.table		= &initial_table,
 	.valid_hooks	= NAT_VALID_HOOKS,
-	.check		= check,
 	.me		= THIS_MODULE,
 };
 
diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
index f2dbefb61ce8..9a0ae59cdc50 100644
--- a/net/bridge/netfilter/ebtables.c
+++ b/net/bridge/netfilter/ebtables.c
@@ -1040,8 +1040,7 @@ static int do_replace_finish(struct net *net, struct ebt_replace *repl,
 		goto free_iterate;
 	}
 
-	/* the table doesn't like it */
-	if (t->check && (ret = t->check(newinfo, repl->valid_hooks)))
+	if (repl->valid_hooks != t->valid_hooks)
 		goto free_unlock;
 
 	if (repl->num_counters && repl->num_counters != t->private->nentries) {
@@ -1231,11 +1230,6 @@ int ebt_register_table(struct net *net, const struct ebt_table *input_table,
 	if (ret != 0)
 		goto free_chainstack;
 
-	if (table->check && table->check(newinfo, table->valid_hooks)) {
-		ret = -EINVAL;
-		goto free_chainstack;
-	}
-
 	table->private = newinfo;
 	rwlock_init(&table->lock);
 	mutex_lock(&ebt_mutex);
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [Bridge] [PATCH nf] netfilter: ebtables: reject blobs that don't provide all entry points
@ 2022-08-20 17:35   ` Florian Westphal
  0 siblings, 0 replies; 17+ messages in thread
From: Florian Westphal @ 2022-08-20 17:35 UTC (permalink / raw)
  To: netfilter-devel
  Cc: john.p.donnelly, vegard.nossum, netdev, bridge, Florian Westphal,
	linux-kernel, george.kennedy, syzkaller, Harshit Mogalapalli

For some reason ebtables reject blobs that provide entry points that are
not supported by the table.

What it should instead reject is the opposite, i.e. rulesets that
DO NOT provide an entry point that is supported by the table.

t->valid_hooks is the bitmask of hooks (input, forward ...) that will
see packets.  So, providing an entry point that is not support is
harmless (never called/used), but the reverse is NOT, this will cause
crash because the ebtables traverser doesn't expect a NULL blob for
a location its receiving packets for.

Instead of fixing all the individual checks, do what iptables is doing and
reject all blobs that doesn't provide the expected hooks.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Reported-by: Harshit Mogalapalli <harshit.m.mogalapalli@oracle.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
---
 Harshit, can you check if this also silences your reproducer?

 Thanks!

 include/linux/netfilter_bridge/ebtables.h | 4 ----
 net/bridge/netfilter/ebtable_broute.c     | 8 --------
 net/bridge/netfilter/ebtable_filter.c     | 8 --------
 net/bridge/netfilter/ebtable_nat.c        | 8 --------
 net/bridge/netfilter/ebtables.c           | 8 +-------
 5 files changed, 1 insertion(+), 35 deletions(-)

diff --git a/include/linux/netfilter_bridge/ebtables.h b/include/linux/netfilter_bridge/ebtables.h
index a13296d6c7ce..fd533552a062 100644
--- a/include/linux/netfilter_bridge/ebtables.h
+++ b/include/linux/netfilter_bridge/ebtables.h
@@ -94,10 +94,6 @@ struct ebt_table {
 	struct ebt_replace_kernel *table;
 	unsigned int valid_hooks;
 	rwlock_t lock;
-	/* e.g. could be the table explicitly only allows certain
-	 * matches, targets, ... 0 == let it in */
-	int (*check)(const struct ebt_table_info *info,
-	   unsigned int valid_hooks);
 	/* the data used by the kernel */
 	struct ebt_table_info *private;
 	struct nf_hook_ops *ops;
diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c
index 1a11064f9990..8f19253024b0 100644
--- a/net/bridge/netfilter/ebtable_broute.c
+++ b/net/bridge/netfilter/ebtable_broute.c
@@ -36,18 +36,10 @@ static struct ebt_replace_kernel initial_table = {
 	.entries	= (char *)&initial_chain,
 };
 
-static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
-{
-	if (valid_hooks & ~(1 << NF_BR_BROUTING))
-		return -EINVAL;
-	return 0;
-}
-
 static const struct ebt_table broute_table = {
 	.name		= "broute",
 	.table		= &initial_table,
 	.valid_hooks	= 1 << NF_BR_BROUTING,
-	.check		= check,
 	.me		= THIS_MODULE,
 };
 
diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c
index cb949436bc0e..278f324e6752 100644
--- a/net/bridge/netfilter/ebtable_filter.c
+++ b/net/bridge/netfilter/ebtable_filter.c
@@ -43,18 +43,10 @@ static struct ebt_replace_kernel initial_table = {
 	.entries	= (char *)initial_chains,
 };
 
-static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
-{
-	if (valid_hooks & ~FILTER_VALID_HOOKS)
-		return -EINVAL;
-	return 0;
-}
-
 static const struct ebt_table frame_filter = {
 	.name		= "filter",
 	.table		= &initial_table,
 	.valid_hooks	= FILTER_VALID_HOOKS,
-	.check		= check,
 	.me		= THIS_MODULE,
 };
 
diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c
index 5ee0531ae506..9066f7f376d5 100644
--- a/net/bridge/netfilter/ebtable_nat.c
+++ b/net/bridge/netfilter/ebtable_nat.c
@@ -43,18 +43,10 @@ static struct ebt_replace_kernel initial_table = {
 	.entries	= (char *)initial_chains,
 };
 
-static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
-{
-	if (valid_hooks & ~NAT_VALID_HOOKS)
-		return -EINVAL;
-	return 0;
-}
-
 static const struct ebt_table frame_nat = {
 	.name		= "nat",
 	.table		= &initial_table,
 	.valid_hooks	= NAT_VALID_HOOKS,
-	.check		= check,
 	.me		= THIS_MODULE,
 };
 
diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
index f2dbefb61ce8..9a0ae59cdc50 100644
--- a/net/bridge/netfilter/ebtables.c
+++ b/net/bridge/netfilter/ebtables.c
@@ -1040,8 +1040,7 @@ static int do_replace_finish(struct net *net, struct ebt_replace *repl,
 		goto free_iterate;
 	}
 
-	/* the table doesn't like it */
-	if (t->check && (ret = t->check(newinfo, repl->valid_hooks)))
+	if (repl->valid_hooks != t->valid_hooks)
 		goto free_unlock;
 
 	if (repl->num_counters && repl->num_counters != t->private->nentries) {
@@ -1231,11 +1230,6 @@ int ebt_register_table(struct net *net, const struct ebt_table *input_table,
 	if (ret != 0)
 		goto free_chainstack;
 
-	if (table->check && table->check(newinfo, table->valid_hooks)) {
-		ret = -EINVAL;
-		goto free_chainstack;
-	}
-
 	table->private = newinfo;
 	rwlock_init(&table->lock);
 	mutex_lock(&ebt_mutex);
-- 
2.37.2


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [External] : Re: [PATCH] netfilter: ebtables: fix a NULL pointer dereference in ebt_do_table()
  2022-08-20 16:26   ` [Bridge] " Florian Westphal
@ 2022-08-20 19:18     ` Harshit Mogalapalli
  -1 siblings, 0 replies; 17+ messages in thread
From: Harshit Mogalapalli @ 2022-08-20 19:18 UTC (permalink / raw)
  To: Florian Westphal
  Cc: syzkaller, george.kennedy, vegard.nossum, john.p.donnelly,
	Pablo Neira Ayuso, Jozsef Kadlecsik, Roopa Prabhu,
	Nikolay Aleksandrov, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, netfilter-devel, coreteam, bridge,
	netdev, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 715 bytes --]

On 20/08/22 9:56 pm, Florian Westphal wrote:
> Harshit Mogalapalli <harshit.m.mogalapalli@oracle.com> wrote:
>> In ebt_do_table() function dereferencing 'private->hook_entry[hook]'
>> can lead to NULL pointer dereference. So add a check to prevent that.
> 
Hi Florian,

Thanks a lot for checking the patch.

> This looks incorrect, i.e. paperimg over the problem.
> 

Okay.

> If hook_entry[hook] is NULL, how did this make it to the eval loop?
>

When I run the reproducer and have 'private->hook_entry[hook]' printed, 
it was NULL, so thought of adding a NULL check to prevent the NULL 
dereference.

Attaching the C reproducer.

Regards,
Harshit


> I guess ebtables lacks a sanity check on incoming ruleset?
> 

[-- Attachment #2: r.c --]
[-- Type: text/plain, Size: 44610 bytes --]

{\rtf1\ansi\ansicpg1252\cocoartf2639
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fmodern\fcharset0 Courier;}
{\colortbl;\red255\green255\blue255;\red0\green0\blue0;}
{\*\expandedcolortbl;;\cssrgb\c0\c0\c0;}
\paperw11900\paperh16840\margl1440\margr1440\vieww11520\viewh8400\viewkind0
\deftab720
\pard\pardeftab720\partightenfactor0

\f0\fs26 \cf0 \expnd0\expndtw0\kerning0
\outl0\strokewidth0 \strokec2 #define _GNU_SOURCE \
\
#include <arpa/inet.h>\
#include <endian.h>\
#include <errno.h>\
#include <fcntl.h>\
#include <net/if.h>\
#include <net/if_arp.h>\
#include <netinet/in.h>\
#include <pthread.h>\
#include <sched.h>\
#include <stdarg.h>\
#include <stdbool.h>\
#include <stdint.h>\
#include <stdio.h>\
#include <stdlib.h>\
#include <string.h>\
#include <sys/epoll.h>\
#include <sys/ioctl.h>\
#include <sys/mount.h>\
#include <sys/prctl.h>\
#include <sys/resource.h>\
#include <sys/socket.h>\
#include <sys/stat.h>\
#include <sys/syscall.h>\
#include <sys/time.h>\
#include <sys/types.h>\
#include <sys/uio.h>\
#include <sys/wait.h>\
#include <unistd.h>\
\
#include <linux/capability.h>\
#include <linux/genetlink.h>\
#include <linux/if_addr.h>\
#include <linux/if_ether.h>\
#include <linux/if_link.h>\
#include <linux/if_tun.h>\
#include <linux/in6.h>\
#include <linux/ip.h>\
#include <linux/neighbour.h>\
#include <linux/net.h>\
#include <linux/netlink.h>\
#include <linux/rfkill.h>\
#include <linux/rtnetlink.h>\
#include <linux/tcp.h>\
#include <linux/veth.h>\
\
static unsigned long long procid;\
\
static bool write_file(const char* file, const char* what, ...)\
\{\
	char buf[1024];\
	va_list args;\
	va_start(args, what);\
	vsnprintf(buf, sizeof(buf), what, args);\
	va_end(args);\
	buf[sizeof(buf) - 1] = 0;\
	int len = strlen(buf);\
	int fd = open(file, O_WRONLY | O_CLOEXEC);\
	if (fd == -1)\
		return false;\
	if (write(fd, buf, len) != len) \{\
		int err = errno;\
		close(fd);\
		errno = err;\
		return false;\
	\}\
	close(fd);\
	return true;\
\}\
\
struct nlmsg \{\
	char* pos;\
	int nesting;\
	struct nlattr* nested[8];\
	char buf[4096];\
\};\
\
static void netlink_init(struct nlmsg* nlmsg, int typ, int flags,\
			 const void* data, int size)\
\{\
	memset(nlmsg, 0, sizeof(*nlmsg));\
	struct nlmsghdr* hdr = (struct nlmsghdr*)nlmsg->buf;\
	hdr->nlmsg_type = typ;\
	hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags;\
	memcpy(hdr + 1, data, size);\
	nlmsg->pos = (char*)(hdr + 1) + NLMSG_ALIGN(size);\
\}\
\
static void netlink_attr(struct nlmsg* nlmsg, int typ,\
			 const void* data, int size)\
\{\
	struct nlattr* attr = (struct nlattr*)nlmsg->pos;\
	attr->nla_len = sizeof(*attr) + size;\
	attr->nla_type = typ;\
	if (size > 0)\
		memcpy(attr + 1, data, size);\
	nlmsg->pos += NLMSG_ALIGN(attr->nla_len);\
\}\
\
static void netlink_nest(struct nlmsg* nlmsg, int typ)\
\{\
	struct nlattr* attr = (struct nlattr*)nlmsg->pos;\
	attr->nla_type = typ;\
	nlmsg->pos += sizeof(*attr);\
	nlmsg->nested[nlmsg->nesting++] = attr;\
\}\
\
static void netlink_done(struct nlmsg* nlmsg)\
\{\
	struct nlattr* attr = nlmsg->nested[--nlmsg->nesting];\
	attr->nla_len = nlmsg->pos - (char*)attr;\
\}\
\
static int netlink_send_ext(struct nlmsg* nlmsg, int sock,\
			    uint16_t reply_type, int* reply_len, bool dofail)\
\{\
	if (nlmsg->pos > nlmsg->buf + sizeof(nlmsg->buf) || nlmsg->nesting)\
	exit(1);\
	struct nlmsghdr* hdr = (struct nlmsghdr*)nlmsg->buf;\
	hdr->nlmsg_len = nlmsg->pos - nlmsg->buf;\
	struct sockaddr_nl addr;\
	memset(&addr, 0, sizeof(addr));\
	addr.nl_family = AF_NETLINK;\
	ssize_t n = sendto(sock, nlmsg->buf, hdr->nlmsg_len, 0, (struct sockaddr*)&addr, sizeof(addr));\
	if (n != (ssize_t)hdr->nlmsg_len) \{\
		if (dofail)\
	exit(1);\
		return -1;\
	\}\
	n = recv(sock, nlmsg->buf, sizeof(nlmsg->buf), 0);\
	if (reply_len)\
		*reply_len = 0;\
	if (n < 0) \{\
		if (dofail)\
	exit(1);\
		return -1;\
	\}\
	if (n < (ssize_t)sizeof(struct nlmsghdr)) \{\
		errno = EINVAL;\
		if (dofail)\
	exit(1);\
		return -1;\
	\}\
	if (hdr->nlmsg_type == NLMSG_DONE)\
		return 0;\
	if (reply_len && hdr->nlmsg_type == reply_type) \{\
		*reply_len = n;\
		return 0;\
	\}\
	if (n < (ssize_t)(sizeof(struct nlmsghdr) + sizeof(struct nlmsgerr))) \{\
		errno = EINVAL;\
		if (dofail)\
	exit(1);\
		return -1;\
	\}\
	if (hdr->nlmsg_type != NLMSG_ERROR) \{\
		errno = EINVAL;\
		if (dofail)\
	exit(1);\
		return -1;\
	\}\
	errno = -((struct nlmsgerr*)(hdr + 1))->error;\
	return -errno;\
\}\
\
static int netlink_send(struct nlmsg* nlmsg, int sock)\
\{\
	return netlink_send_ext(nlmsg, sock, 0, NULL, true);\
\}\
\
static int netlink_query_family_id(struct nlmsg* nlmsg, int sock, const char* family_name, bool dofail)\
\{\
	struct genlmsghdr genlhdr;\
	memset(&genlhdr, 0, sizeof(genlhdr));\
	genlhdr.cmd = CTRL_CMD_GETFAMILY;\
	netlink_init(nlmsg, GENL_ID_CTRL, 0, &genlhdr, sizeof(genlhdr));\
	netlink_attr(nlmsg, CTRL_ATTR_FAMILY_NAME, family_name, strnlen(family_name, GENL_NAMSIZ - 1) + 1);\
	int n = 0;\
	int err = netlink_send_ext(nlmsg, sock, GENL_ID_CTRL, &n, dofail);\
	if (err < 0) \{\
		return -1;\
	\}\
	uint16_t id = 0;\
	struct nlattr* attr = (struct nlattr*)(nlmsg->buf + NLMSG_HDRLEN + NLMSG_ALIGN(sizeof(genlhdr)));\
	for (; (char*)attr < nlmsg->buf + n; attr = (struct nlattr*)((char*)attr + NLMSG_ALIGN(attr->nla_len))) \{\
		if (attr->nla_type == CTRL_ATTR_FAMILY_ID) \{\
			id = *(uint16_t*)(attr + 1);\
			break;\
		\}\
	\}\
	if (!id) \{\
		errno = EINVAL;\
		return -1;\
	\}\
	recv(sock, nlmsg->buf, sizeof(nlmsg->buf), 0);\
	return id;\
\}\
\
static int netlink_next_msg(struct nlmsg* nlmsg, unsigned int offset,\
			    unsigned int total_len)\
\{\
	struct nlmsghdr* hdr = (struct nlmsghdr*)(nlmsg->buf + offset);\
	if (offset == total_len || offset + hdr->nlmsg_len > total_len)\
		return -1;\
	return hdr->nlmsg_len;\
\}\
\
static void netlink_add_device_impl(struct nlmsg* nlmsg, const char* type,\
				    const char* name)\
\{\
	struct ifinfomsg hdr;\
	memset(&hdr, 0, sizeof(hdr));\
	netlink_init(nlmsg, RTM_NEWLINK, NLM_F_EXCL | NLM_F_CREATE, &hdr, sizeof(hdr));\
	if (name)\
		netlink_attr(nlmsg, IFLA_IFNAME, name, strlen(name));\
	netlink_nest(nlmsg, IFLA_LINKINFO);\
	netlink_attr(nlmsg, IFLA_INFO_KIND, type, strlen(type));\
\}\
\
static void netlink_add_device(struct nlmsg* nlmsg, int sock, const char* type,\
			       const char* name)\
\{\
	netlink_add_device_impl(nlmsg, type, name);\
	netlink_done(nlmsg);\
	int err = netlink_send(nlmsg, sock);\
	if (err < 0) \{\
	\}\
\}\
\
static void netlink_add_veth(struct nlmsg* nlmsg, int sock, const char* name,\
			     const char* peer)\
\{\
	netlink_add_device_impl(nlmsg, "veth", name);\
	netlink_nest(nlmsg, IFLA_INFO_DATA);\
	netlink_nest(nlmsg, VETH_INFO_PEER);\
	nlmsg->pos += sizeof(struct ifinfomsg);\
	netlink_attr(nlmsg, IFLA_IFNAME, peer, strlen(peer));\
	netlink_done(nlmsg);\
	netlink_done(nlmsg);\
	netlink_done(nlmsg);\
	int err = netlink_send(nlmsg, sock);\
	if (err < 0) \{\
	\}\
\}\
\
static void netlink_add_hsr(struct nlmsg* nlmsg, int sock, const char* name,\
			    const char* slave1, const char* slave2)\
\{\
	netlink_add_device_impl(nlmsg, "hsr", name);\
	netlink_nest(nlmsg, IFLA_INFO_DATA);\
	int ifindex1 = if_nametoindex(slave1);\
	netlink_attr(nlmsg, IFLA_HSR_SLAVE1, &ifindex1, sizeof(ifindex1));\
	int ifindex2 = if_nametoindex(slave2);\
	netlink_attr(nlmsg, IFLA_HSR_SLAVE2, &ifindex2, sizeof(ifindex2));\
	netlink_done(nlmsg);\
	netlink_done(nlmsg);\
	int err = netlink_send(nlmsg, sock);\
	if (err < 0) \{\
	\}\
\}\
\
static void netlink_add_linked(struct nlmsg* nlmsg, int sock, const char* type, const char* name, const char* link)\
\{\
	netlink_add_device_impl(nlmsg, type, name);\
	netlink_done(nlmsg);\
	int ifindex = if_nametoindex(link);\
	netlink_attr(nlmsg, IFLA_LINK, &ifindex, sizeof(ifindex));\
	int err = netlink_send(nlmsg, sock);\
	if (err < 0) \{\
	\}\
\}\
\
static void netlink_add_vlan(struct nlmsg* nlmsg, int sock, const char* name, const char* link, uint16_t id, uint16_t proto)\
\{\
	netlink_add_device_impl(nlmsg, "vlan", name);\
	netlink_nest(nlmsg, IFLA_INFO_DATA);\
	netlink_attr(nlmsg, IFLA_VLAN_ID, &id, sizeof(id));\
	netlink_attr(nlmsg, IFLA_VLAN_PROTOCOL, &proto, sizeof(proto));\
	netlink_done(nlmsg);\
	netlink_done(nlmsg);\
	int ifindex = if_nametoindex(link);\
	netlink_attr(nlmsg, IFLA_LINK, &ifindex, sizeof(ifindex));\
	int err = netlink_send(nlmsg, sock);\
	if (err < 0) \{\
	\}\
\}\
\
static void netlink_add_macvlan(struct nlmsg* nlmsg, int sock, const char* name, const char* link)\
\{\
	netlink_add_device_impl(nlmsg, "macvlan", name);\
	netlink_nest(nlmsg, IFLA_INFO_DATA);\
	uint32_t mode = MACVLAN_MODE_BRIDGE;\
	netlink_attr(nlmsg, IFLA_MACVLAN_MODE, &mode, sizeof(mode));\
	netlink_done(nlmsg);\
	netlink_done(nlmsg);\
	int ifindex = if_nametoindex(link);\
	netlink_attr(nlmsg, IFLA_LINK, &ifindex, sizeof(ifindex));\
	int err = netlink_send(nlmsg, sock);\
	if (err < 0) \{\
	\}\
\}\
\
static void netlink_add_geneve(struct nlmsg* nlmsg, int sock, const char* name, uint32_t vni, struct in_addr* addr4, struct in6_addr* addr6)\
\{\
	netlink_add_device_impl(nlmsg, "geneve", name);\
	netlink_nest(nlmsg, IFLA_INFO_DATA);\
	netlink_attr(nlmsg, IFLA_GENEVE_ID, &vni, sizeof(vni));\
	if (addr4)\
		netlink_attr(nlmsg, IFLA_GENEVE_REMOTE, addr4, sizeof(*addr4));\
	if (addr6)\
		netlink_attr(nlmsg, IFLA_GENEVE_REMOTE6, addr6, sizeof(*addr6));\
	netlink_done(nlmsg);\
	netlink_done(nlmsg);\
	int err = netlink_send(nlmsg, sock);\
	if (err < 0) \{\
	\}\
\}\
\
#define IFLA_IPVLAN_FLAGS 2\
#define IPVLAN_MODE_L3S 2\
#undef IPVLAN_F_VEPA\
#define IPVLAN_F_VEPA 2\
\
static void netlink_add_ipvlan(struct nlmsg* nlmsg, int sock, const char* name, const char* link, uint16_t mode, uint16_t flags)\
\{\
	netlink_add_device_impl(nlmsg, "ipvlan", name);\
	netlink_nest(nlmsg, IFLA_INFO_DATA);\
	netlink_attr(nlmsg, IFLA_IPVLAN_MODE, &mode, sizeof(mode));\
	netlink_attr(nlmsg, IFLA_IPVLAN_FLAGS, &flags, sizeof(flags));\
	netlink_done(nlmsg);\
	netlink_done(nlmsg);\
	int ifindex = if_nametoindex(link);\
	netlink_attr(nlmsg, IFLA_LINK, &ifindex, sizeof(ifindex));\
	int err = netlink_send(nlmsg, sock);\
	if (err < 0) \{\
	\}\
\}\
\
static void netlink_device_change(struct nlmsg* nlmsg, int sock, const char* name, bool up,\
				  const char* master, const void* mac, int macsize,\
				  const char* new_name)\
\{\
	struct ifinfomsg hdr;\
	memset(&hdr, 0, sizeof(hdr));\
	if (up)\
		hdr.ifi_flags = hdr.ifi_change = IFF_UP;\
	hdr.ifi_index = if_nametoindex(name);\
	netlink_init(nlmsg, RTM_NEWLINK, 0, &hdr, sizeof(hdr));\
	if (new_name)\
		netlink_attr(nlmsg, IFLA_IFNAME, new_name, strlen(new_name));\
	if (master) \{\
		int ifindex = if_nametoindex(master);\
		netlink_attr(nlmsg, IFLA_MASTER, &ifindex, sizeof(ifindex));\
	\}\
	if (macsize)\
		netlink_attr(nlmsg, IFLA_ADDRESS, mac, macsize);\
	int err = netlink_send(nlmsg, sock);\
	if (err < 0) \{\
	\}\
\}\
\
static int netlink_add_addr(struct nlmsg* nlmsg, int sock, const char* dev,\
			    const void* addr, int addrsize)\
\{\
	struct ifaddrmsg hdr;\
	memset(&hdr, 0, sizeof(hdr));\
	hdr.ifa_family = addrsize == 4 ? AF_INET : AF_INET6;\
	hdr.ifa_prefixlen = addrsize == 4 ? 24 : 120;\
	hdr.ifa_scope = RT_SCOPE_UNIVERSE;\
	hdr.ifa_index = if_nametoindex(dev);\
	netlink_init(nlmsg, RTM_NEWADDR, NLM_F_CREATE | NLM_F_REPLACE, &hdr, sizeof(hdr));\
	netlink_attr(nlmsg, IFA_LOCAL, addr, addrsize);\
	netlink_attr(nlmsg, IFA_ADDRESS, addr, addrsize);\
	return netlink_send(nlmsg, sock);\
\}\
\
static void netlink_add_addr4(struct nlmsg* nlmsg, int sock,\
			      const char* dev, const char* addr)\
\{\
	struct in_addr in_addr;\
	inet_pton(AF_INET, addr, &in_addr);\
	int err = netlink_add_addr(nlmsg, sock, dev, &in_addr, sizeof(in_addr));\
	if (err < 0) \{\
	\}\
\}\
\
static void netlink_add_addr6(struct nlmsg* nlmsg, int sock,\
			      const char* dev, const char* addr)\
\{\
	struct in6_addr in6_addr;\
	inet_pton(AF_INET6, addr, &in6_addr);\
	int err = netlink_add_addr(nlmsg, sock, dev, &in6_addr, sizeof(in6_addr));\
	if (err < 0) \{\
	\}\
\}\
\
static struct nlmsg nlmsg;\
\
#define DEVLINK_FAMILY_NAME "devlink"\
\
#define DEVLINK_CMD_PORT_GET 5\
#define DEVLINK_ATTR_BUS_NAME 1\
#define DEVLINK_ATTR_DEV_NAME 2\
#define DEVLINK_ATTR_NETDEV_NAME 7\
\
static struct nlmsg nlmsg2;\
\
static void initialize_devlink_ports(const char* bus_name, const char* dev_name,\
				     const char* netdev_prefix)\
\{\
	struct genlmsghdr genlhdr;\
	int len, total_len, id, err, offset;\
	uint16_t netdev_index;\
	int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);\
	if (sock == -1)\
	exit(1);\
	int rtsock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);\
	if (rtsock == -1)\
	exit(1);\
	id = netlink_query_family_id(&nlmsg, sock, DEVLINK_FAMILY_NAME, true);\
	if (id == -1)\
		goto error;\
	memset(&genlhdr, 0, sizeof(genlhdr));\
	genlhdr.cmd = DEVLINK_CMD_PORT_GET;\
	netlink_init(&nlmsg, id, NLM_F_DUMP, &genlhdr, sizeof(genlhdr));\
	netlink_attr(&nlmsg, DEVLINK_ATTR_BUS_NAME, bus_name, strlen(bus_name) + 1);\
	netlink_attr(&nlmsg, DEVLINK_ATTR_DEV_NAME, dev_name, strlen(dev_name) + 1);\
	err = netlink_send_ext(&nlmsg, sock, id, &total_len, true);\
	if (err < 0) \{\
		goto error;\
	\}\
	offset = 0;\
	netdev_index = 0;\
	while ((len = netlink_next_msg(&nlmsg, offset, total_len)) != -1) \{\
		struct nlattr* attr = (struct nlattr*)(nlmsg.buf + offset + NLMSG_HDRLEN + NLMSG_ALIGN(sizeof(genlhdr)));\
		for (; (char*)attr < nlmsg.buf + offset + len; attr = (struct nlattr*)((char*)attr + NLMSG_ALIGN(attr->nla_len))) \{\
			if (attr->nla_type == DEVLINK_ATTR_NETDEV_NAME) \{\
				char* port_name;\
				char netdev_name[IFNAMSIZ];\
				port_name = (char*)(attr + 1);\
				snprintf(netdev_name, sizeof(netdev_name), "%s%d", netdev_prefix, netdev_index);\
				netlink_device_change(&nlmsg2, rtsock, port_name, true, 0, 0, 0, netdev_name);\
				break;\
			\}\
		\}\
		offset += len;\
		netdev_index++;\
	\}\
error:\
	close(rtsock);\
	close(sock);\
\}\
\
#define DEV_IPV4 "172.20.20.%d"\
#define DEV_IPV6 "fe80::%02x"\
#define DEV_MAC 0x00aaaaaaaaaa\
\
static void netdevsim_add(unsigned int addr, unsigned int port_count)\
\{\
	char buf[16];\
	sprintf(buf, "%u %u", addr, port_count);\
	if (write_file("/sys/bus/netdevsim/new_device", buf)) \{\
		snprintf(buf, sizeof(buf), "netdevsim%d", addr);\
		initialize_devlink_ports("netdevsim", buf, "netdevsim");\
	\}\
\}\
\
#define WG_GENL_NAME "wireguard"\
enum wg_cmd \{\
	WG_CMD_GET_DEVICE,\
	WG_CMD_SET_DEVICE,\
\};\
enum wgdevice_attribute \{\
	WGDEVICE_A_UNSPEC,\
	WGDEVICE_A_IFINDEX,\
	WGDEVICE_A_IFNAME,\
	WGDEVICE_A_PRIVATE_KEY,\
	WGDEVICE_A_PUBLIC_KEY,\
	WGDEVICE_A_FLAGS,\
	WGDEVICE_A_LISTEN_PORT,\
	WGDEVICE_A_FWMARK,\
	WGDEVICE_A_PEERS,\
\};\
enum wgpeer_attribute \{\
	WGPEER_A_UNSPEC,\
	WGPEER_A_PUBLIC_KEY,\
	WGPEER_A_PRESHARED_KEY,\
	WGPEER_A_FLAGS,\
	WGPEER_A_ENDPOINT,\
	WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL,\
	WGPEER_A_LAST_HANDSHAKE_TIME,\
	WGPEER_A_RX_BYTES,\
	WGPEER_A_TX_BYTES,\
	WGPEER_A_ALLOWEDIPS,\
	WGPEER_A_PROTOCOL_VERSION,\
\};\
enum wgallowedip_attribute \{\
	WGALLOWEDIP_A_UNSPEC,\
	WGALLOWEDIP_A_FAMILY,\
	WGALLOWEDIP_A_IPADDR,\
	WGALLOWEDIP_A_CIDR_MASK,\
\};\
\
static void netlink_wireguard_setup(void)\
\{\
	const char ifname_a[] = "wg0";\
	const char ifname_b[] = "wg1";\
	const char ifname_c[] = "wg2";\
	const char private_a[] = "\\xa0\\x5c\\xa8\\x4f\\x6c\\x9c\\x8e\\x38\\x53\\xe2\\xfd\\x7a\\x70\\xae\\x0f\\xb2\\x0f\\xa1\\x52\\x60\\x0c\\xb0\\x08\\x45\\x17\\x4f\\x08\\x07\\x6f\\x8d\\x78\\x43";\
	const char private_b[] = "\\xb0\\x80\\x73\\xe8\\xd4\\x4e\\x91\\xe3\\xda\\x92\\x2c\\x22\\x43\\x82\\x44\\xbb\\x88\\x5c\\x69\\xe2\\x69\\xc8\\xe9\\xd8\\x35\\xb1\\x14\\x29\\x3a\\x4d\\xdc\\x6e";\
	const char private_c[] = "\\xa0\\xcb\\x87\\x9a\\x47\\xf5\\xbc\\x64\\x4c\\x0e\\x69\\x3f\\xa6\\xd0\\x31\\xc7\\x4a\\x15\\x53\\xb6\\xe9\\x01\\xb9\\xff\\x2f\\x51\\x8c\\x78\\x04\\x2f\\xb5\\x42";\
	const char public_a[] = "\\x97\\x5c\\x9d\\x81\\xc9\\x83\\xc8\\x20\\x9e\\xe7\\x81\\x25\\x4b\\x89\\x9f\\x8e\\xd9\\x25\\xae\\x9f\\x09\\x23\\xc2\\x3c\\x62\\xf5\\x3c\\x57\\xcd\\xbf\\x69\\x1c";\
	const char public_b[] = "\\xd1\\x73\\x28\\x99\\xf6\\x11\\xcd\\x89\\x94\\x03\\x4d\\x7f\\x41\\x3d\\xc9\\x57\\x63\\x0e\\x54\\x93\\xc2\\x85\\xac\\xa4\\x00\\x65\\xcb\\x63\\x11\\xbe\\x69\\x6b";\
	const char public_c[] = "\\xf4\\x4d\\xa3\\x67\\xa8\\x8e\\xe6\\x56\\x4f\\x02\\x02\\x11\\x45\\x67\\x27\\x08\\x2f\\x5c\\xeb\\xee\\x8b\\x1b\\xf5\\xeb\\x73\\x37\\x34\\x1b\\x45\\x9b\\x39\\x22";\
	const uint16_t listen_a = 20001;\
	const uint16_t listen_b = 20002;\
	const uint16_t listen_c = 20003;\
	const uint16_t af_inet = AF_INET;\
	const uint16_t af_inet6 = AF_INET6;\
	const struct sockaddr_in endpoint_b_v4 = \{\
	    .sin_family = AF_INET,\
	    .sin_port = htons(listen_b),\
	    .sin_addr = \{htonl(INADDR_LOOPBACK)\}\};\
	const struct sockaddr_in endpoint_c_v4 = \{\
	    .sin_family = AF_INET,\
	    .sin_port = htons(listen_c),\
	    .sin_addr = \{htonl(INADDR_LOOPBACK)\}\};\
	struct sockaddr_in6 endpoint_a_v6 = \{\
	    .sin6_family = AF_INET6,\
	    .sin6_port = htons(listen_a)\};\
	endpoint_a_v6.sin6_addr = in6addr_loopback;\
	struct sockaddr_in6 endpoint_c_v6 = \{\
	    .sin6_family = AF_INET6,\
	    .sin6_port = htons(listen_c)\};\
	endpoint_c_v6.sin6_addr = in6addr_loopback;\
	const struct in_addr first_half_v4 = \{0\};\
	const struct in_addr second_half_v4 = \{(uint32_t)htonl(128 << 24)\};\
	const struct in6_addr first_half_v6 = \{\{\{0\}\}\};\
	const struct in6_addr second_half_v6 = \{\{\{0x80\}\}\};\
	const uint8_t half_cidr = 1;\
	const uint16_t persistent_keepalives[] = \{1, 3, 7, 9, 14, 19\};\
	struct genlmsghdr genlhdr = \{\
	    .cmd = WG_CMD_SET_DEVICE,\
	    .version = 1\};\
	int sock;\
	int id, err;\
	sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);\
	if (sock == -1) \{\
		return;\
	\}\
	id = netlink_query_family_id(&nlmsg, sock, WG_GENL_NAME, true);\
	if (id == -1)\
		goto error;\
	netlink_init(&nlmsg, id, 0, &genlhdr, sizeof(genlhdr));\
	netlink_attr(&nlmsg, WGDEVICE_A_IFNAME, ifname_a, strlen(ifname_a) + 1);\
	netlink_attr(&nlmsg, WGDEVICE_A_PRIVATE_KEY, private_a, 32);\
	netlink_attr(&nlmsg, WGDEVICE_A_LISTEN_PORT, &listen_a, 2);\
	netlink_nest(&nlmsg, NLA_F_NESTED | WGDEVICE_A_PEERS);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGPEER_A_PUBLIC_KEY, public_b, 32);\
	netlink_attr(&nlmsg, WGPEER_A_ENDPOINT, &endpoint_b_v4, sizeof(endpoint_b_v4));\
	netlink_attr(&nlmsg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, &persistent_keepalives[0], 2);\
	netlink_nest(&nlmsg, NLA_F_NESTED | WGPEER_A_ALLOWEDIPS);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet, 2);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &first_half_v4, sizeof(first_half_v4));\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1);\
	netlink_done(&nlmsg);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet6, 2);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &first_half_v6, sizeof(first_half_v6));\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1);\
	netlink_done(&nlmsg);\
	netlink_done(&nlmsg);\
	netlink_done(&nlmsg);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGPEER_A_PUBLIC_KEY, public_c, 32);\
	netlink_attr(&nlmsg, WGPEER_A_ENDPOINT, &endpoint_c_v6, sizeof(endpoint_c_v6));\
	netlink_attr(&nlmsg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, &persistent_keepalives[1], 2);\
	netlink_nest(&nlmsg, NLA_F_NESTED | WGPEER_A_ALLOWEDIPS);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet, 2);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &second_half_v4, sizeof(second_half_v4));\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1);\
	netlink_done(&nlmsg);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet6, 2);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &second_half_v6, sizeof(second_half_v6));\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1);\
	netlink_done(&nlmsg);\
	netlink_done(&nlmsg);\
	netlink_done(&nlmsg);\
	netlink_done(&nlmsg);\
	err = netlink_send(&nlmsg, sock);\
	if (err < 0) \{\
	\}\
	netlink_init(&nlmsg, id, 0, &genlhdr, sizeof(genlhdr));\
	netlink_attr(&nlmsg, WGDEVICE_A_IFNAME, ifname_b, strlen(ifname_b) + 1);\
	netlink_attr(&nlmsg, WGDEVICE_A_PRIVATE_KEY, private_b, 32);\
	netlink_attr(&nlmsg, WGDEVICE_A_LISTEN_PORT, &listen_b, 2);\
	netlink_nest(&nlmsg, NLA_F_NESTED | WGDEVICE_A_PEERS);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGPEER_A_PUBLIC_KEY, public_a, 32);\
	netlink_attr(&nlmsg, WGPEER_A_ENDPOINT, &endpoint_a_v6, sizeof(endpoint_a_v6));\
	netlink_attr(&nlmsg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, &persistent_keepalives[2], 2);\
	netlink_nest(&nlmsg, NLA_F_NESTED | WGPEER_A_ALLOWEDIPS);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet, 2);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &first_half_v4, sizeof(first_half_v4));\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1);\
	netlink_done(&nlmsg);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet6, 2);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &first_half_v6, sizeof(first_half_v6));\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1);\
	netlink_done(&nlmsg);\
	netlink_done(&nlmsg);\
	netlink_done(&nlmsg);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGPEER_A_PUBLIC_KEY, public_c, 32);\
	netlink_attr(&nlmsg, WGPEER_A_ENDPOINT, &endpoint_c_v4, sizeof(endpoint_c_v4));\
	netlink_attr(&nlmsg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, &persistent_keepalives[3], 2);\
	netlink_nest(&nlmsg, NLA_F_NESTED | WGPEER_A_ALLOWEDIPS);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet, 2);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &second_half_v4, sizeof(second_half_v4));\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1);\
	netlink_done(&nlmsg);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet6, 2);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &second_half_v6, sizeof(second_half_v6));\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1);\
	netlink_done(&nlmsg);\
	netlink_done(&nlmsg);\
	netlink_done(&nlmsg);\
	netlink_done(&nlmsg);\
	err = netlink_send(&nlmsg, sock);\
	if (err < 0) \{\
	\}\
	netlink_init(&nlmsg, id, 0, &genlhdr, sizeof(genlhdr));\
	netlink_attr(&nlmsg, WGDEVICE_A_IFNAME, ifname_c, strlen(ifname_c) + 1);\
	netlink_attr(&nlmsg, WGDEVICE_A_PRIVATE_KEY, private_c, 32);\
	netlink_attr(&nlmsg, WGDEVICE_A_LISTEN_PORT, &listen_c, 2);\
	netlink_nest(&nlmsg, NLA_F_NESTED | WGDEVICE_A_PEERS);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGPEER_A_PUBLIC_KEY, public_a, 32);\
	netlink_attr(&nlmsg, WGPEER_A_ENDPOINT, &endpoint_a_v6, sizeof(endpoint_a_v6));\
	netlink_attr(&nlmsg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, &persistent_keepalives[4], 2);\
	netlink_nest(&nlmsg, NLA_F_NESTED | WGPEER_A_ALLOWEDIPS);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet, 2);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &first_half_v4, sizeof(first_half_v4));\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1);\
	netlink_done(&nlmsg);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet6, 2);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &first_half_v6, sizeof(first_half_v6));\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1);\
	netlink_done(&nlmsg);\
	netlink_done(&nlmsg);\
	netlink_done(&nlmsg);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGPEER_A_PUBLIC_KEY, public_b, 32);\
	netlink_attr(&nlmsg, WGPEER_A_ENDPOINT, &endpoint_b_v4, sizeof(endpoint_b_v4));\
	netlink_attr(&nlmsg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, &persistent_keepalives[5], 2);\
	netlink_nest(&nlmsg, NLA_F_NESTED | WGPEER_A_ALLOWEDIPS);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet, 2);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &second_half_v4, sizeof(second_half_v4));\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1);\
	netlink_done(&nlmsg);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet6, 2);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &second_half_v6, sizeof(second_half_v6));\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1);\
	netlink_done(&nlmsg);\
	netlink_done(&nlmsg);\
	netlink_done(&nlmsg);\
	netlink_done(&nlmsg);\
	err = netlink_send(&nlmsg, sock);\
	if (err < 0) \{\
	\}\
\
error:\
	close(sock);\
\}\
static void initialize_netdevices(void)\
\{\
	char netdevsim[16];\
	sprintf(netdevsim, "netdevsim%d", (int)procid);\
	struct \{\
		const char* type;\
		const char* dev;\
	\} devtypes[] = \{\
	    \{"ip6gretap", "ip6gretap0"\},\
	    \{"bridge", "bridge0"\},\
	    \{"vcan", "vcan0"\},\
	    \{"bond", "bond0"\},\
	    \{"team", "team0"\},\
	    \{"dummy", "dummy0"\},\
	    \{"nlmon", "nlmon0"\},\
	    \{"caif", "caif0"\},\
	    \{"batadv", "batadv0"\},\
	    \{"vxcan", "vxcan1"\},\
	    \{"netdevsim", netdevsim\},\
	    \{"veth", 0\},\
	    \{"xfrm", "xfrm0"\},\
	    \{"wireguard", "wg0"\},\
	    \{"wireguard", "wg1"\},\
	    \{"wireguard", "wg2"\},\
	\};\
	const char* devmasters[] = \{"bridge", "bond", "team", "batadv"\};\
	struct \{\
		const char* name;\
		int macsize;\
		bool noipv6;\
	\} devices[] = \{\
	    \{"lo", ETH_ALEN\},\
	    \{"sit0", 0\},\
	    \{"bridge0", ETH_ALEN\},\
	    \{"vcan0", 0, true\},\
	    \{"tunl0", 0\},\
	    \{"gre0", 0\},\
	    \{"gretap0", ETH_ALEN\},\
	    \{"ip_vti0", 0\},\
	    \{"ip6_vti0", 0\},\
	    \{"ip6tnl0", 0\},\
	    \{"ip6gre0", 0\},\
	    \{"ip6gretap0", ETH_ALEN\},\
	    \{"erspan0", ETH_ALEN\},\
	    \{"bond0", ETH_ALEN\},\
	    \{"veth0", ETH_ALEN\},\
	    \{"veth1", ETH_ALEN\},\
	    \{"team0", ETH_ALEN\},\
	    \{"veth0_to_bridge", ETH_ALEN\},\
	    \{"veth1_to_bridge", ETH_ALEN\},\
	    \{"veth0_to_bond", ETH_ALEN\},\
	    \{"veth1_to_bond", ETH_ALEN\},\
	    \{"veth0_to_team", ETH_ALEN\},\
	    \{"veth1_to_team", ETH_ALEN\},\
	    \{"veth0_to_hsr", ETH_ALEN\},\
	    \{"veth1_to_hsr", ETH_ALEN\},\
	    \{"hsr0", 0\},\
	    \{"dummy0", ETH_ALEN\},\
	    \{"nlmon0", 0\},\
	    \{"vxcan0", 0, true\},\
	    \{"vxcan1", 0, true\},\
	    \{"caif0", ETH_ALEN\},\
	    \{"batadv0", ETH_ALEN\},\
	    \{netdevsim, ETH_ALEN\},\
	    \{"xfrm0", ETH_ALEN\},\
	    \{"veth0_virt_wifi", ETH_ALEN\},\
	    \{"veth1_virt_wifi", ETH_ALEN\},\
	    \{"virt_wifi0", ETH_ALEN\},\
	    \{"veth0_vlan", ETH_ALEN\},\
	    \{"veth1_vlan", ETH_ALEN\},\
	    \{"vlan0", ETH_ALEN\},\
	    \{"vlan1", ETH_ALEN\},\
	    \{"macvlan0", ETH_ALEN\},\
	    \{"macvlan1", ETH_ALEN\},\
	    \{"ipvlan0", ETH_ALEN\},\
	    \{"ipvlan1", ETH_ALEN\},\
	    \{"veth0_macvtap", ETH_ALEN\},\
	    \{"veth1_macvtap", ETH_ALEN\},\
	    \{"macvtap0", ETH_ALEN\},\
	    \{"macsec0", ETH_ALEN\},\
	    \{"veth0_to_batadv", ETH_ALEN\},\
	    \{"veth1_to_batadv", ETH_ALEN\},\
	    \{"batadv_slave_0", ETH_ALEN\},\
	    \{"batadv_slave_1", ETH_ALEN\},\
	    \{"geneve0", ETH_ALEN\},\
	    \{"geneve1", ETH_ALEN\},\
	    \{"wg0", 0\},\
	    \{"wg1", 0\},\
	    \{"wg2", 0\},\
	\};\
	int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);\
	if (sock == -1)\
	exit(1);\
	unsigned i;\
	for (i = 0; i < sizeof(devtypes) / sizeof(devtypes[0]); i++)\
		netlink_add_device(&nlmsg, sock, devtypes[i].type, devtypes[i].dev);\
	for (i = 0; i < sizeof(devmasters) / (sizeof(devmasters[0])); i++) \{\
		char master[32], slave0[32], veth0[32], slave1[32], veth1[32];\
		sprintf(slave0, "%s_slave_0", devmasters[i]);\
		sprintf(veth0, "veth0_to_%s", devmasters[i]);\
		netlink_add_veth(&nlmsg, sock, slave0, veth0);\
		sprintf(slave1, "%s_slave_1", devmasters[i]);\
		sprintf(veth1, "veth1_to_%s", devmasters[i]);\
		netlink_add_veth(&nlmsg, sock, slave1, veth1);\
		sprintf(master, "%s0", devmasters[i]);\
		netlink_device_change(&nlmsg, sock, slave0, false, master, 0, 0, NULL);\
		netlink_device_change(&nlmsg, sock, slave1, false, master, 0, 0, NULL);\
	\}\
	netlink_device_change(&nlmsg, sock, "bridge_slave_0", true, 0, 0, 0, NULL);\
	netlink_device_change(&nlmsg, sock, "bridge_slave_1", true, 0, 0, 0, NULL);\
	netlink_add_veth(&nlmsg, sock, "hsr_slave_0", "veth0_to_hsr");\
	netlink_add_veth(&nlmsg, sock, "hsr_slave_1", "veth1_to_hsr");\
	netlink_add_hsr(&nlmsg, sock, "hsr0", "hsr_slave_0", "hsr_slave_1");\
	netlink_device_change(&nlmsg, sock, "hsr_slave_0", true, 0, 0, 0, NULL);\
	netlink_device_change(&nlmsg, sock, "hsr_slave_1", true, 0, 0, 0, NULL);\
	netlink_add_veth(&nlmsg, sock, "veth0_virt_wifi", "veth1_virt_wifi");\
	netlink_add_linked(&nlmsg, sock, "virt_wifi", "virt_wifi0", "veth1_virt_wifi");\
	netlink_add_veth(&nlmsg, sock, "veth0_vlan", "veth1_vlan");\
	netlink_add_vlan(&nlmsg, sock, "vlan0", "veth0_vlan", 0, htons(ETH_P_8021Q));\
	netlink_add_vlan(&nlmsg, sock, "vlan1", "veth0_vlan", 1, htons(ETH_P_8021AD));\
	netlink_add_macvlan(&nlmsg, sock, "macvlan0", "veth1_vlan");\
	netlink_add_macvlan(&nlmsg, sock, "macvlan1", "veth1_vlan");\
	netlink_add_ipvlan(&nlmsg, sock, "ipvlan0", "veth0_vlan", IPVLAN_MODE_L2, 0);\
	netlink_add_ipvlan(&nlmsg, sock, "ipvlan1", "veth0_vlan", IPVLAN_MODE_L3S, IPVLAN_F_VEPA);\
	netlink_add_veth(&nlmsg, sock, "veth0_macvtap", "veth1_macvtap");\
	netlink_add_linked(&nlmsg, sock, "macvtap", "macvtap0", "veth0_macvtap");\
	netlink_add_linked(&nlmsg, sock, "macsec", "macsec0", "veth1_macvtap");\
	char addr[32];\
	sprintf(addr, DEV_IPV4, 14 + 10);\
	struct in_addr geneve_addr4;\
	if (inet_pton(AF_INET, addr, &geneve_addr4) <= 0)\
	exit(1);\
	struct in6_addr geneve_addr6;\
	if (inet_pton(AF_INET6, "fc00::01", &geneve_addr6) <= 0)\
	exit(1);\
	netlink_add_geneve(&nlmsg, sock, "geneve0", 0, &geneve_addr4, 0);\
	netlink_add_geneve(&nlmsg, sock, "geneve1", 1, 0, &geneve_addr6);\
	netdevsim_add((int)procid, 4);\
	netlink_wireguard_setup();\
	for (i = 0; i < sizeof(devices) / (sizeof(devices[0])); i++) \{\
		char addr[32];\
		sprintf(addr, DEV_IPV4, i + 10);\
		netlink_add_addr4(&nlmsg, sock, devices[i].name, addr);\
		if (!devices[i].noipv6) \{\
			sprintf(addr, DEV_IPV6, i + 10);\
			netlink_add_addr6(&nlmsg, sock, devices[i].name, addr);\
		\}\
		uint64_t macaddr = DEV_MAC + ((i + 10ull) << 40);\
		netlink_device_change(&nlmsg, sock, devices[i].name, true, 0, &macaddr, devices[i].macsize, NULL);\
	\}\
	close(sock);\
\}\
static void initialize_netdevices_init(void)\
\{\
	int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);\
	if (sock == -1)\
	exit(1);\
	struct \{\
		const char* type;\
		int macsize;\
		bool noipv6;\
		bool noup;\
	\} devtypes[] = \{\
	    \{"nr", 7, true\},\
	    \{"rose", 5, true, true\},\
	\};\
	unsigned i;\
	for (i = 0; i < sizeof(devtypes) / sizeof(devtypes[0]); i++) \{\
		char dev[32], addr[32];\
		sprintf(dev, "%s%d", devtypes[i].type, (int)procid);\
		sprintf(addr, "172.30.%d.%d", i, (int)procid + 1);\
		netlink_add_addr4(&nlmsg, sock, dev, addr);\
		if (!devtypes[i].noipv6) \{\
			sprintf(addr, "fe88::%02x:%02x", i, (int)procid + 1);\
			netlink_add_addr6(&nlmsg, sock, dev, addr);\
		\}\
		int macsize = devtypes[i].macsize;\
		uint64_t macaddr = 0xbbbbbb + ((unsigned long long)i << (8 * (macsize - 2))) +\
				 (procid << (8 * (macsize - 1)));\
		netlink_device_change(&nlmsg, sock, dev, !devtypes[i].noup, 0, &macaddr, macsize, NULL);\
	\}\
	close(sock);\
\}\
\
#define MAX_FDS 30\
\
#define BTPROTO_HCI 1\
#define ACL_LINK 1\
#define SCAN_PAGE 2\
\
typedef struct \{\
	uint8_t b[6];\
\} __attribute__((packed)) bdaddr_t;\
\
#define HCI_COMMAND_PKT 1\
#define HCI_EVENT_PKT 4\
#define HCI_VENDOR_PKT 0xff\
\
struct hci_command_hdr \{\
	uint16_t opcode;\
	uint8_t plen;\
\} __attribute__((packed));\
\
struct hci_event_hdr \{\
	uint8_t evt;\
	uint8_t plen;\
\} __attribute__((packed));\
\
#define HCI_EV_CONN_COMPLETE 0x03\
struct hci_ev_conn_complete \{\
	uint8_t status;\
	uint16_t handle;\
	bdaddr_t bdaddr;\
	uint8_t link_type;\
	uint8_t encr_mode;\
\} __attribute__((packed));\
\
#define HCI_EV_CONN_REQUEST 0x04\
struct hci_ev_conn_request \{\
	bdaddr_t bdaddr;\
	uint8_t dev_class[3];\
	uint8_t link_type;\
\} __attribute__((packed));\
\
#define HCI_EV_REMOTE_FEATURES 0x0b\
struct hci_ev_remote_features \{\
	uint8_t status;\
	uint16_t handle;\
	uint8_t features[8];\
\} __attribute__((packed));\
\
#define HCI_EV_CMD_COMPLETE 0x0e\
struct hci_ev_cmd_complete \{\
	uint8_t ncmd;\
	uint16_t opcode;\
\} __attribute__((packed));\
\
#define HCI_OP_WRITE_SCAN_ENABLE 0x0c1a\
\
#define HCI_OP_READ_BUFFER_SIZE 0x1005\
struct hci_rp_read_buffer_size \{\
	uint8_t status;\
	uint16_t acl_mtu;\
	uint8_t sco_mtu;\
	uint16_t acl_max_pkt;\
	uint16_t sco_max_pkt;\
\} __attribute__((packed));\
\
#define HCI_OP_READ_BD_ADDR 0x1009\
struct hci_rp_read_bd_addr \{\
	uint8_t status;\
	bdaddr_t bdaddr;\
\} __attribute__((packed));\
\
#define HCI_EV_LE_META 0x3e\
struct hci_ev_le_meta \{\
	uint8_t subevent;\
\} __attribute__((packed));\
\
#define HCI_EV_LE_CONN_COMPLETE 0x01\
struct hci_ev_le_conn_complete \{\
	uint8_t status;\
	uint16_t handle;\
	uint8_t role;\
	uint8_t bdaddr_type;\
	bdaddr_t bdaddr;\
	uint16_t interval;\
	uint16_t latency;\
	uint16_t supervision_timeout;\
	uint8_t clk_accurancy;\
\} __attribute__((packed));\
\
struct hci_dev_req \{\
	uint16_t dev_id;\
	uint32_t dev_opt;\
\};\
\
struct vhci_vendor_pkt \{\
	uint8_t type;\
	uint8_t opcode;\
	uint16_t id;\
\};\
\
#define HCIDEVUP _IOW('H', 201, int)\
#define HCISETSCAN _IOW('H', 221, int)\
\
static int vhci_fd = -1;\
\
static void rfkill_unblock_all()\
\{\
	int fd = open("/dev/rfkill", O_WRONLY);\
	if (fd < 0)\
	exit(1);\
	struct rfkill_event event = \{0\};\
	event.idx = 0;\
	event.type = RFKILL_TYPE_ALL;\
	event.op = RFKILL_OP_CHANGE_ALL;\
	event.soft = 0;\
	event.hard = 0;\
	if (write(fd, &event, sizeof(event)) < 0)\
	exit(1);\
	close(fd);\
\}\
\
static void hci_send_event_packet(int fd, uint8_t evt, void* data, size_t data_len)\
\{\
	struct iovec iv[3];\
	struct hci_event_hdr hdr;\
	hdr.evt = evt;\
	hdr.plen = data_len;\
	uint8_t type = HCI_EVENT_PKT;\
	iv[0].iov_base = &type;\
	iv[0].iov_len = sizeof(type);\
	iv[1].iov_base = &hdr;\
	iv[1].iov_len = sizeof(hdr);\
	iv[2].iov_base = data;\
	iv[2].iov_len = data_len;\
	if (writev(fd, iv, sizeof(iv) / sizeof(struct iovec)) < 0)\
	exit(1);\
\}\
\
static void hci_send_event_cmd_complete(int fd, uint16_t opcode, void* data, size_t data_len)\
\{\
	struct iovec iv[4];\
	struct hci_event_hdr hdr;\
	hdr.evt = HCI_EV_CMD_COMPLETE;\
	hdr.plen = sizeof(struct hci_ev_cmd_complete) + data_len;\
	struct hci_ev_cmd_complete evt_hdr;\
	evt_hdr.ncmd = 1;\
	evt_hdr.opcode = opcode;\
	uint8_t type = HCI_EVENT_PKT;\
	iv[0].iov_base = &type;\
	iv[0].iov_len = sizeof(type);\
	iv[1].iov_base = &hdr;\
	iv[1].iov_len = sizeof(hdr);\
	iv[2].iov_base = &evt_hdr;\
	iv[2].iov_len = sizeof(evt_hdr);\
	iv[3].iov_base = data;\
	iv[3].iov_len = data_len;\
	if (writev(fd, iv, sizeof(iv) / sizeof(struct iovec)) < 0)\
	exit(1);\
\}\
\
static bool process_command_pkt(int fd, char* buf, ssize_t buf_size)\
\{\
	struct hci_command_hdr* hdr = (struct hci_command_hdr*)buf;\
	if (buf_size < (ssize_t)sizeof(struct hci_command_hdr) ||\
	    hdr->plen != buf_size - sizeof(struct hci_command_hdr))\
	exit(1);\
	switch (hdr->opcode) \{\
	case HCI_OP_WRITE_SCAN_ENABLE: \{\
		uint8_t status = 0;\
		hci_send_event_cmd_complete(fd, hdr->opcode, &status, sizeof(status));\
		return true;\
	\}\
	case HCI_OP_READ_BD_ADDR: \{\
		struct hci_rp_read_bd_addr rp = \{0\};\
		rp.status = 0;\
		memset(&rp.bdaddr, 0xaa, 6);\
		hci_send_event_cmd_complete(fd, hdr->opcode, &rp, sizeof(rp));\
		return false;\
	\}\
	case HCI_OP_READ_BUFFER_SIZE: \{\
		struct hci_rp_read_buffer_size rp = \{0\};\
		rp.status = 0;\
		rp.acl_mtu = 1021;\
		rp.sco_mtu = 96;\
		rp.acl_max_pkt = 4;\
		rp.sco_max_pkt = 6;\
		hci_send_event_cmd_complete(fd, hdr->opcode, &rp, sizeof(rp));\
		return false;\
	\}\
	\}\
	char dummy[0xf9] = \{0\};\
	hci_send_event_cmd_complete(fd, hdr->opcode, dummy, sizeof(dummy));\
	return false;\
\}\
\
static void* event_thread(void* arg)\
\{\
	while (1) \{\
		char buf[1024] = \{0\};\
		ssize_t buf_size = read(vhci_fd, buf, sizeof(buf));\
		if (buf_size < 0)\
	exit(1);\
		if (buf_size > 0 && buf[0] == HCI_COMMAND_PKT) \{\
			if (process_command_pkt(vhci_fd, buf + 1, buf_size - 1))\
				break;\
		\}\
	\}\
	return NULL;\
\}\
#define HCI_HANDLE_1 200\
#define HCI_HANDLE_2 201\
\
static void initialize_vhci()\
\{\
	int hci_sock = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);\
	if (hci_sock < 0)\
	exit(1);\
	vhci_fd = open("/dev/vhci", O_RDWR);\
	if (vhci_fd == -1)\
	exit(1);\
	const int kVhciFd = 202;\
	if (dup2(vhci_fd, kVhciFd) < 0)\
	exit(1);\
	close(vhci_fd);\
	vhci_fd = kVhciFd;\
	struct vhci_vendor_pkt vendor_pkt;\
	if (read(vhci_fd, &vendor_pkt, sizeof(vendor_pkt)) != sizeof(vendor_pkt))\
	exit(1);\
	if (vendor_pkt.type != HCI_VENDOR_PKT)\
	exit(1);\
	pthread_t th;\
	if (pthread_create(&th, NULL, event_thread, NULL))\
	exit(1);\
	int ret = ioctl(hci_sock, HCIDEVUP, vendor_pkt.id);\
	if (ret) \{\
		if (errno == ERFKILL) \{\
			rfkill_unblock_all();\
			ret = ioctl(hci_sock, HCIDEVUP, vendor_pkt.id);\
		\}\
		if (ret && errno != EALREADY)\
	exit(1);\
	\}\
	struct hci_dev_req dr = \{0\};\
	dr.dev_id = vendor_pkt.id;\
	dr.dev_opt = SCAN_PAGE;\
	if (ioctl(hci_sock, HCISETSCAN, &dr))\
	exit(1);\
	struct hci_ev_conn_request request;\
	memset(&request, 0, sizeof(request));\
	memset(&request.bdaddr, 0xaa, 6);\
	*(uint8_t*)&request.bdaddr.b[5] = 0x10;\
	request.link_type = ACL_LINK;\
	hci_send_event_packet(vhci_fd, HCI_EV_CONN_REQUEST, &request, sizeof(request));\
	struct hci_ev_conn_complete complete;\
	memset(&complete, 0, sizeof(complete));\
	complete.status = 0;\
	complete.handle = HCI_HANDLE_1;\
	memset(&complete.bdaddr, 0xaa, 6);\
	*(uint8_t*)&complete.bdaddr.b[5] = 0x10;\
	complete.link_type = ACL_LINK;\
	complete.encr_mode = 0;\
	hci_send_event_packet(vhci_fd, HCI_EV_CONN_COMPLETE, &complete, sizeof(complete));\
	struct hci_ev_remote_features features;\
	memset(&features, 0, sizeof(features));\
	features.status = 0;\
	features.handle = HCI_HANDLE_1;\
	hci_send_event_packet(vhci_fd, HCI_EV_REMOTE_FEATURES, &features, sizeof(features));\
	struct \{\
		struct hci_ev_le_meta le_meta;\
		struct hci_ev_le_conn_complete le_conn;\
	\} le_conn;\
	memset(&le_conn, 0, sizeof(le_conn));\
	le_conn.le_meta.subevent = HCI_EV_LE_CONN_COMPLETE;\
	memset(&le_conn.le_conn.bdaddr, 0xaa, 6);\
	*(uint8_t*)&le_conn.le_conn.bdaddr.b[5] = 0x11;\
	le_conn.le_conn.role = 1;\
	le_conn.le_conn.handle = HCI_HANDLE_2;\
	hci_send_event_packet(vhci_fd, HCI_EV_LE_META, &le_conn, sizeof(le_conn));\
	pthread_join(th, NULL);\
	close(hci_sock);\
\}\
\
static void setup_common()\
\{\
	if (mount(0, "/sys/fs/fuse/connections", "fusectl", 0, 0)) \{\
	\}\
\}\
\
static void setup_binderfs()\
\{\
	if (mkdir("/dev/binderfs", 0777)) \{\
	\}\
	if (mount("binder", "/dev/binderfs", "binder", 0, NULL)) \{\
	\}\
	if (symlink("/dev/binderfs", "./binderfs")) \{\
	\}\
\}\
\
static void loop();\
\
static void sandbox_common()\
\{\
	prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);\
	setsid();\
	struct rlimit rlim;\
	rlim.rlim_cur = rlim.rlim_max = (200 << 20);\
	setrlimit(RLIMIT_AS, &rlim);\
	rlim.rlim_cur = rlim.rlim_max = 32 << 20;\
	setrlimit(RLIMIT_MEMLOCK, &rlim);\
	rlim.rlim_cur = rlim.rlim_max = 136 << 20;\
	setrlimit(RLIMIT_FSIZE, &rlim);\
	rlim.rlim_cur = rlim.rlim_max = 1 << 20;\
	setrlimit(RLIMIT_STACK, &rlim);\
	rlim.rlim_cur = rlim.rlim_max = 0;\
	setrlimit(RLIMIT_CORE, &rlim);\
	rlim.rlim_cur = rlim.rlim_max = 256;\
	setrlimit(RLIMIT_NOFILE, &rlim);\
	if (unshare(CLONE_NEWNS)) \{\
	\}\
	if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL)) \{\
	\}\
	if (unshare(CLONE_NEWIPC)) \{\
	\}\
	if (unshare(0x02000000)) \{\
	\}\
	if (unshare(CLONE_NEWUTS)) \{\
	\}\
	if (unshare(CLONE_SYSVSEM)) \{\
	\}\
	typedef struct \{\
		const char* name;\
		const char* value;\
	\} sysctl_t;\
	static const sysctl_t sysctls[] = \{\
	    \{"/proc/sys/kernel/shmmax", "16777216"\},\
	    \{"/proc/sys/kernel/shmall", "536870912"\},\
	    \{"/proc/sys/kernel/shmmni", "1024"\},\
	    \{"/proc/sys/kernel/msgmax", "8192"\},\
	    \{"/proc/sys/kernel/msgmni", "1024"\},\
	    \{"/proc/sys/kernel/msgmnb", "1024"\},\
	    \{"/proc/sys/kernel/sem", "1024 1048576 500 1024"\},\
	\};\
	unsigned i;\
	for (i = 0; i < sizeof(sysctls) / sizeof(sysctls[0]); i++)\
		write_file(sysctls[i].name, sysctls[i].value);\
\}\
\
static int wait_for_loop(int pid)\
\{\
	if (pid < 0)\
	exit(1);\
	int status = 0;\
	while (waitpid(-1, &status, __WALL) != pid) \{\
	\}\
	return WEXITSTATUS(status);\
\}\
\
static void drop_caps(void)\
\{\
	struct __user_cap_header_struct cap_hdr = \{\};\
	struct __user_cap_data_struct cap_data[2] = \{\};\
	cap_hdr.version = _LINUX_CAPABILITY_VERSION_3;\
	cap_hdr.pid = getpid();\
	if (syscall(SYS_capget, &cap_hdr, &cap_data))\
	exit(1);\
	const int drop = (1 << CAP_SYS_PTRACE) | (1 << CAP_SYS_NICE);\
	cap_data[0].effective &= ~drop;\
	cap_data[0].permitted &= ~drop;\
	cap_data[0].inheritable &= ~drop;\
	if (syscall(SYS_capset, &cap_hdr, &cap_data))\
	exit(1);\
\}\
\
static int do_sandbox_none(void)\
\{\
	if (unshare(CLONE_NEWPID)) \{\
	\}\
	int pid = fork();\
	if (pid != 0)\
		return wait_for_loop(pid);\
	setup_common();\
	initialize_vhci();\
	sandbox_common();\
	drop_caps();\
	initialize_netdevices_init();\
	if (unshare(CLONE_NEWNET)) \{\
	\}\
	initialize_netdevices();\
	setup_binderfs();\
	loop();\
	exit(1);\
\}\
\
static void close_fds()\
\{\
	for (int fd = 3; fd < MAX_FDS; fd++)\
		close(fd);\
\}\
\
static void setup_binfmt_misc()\
\{\
	if (mount(0, "/proc/sys/fs/binfmt_misc", "binfmt_misc", 0, 0)) \{\
	\}\
	write_file("/proc/sys/fs/binfmt_misc/register", ":syz0:M:0:\\x01::./file0:");\
	write_file("/proc/sys/fs/binfmt_misc/register", ":syz1:M:1:\\x02::./file0:POC");\
\}\
\
static void setup_sysctl()\
\{\
	char mypid[32];\
	snprintf(mypid, sizeof(mypid), "%d", getpid());\
	struct \{\
		const char* name;\
		const char* data;\
	\} files[] = \{\
		\{"/sys/kernel/debug/x86/nmi_longest_ns", "10000000000"\},\
		\{"/proc/sys/kernel/hung_task_check_interval_secs", "20"\},\
		\{"/proc/sys/net/core/bpf_jit_kallsyms", "1"\},\
		\{"/proc/sys/net/core/bpf_jit_harden", "0"\},\
		\{"/proc/sys/kernel/kptr_restrict", "0"\},\
		\{"/proc/sys/kernel/softlockup_all_cpu_backtrace", "1"\},\
		\{"/proc/sys/fs/mount-max", "100"\},\
		\{"/proc/sys/vm/oom_dump_tasks", "0"\},\
		\{"/proc/sys/debug/exception-trace", "0"\},\
		\{"/proc/sys/kernel/printk", "7 4 1 3"\},\
		\{"/proc/sys/net/ipv4/ping_group_range", "0 65535"\},\
		\{"/proc/sys/kernel/keys/gc_delay", "1"\},\
		\{"/proc/sys/vm/oom_kill_allocating_task", "1"\},\
		\{"/proc/sys/kernel/ctrl-alt-del", "0"\},\
		\{"/proc/sys/kernel/cad_pid", mypid\},\
	\};\
	for (size_t i = 0; i < sizeof(files) / sizeof(files[0]); i++) \{\
		if (!write_file(files[i].name, files[i].data))\
			printf("write to %s failed: %s\\n", files[i].name, strerror(errno));\
	\}\
\}\
\
uint64_t r[1] = \{0xffffffffffffffff\};\
\
void loop(void)\
\{\
		intptr_t res = 0;\
	res = syscall(__NR_socket, 2ul, 2ul, 0x73);\
	if (res != -1)\
		r[0] = res;\
memcpy((void*)0x20000040, "filter\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000", 32);\
*(uint32_t*)0x20000060 = 6;\
*(uint32_t*)0x20000064 = 0;\
*(uint32_t*)0x20000068 = 0x90;\
*(uint64_t*)0x20000070 = 0;\
*(uint64_t*)0x20000078 = 0x20000400;\
*(uint64_t*)0x20000080 = 0x20000430;\
*(uint64_t*)0x20000088 = 0;\
*(uint64_t*)0x20000090 = 0;\
*(uint64_t*)0x20000098 = 0;\
*(uint32_t*)0x200000a0 = 0;\
*(uint64_t*)0x200000a8 = 0;\
*(uint64_t*)0x200000b0 = 0x20000400;\
*(uint32_t*)0x20000400 = 0;\
memset((void*)0x20000404, 0, 32);\
*(uint32_t*)0x20000424 = 0;\
*(uint32_t*)0x20000428 = -1;\
*(uint32_t*)0x2000042c = 0;\
*(uint32_t*)0x20000430 = 0;\
memset((void*)0x20000434, 0, 32);\
*(uint32_t*)0x20000454 = 0;\
*(uint32_t*)0x20000458 = -1;\
*(uint32_t*)0x2000045c = 0;\
*(uint32_t*)0x20000460 = 0;\
memset((void*)0x20000464, 0, 32);\
*(uint32_t*)0x20000484 = 0;\
*(uint32_t*)0x20000488 = 0xfffffffc;\
*(uint32_t*)0x2000048c = 0;\
	syscall(__NR_setsockopt, r[0], 0, 0x80, 0x20000040ul, 0x108ul);\
	close_fds();\
\}\
int main(void)\
\{\
		syscall(__NR_mmap, 0x1ffff000ul, 0x1000ul, 0ul, 0x32ul, -1, 0ul);\
	syscall(__NR_mmap, 0x20000000ul, 0x1000000ul, 7ul, 0x32ul, -1, 0ul);\
	syscall(__NR_mmap, 0x21000000ul, 0x1000ul, 0ul, 0x32ul, -1, 0ul);\
	setup_sysctl();\
	setup_binfmt_misc();\
			do_sandbox_none();\
	return 0;\
\}\
}

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [Bridge] [External] : Re: [PATCH] netfilter: ebtables: fix a NULL pointer dereference in ebt_do_table()
@ 2022-08-20 19:18     ` Harshit Mogalapalli
  0 siblings, 0 replies; 17+ messages in thread
From: Harshit Mogalapalli @ 2022-08-20 19:18 UTC (permalink / raw)
  To: Florian Westphal
  Cc: john.p.donnelly, vegard.nossum, coreteam, netdev,
	Nikolay Aleksandrov, bridge, linux-kernel, Jozsef Kadlecsik,
	george.kennedy, Eric Dumazet, syzkaller, netfilter-devel,
	Roopa Prabhu, Jakub Kicinski, Paolo Abeni, David S. Miller,
	Pablo Neira Ayuso

[-- Attachment #1: Type: text/plain, Size: 715 bytes --]

On 20/08/22 9:56 pm, Florian Westphal wrote:
> Harshit Mogalapalli <harshit.m.mogalapalli@oracle.com> wrote:
>> In ebt_do_table() function dereferencing 'private->hook_entry[hook]'
>> can lead to NULL pointer dereference. So add a check to prevent that.
> 
Hi Florian,

Thanks a lot for checking the patch.

> This looks incorrect, i.e. paperimg over the problem.
> 

Okay.

> If hook_entry[hook] is NULL, how did this make it to the eval loop?
>

When I run the reproducer and have 'private->hook_entry[hook]' printed, 
it was NULL, so thought of adding a NULL check to prevent the NULL 
dereference.

Attaching the C reproducer.

Regards,
Harshit


> I guess ebtables lacks a sanity check on incoming ruleset?
> 

[-- Attachment #2: r.c --]
[-- Type: text/plain, Size: 44610 bytes --]

{\rtf1\ansi\ansicpg1252\cocoartf2639
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fmodern\fcharset0 Courier;}
{\colortbl;\red255\green255\blue255;\red0\green0\blue0;}
{\*\expandedcolortbl;;\cssrgb\c0\c0\c0;}
\paperw11900\paperh16840\margl1440\margr1440\vieww11520\viewh8400\viewkind0
\deftab720
\pard\pardeftab720\partightenfactor0

\f0\fs26 \cf0 \expnd0\expndtw0\kerning0
\outl0\strokewidth0 \strokec2 #define _GNU_SOURCE \
\
#include <arpa/inet.h>\
#include <endian.h>\
#include <errno.h>\
#include <fcntl.h>\
#include <net/if.h>\
#include <net/if_arp.h>\
#include <netinet/in.h>\
#include <pthread.h>\
#include <sched.h>\
#include <stdarg.h>\
#include <stdbool.h>\
#include <stdint.h>\
#include <stdio.h>\
#include <stdlib.h>\
#include <string.h>\
#include <sys/epoll.h>\
#include <sys/ioctl.h>\
#include <sys/mount.h>\
#include <sys/prctl.h>\
#include <sys/resource.h>\
#include <sys/socket.h>\
#include <sys/stat.h>\
#include <sys/syscall.h>\
#include <sys/time.h>\
#include <sys/types.h>\
#include <sys/uio.h>\
#include <sys/wait.h>\
#include <unistd.h>\
\
#include <linux/capability.h>\
#include <linux/genetlink.h>\
#include <linux/if_addr.h>\
#include <linux/if_ether.h>\
#include <linux/if_link.h>\
#include <linux/if_tun.h>\
#include <linux/in6.h>\
#include <linux/ip.h>\
#include <linux/neighbour.h>\
#include <linux/net.h>\
#include <linux/netlink.h>\
#include <linux/rfkill.h>\
#include <linux/rtnetlink.h>\
#include <linux/tcp.h>\
#include <linux/veth.h>\
\
static unsigned long long procid;\
\
static bool write_file(const char* file, const char* what, ...)\
\{\
	char buf[1024];\
	va_list args;\
	va_start(args, what);\
	vsnprintf(buf, sizeof(buf), what, args);\
	va_end(args);\
	buf[sizeof(buf) - 1] = 0;\
	int len = strlen(buf);\
	int fd = open(file, O_WRONLY | O_CLOEXEC);\
	if (fd == -1)\
		return false;\
	if (write(fd, buf, len) != len) \{\
		int err = errno;\
		close(fd);\
		errno = err;\
		return false;\
	\}\
	close(fd);\
	return true;\
\}\
\
struct nlmsg \{\
	char* pos;\
	int nesting;\
	struct nlattr* nested[8];\
	char buf[4096];\
\};\
\
static void netlink_init(struct nlmsg* nlmsg, int typ, int flags,\
			 const void* data, int size)\
\{\
	memset(nlmsg, 0, sizeof(*nlmsg));\
	struct nlmsghdr* hdr = (struct nlmsghdr*)nlmsg->buf;\
	hdr->nlmsg_type = typ;\
	hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags;\
	memcpy(hdr + 1, data, size);\
	nlmsg->pos = (char*)(hdr + 1) + NLMSG_ALIGN(size);\
\}\
\
static void netlink_attr(struct nlmsg* nlmsg, int typ,\
			 const void* data, int size)\
\{\
	struct nlattr* attr = (struct nlattr*)nlmsg->pos;\
	attr->nla_len = sizeof(*attr) + size;\
	attr->nla_type = typ;\
	if (size > 0)\
		memcpy(attr + 1, data, size);\
	nlmsg->pos += NLMSG_ALIGN(attr->nla_len);\
\}\
\
static void netlink_nest(struct nlmsg* nlmsg, int typ)\
\{\
	struct nlattr* attr = (struct nlattr*)nlmsg->pos;\
	attr->nla_type = typ;\
	nlmsg->pos += sizeof(*attr);\
	nlmsg->nested[nlmsg->nesting++] = attr;\
\}\
\
static void netlink_done(struct nlmsg* nlmsg)\
\{\
	struct nlattr* attr = nlmsg->nested[--nlmsg->nesting];\
	attr->nla_len = nlmsg->pos - (char*)attr;\
\}\
\
static int netlink_send_ext(struct nlmsg* nlmsg, int sock,\
			    uint16_t reply_type, int* reply_len, bool dofail)\
\{\
	if (nlmsg->pos > nlmsg->buf + sizeof(nlmsg->buf) || nlmsg->nesting)\
	exit(1);\
	struct nlmsghdr* hdr = (struct nlmsghdr*)nlmsg->buf;\
	hdr->nlmsg_len = nlmsg->pos - nlmsg->buf;\
	struct sockaddr_nl addr;\
	memset(&addr, 0, sizeof(addr));\
	addr.nl_family = AF_NETLINK;\
	ssize_t n = sendto(sock, nlmsg->buf, hdr->nlmsg_len, 0, (struct sockaddr*)&addr, sizeof(addr));\
	if (n != (ssize_t)hdr->nlmsg_len) \{\
		if (dofail)\
	exit(1);\
		return -1;\
	\}\
	n = recv(sock, nlmsg->buf, sizeof(nlmsg->buf), 0);\
	if (reply_len)\
		*reply_len = 0;\
	if (n < 0) \{\
		if (dofail)\
	exit(1);\
		return -1;\
	\}\
	if (n < (ssize_t)sizeof(struct nlmsghdr)) \{\
		errno = EINVAL;\
		if (dofail)\
	exit(1);\
		return -1;\
	\}\
	if (hdr->nlmsg_type == NLMSG_DONE)\
		return 0;\
	if (reply_len && hdr->nlmsg_type == reply_type) \{\
		*reply_len = n;\
		return 0;\
	\}\
	if (n < (ssize_t)(sizeof(struct nlmsghdr) + sizeof(struct nlmsgerr))) \{\
		errno = EINVAL;\
		if (dofail)\
	exit(1);\
		return -1;\
	\}\
	if (hdr->nlmsg_type != NLMSG_ERROR) \{\
		errno = EINVAL;\
		if (dofail)\
	exit(1);\
		return -1;\
	\}\
	errno = -((struct nlmsgerr*)(hdr + 1))->error;\
	return -errno;\
\}\
\
static int netlink_send(struct nlmsg* nlmsg, int sock)\
\{\
	return netlink_send_ext(nlmsg, sock, 0, NULL, true);\
\}\
\
static int netlink_query_family_id(struct nlmsg* nlmsg, int sock, const char* family_name, bool dofail)\
\{\
	struct genlmsghdr genlhdr;\
	memset(&genlhdr, 0, sizeof(genlhdr));\
	genlhdr.cmd = CTRL_CMD_GETFAMILY;\
	netlink_init(nlmsg, GENL_ID_CTRL, 0, &genlhdr, sizeof(genlhdr));\
	netlink_attr(nlmsg, CTRL_ATTR_FAMILY_NAME, family_name, strnlen(family_name, GENL_NAMSIZ - 1) + 1);\
	int n = 0;\
	int err = netlink_send_ext(nlmsg, sock, GENL_ID_CTRL, &n, dofail);\
	if (err < 0) \{\
		return -1;\
	\}\
	uint16_t id = 0;\
	struct nlattr* attr = (struct nlattr*)(nlmsg->buf + NLMSG_HDRLEN + NLMSG_ALIGN(sizeof(genlhdr)));\
	for (; (char*)attr < nlmsg->buf + n; attr = (struct nlattr*)((char*)attr + NLMSG_ALIGN(attr->nla_len))) \{\
		if (attr->nla_type == CTRL_ATTR_FAMILY_ID) \{\
			id = *(uint16_t*)(attr + 1);\
			break;\
		\}\
	\}\
	if (!id) \{\
		errno = EINVAL;\
		return -1;\
	\}\
	recv(sock, nlmsg->buf, sizeof(nlmsg->buf), 0);\
	return id;\
\}\
\
static int netlink_next_msg(struct nlmsg* nlmsg, unsigned int offset,\
			    unsigned int total_len)\
\{\
	struct nlmsghdr* hdr = (struct nlmsghdr*)(nlmsg->buf + offset);\
	if (offset == total_len || offset + hdr->nlmsg_len > total_len)\
		return -1;\
	return hdr->nlmsg_len;\
\}\
\
static void netlink_add_device_impl(struct nlmsg* nlmsg, const char* type,\
				    const char* name)\
\{\
	struct ifinfomsg hdr;\
	memset(&hdr, 0, sizeof(hdr));\
	netlink_init(nlmsg, RTM_NEWLINK, NLM_F_EXCL | NLM_F_CREATE, &hdr, sizeof(hdr));\
	if (name)\
		netlink_attr(nlmsg, IFLA_IFNAME, name, strlen(name));\
	netlink_nest(nlmsg, IFLA_LINKINFO);\
	netlink_attr(nlmsg, IFLA_INFO_KIND, type, strlen(type));\
\}\
\
static void netlink_add_device(struct nlmsg* nlmsg, int sock, const char* type,\
			       const char* name)\
\{\
	netlink_add_device_impl(nlmsg, type, name);\
	netlink_done(nlmsg);\
	int err = netlink_send(nlmsg, sock);\
	if (err < 0) \{\
	\}\
\}\
\
static void netlink_add_veth(struct nlmsg* nlmsg, int sock, const char* name,\
			     const char* peer)\
\{\
	netlink_add_device_impl(nlmsg, "veth", name);\
	netlink_nest(nlmsg, IFLA_INFO_DATA);\
	netlink_nest(nlmsg, VETH_INFO_PEER);\
	nlmsg->pos += sizeof(struct ifinfomsg);\
	netlink_attr(nlmsg, IFLA_IFNAME, peer, strlen(peer));\
	netlink_done(nlmsg);\
	netlink_done(nlmsg);\
	netlink_done(nlmsg);\
	int err = netlink_send(nlmsg, sock);\
	if (err < 0) \{\
	\}\
\}\
\
static void netlink_add_hsr(struct nlmsg* nlmsg, int sock, const char* name,\
			    const char* slave1, const char* slave2)\
\{\
	netlink_add_device_impl(nlmsg, "hsr", name);\
	netlink_nest(nlmsg, IFLA_INFO_DATA);\
	int ifindex1 = if_nametoindex(slave1);\
	netlink_attr(nlmsg, IFLA_HSR_SLAVE1, &ifindex1, sizeof(ifindex1));\
	int ifindex2 = if_nametoindex(slave2);\
	netlink_attr(nlmsg, IFLA_HSR_SLAVE2, &ifindex2, sizeof(ifindex2));\
	netlink_done(nlmsg);\
	netlink_done(nlmsg);\
	int err = netlink_send(nlmsg, sock);\
	if (err < 0) \{\
	\}\
\}\
\
static void netlink_add_linked(struct nlmsg* nlmsg, int sock, const char* type, const char* name, const char* link)\
\{\
	netlink_add_device_impl(nlmsg, type, name);\
	netlink_done(nlmsg);\
	int ifindex = if_nametoindex(link);\
	netlink_attr(nlmsg, IFLA_LINK, &ifindex, sizeof(ifindex));\
	int err = netlink_send(nlmsg, sock);\
	if (err < 0) \{\
	\}\
\}\
\
static void netlink_add_vlan(struct nlmsg* nlmsg, int sock, const char* name, const char* link, uint16_t id, uint16_t proto)\
\{\
	netlink_add_device_impl(nlmsg, "vlan", name);\
	netlink_nest(nlmsg, IFLA_INFO_DATA);\
	netlink_attr(nlmsg, IFLA_VLAN_ID, &id, sizeof(id));\
	netlink_attr(nlmsg, IFLA_VLAN_PROTOCOL, &proto, sizeof(proto));\
	netlink_done(nlmsg);\
	netlink_done(nlmsg);\
	int ifindex = if_nametoindex(link);\
	netlink_attr(nlmsg, IFLA_LINK, &ifindex, sizeof(ifindex));\
	int err = netlink_send(nlmsg, sock);\
	if (err < 0) \{\
	\}\
\}\
\
static void netlink_add_macvlan(struct nlmsg* nlmsg, int sock, const char* name, const char* link)\
\{\
	netlink_add_device_impl(nlmsg, "macvlan", name);\
	netlink_nest(nlmsg, IFLA_INFO_DATA);\
	uint32_t mode = MACVLAN_MODE_BRIDGE;\
	netlink_attr(nlmsg, IFLA_MACVLAN_MODE, &mode, sizeof(mode));\
	netlink_done(nlmsg);\
	netlink_done(nlmsg);\
	int ifindex = if_nametoindex(link);\
	netlink_attr(nlmsg, IFLA_LINK, &ifindex, sizeof(ifindex));\
	int err = netlink_send(nlmsg, sock);\
	if (err < 0) \{\
	\}\
\}\
\
static void netlink_add_geneve(struct nlmsg* nlmsg, int sock, const char* name, uint32_t vni, struct in_addr* addr4, struct in6_addr* addr6)\
\{\
	netlink_add_device_impl(nlmsg, "geneve", name);\
	netlink_nest(nlmsg, IFLA_INFO_DATA);\
	netlink_attr(nlmsg, IFLA_GENEVE_ID, &vni, sizeof(vni));\
	if (addr4)\
		netlink_attr(nlmsg, IFLA_GENEVE_REMOTE, addr4, sizeof(*addr4));\
	if (addr6)\
		netlink_attr(nlmsg, IFLA_GENEVE_REMOTE6, addr6, sizeof(*addr6));\
	netlink_done(nlmsg);\
	netlink_done(nlmsg);\
	int err = netlink_send(nlmsg, sock);\
	if (err < 0) \{\
	\}\
\}\
\
#define IFLA_IPVLAN_FLAGS 2\
#define IPVLAN_MODE_L3S 2\
#undef IPVLAN_F_VEPA\
#define IPVLAN_F_VEPA 2\
\
static void netlink_add_ipvlan(struct nlmsg* nlmsg, int sock, const char* name, const char* link, uint16_t mode, uint16_t flags)\
\{\
	netlink_add_device_impl(nlmsg, "ipvlan", name);\
	netlink_nest(nlmsg, IFLA_INFO_DATA);\
	netlink_attr(nlmsg, IFLA_IPVLAN_MODE, &mode, sizeof(mode));\
	netlink_attr(nlmsg, IFLA_IPVLAN_FLAGS, &flags, sizeof(flags));\
	netlink_done(nlmsg);\
	netlink_done(nlmsg);\
	int ifindex = if_nametoindex(link);\
	netlink_attr(nlmsg, IFLA_LINK, &ifindex, sizeof(ifindex));\
	int err = netlink_send(nlmsg, sock);\
	if (err < 0) \{\
	\}\
\}\
\
static void netlink_device_change(struct nlmsg* nlmsg, int sock, const char* name, bool up,\
				  const char* master, const void* mac, int macsize,\
				  const char* new_name)\
\{\
	struct ifinfomsg hdr;\
	memset(&hdr, 0, sizeof(hdr));\
	if (up)\
		hdr.ifi_flags = hdr.ifi_change = IFF_UP;\
	hdr.ifi_index = if_nametoindex(name);\
	netlink_init(nlmsg, RTM_NEWLINK, 0, &hdr, sizeof(hdr));\
	if (new_name)\
		netlink_attr(nlmsg, IFLA_IFNAME, new_name, strlen(new_name));\
	if (master) \{\
		int ifindex = if_nametoindex(master);\
		netlink_attr(nlmsg, IFLA_MASTER, &ifindex, sizeof(ifindex));\
	\}\
	if (macsize)\
		netlink_attr(nlmsg, IFLA_ADDRESS, mac, macsize);\
	int err = netlink_send(nlmsg, sock);\
	if (err < 0) \{\
	\}\
\}\
\
static int netlink_add_addr(struct nlmsg* nlmsg, int sock, const char* dev,\
			    const void* addr, int addrsize)\
\{\
	struct ifaddrmsg hdr;\
	memset(&hdr, 0, sizeof(hdr));\
	hdr.ifa_family = addrsize == 4 ? AF_INET : AF_INET6;\
	hdr.ifa_prefixlen = addrsize == 4 ? 24 : 120;\
	hdr.ifa_scope = RT_SCOPE_UNIVERSE;\
	hdr.ifa_index = if_nametoindex(dev);\
	netlink_init(nlmsg, RTM_NEWADDR, NLM_F_CREATE | NLM_F_REPLACE, &hdr, sizeof(hdr));\
	netlink_attr(nlmsg, IFA_LOCAL, addr, addrsize);\
	netlink_attr(nlmsg, IFA_ADDRESS, addr, addrsize);\
	return netlink_send(nlmsg, sock);\
\}\
\
static void netlink_add_addr4(struct nlmsg* nlmsg, int sock,\
			      const char* dev, const char* addr)\
\{\
	struct in_addr in_addr;\
	inet_pton(AF_INET, addr, &in_addr);\
	int err = netlink_add_addr(nlmsg, sock, dev, &in_addr, sizeof(in_addr));\
	if (err < 0) \{\
	\}\
\}\
\
static void netlink_add_addr6(struct nlmsg* nlmsg, int sock,\
			      const char* dev, const char* addr)\
\{\
	struct in6_addr in6_addr;\
	inet_pton(AF_INET6, addr, &in6_addr);\
	int err = netlink_add_addr(nlmsg, sock, dev, &in6_addr, sizeof(in6_addr));\
	if (err < 0) \{\
	\}\
\}\
\
static struct nlmsg nlmsg;\
\
#define DEVLINK_FAMILY_NAME "devlink"\
\
#define DEVLINK_CMD_PORT_GET 5\
#define DEVLINK_ATTR_BUS_NAME 1\
#define DEVLINK_ATTR_DEV_NAME 2\
#define DEVLINK_ATTR_NETDEV_NAME 7\
\
static struct nlmsg nlmsg2;\
\
static void initialize_devlink_ports(const char* bus_name, const char* dev_name,\
				     const char* netdev_prefix)\
\{\
	struct genlmsghdr genlhdr;\
	int len, total_len, id, err, offset;\
	uint16_t netdev_index;\
	int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);\
	if (sock == -1)\
	exit(1);\
	int rtsock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);\
	if (rtsock == -1)\
	exit(1);\
	id = netlink_query_family_id(&nlmsg, sock, DEVLINK_FAMILY_NAME, true);\
	if (id == -1)\
		goto error;\
	memset(&genlhdr, 0, sizeof(genlhdr));\
	genlhdr.cmd = DEVLINK_CMD_PORT_GET;\
	netlink_init(&nlmsg, id, NLM_F_DUMP, &genlhdr, sizeof(genlhdr));\
	netlink_attr(&nlmsg, DEVLINK_ATTR_BUS_NAME, bus_name, strlen(bus_name) + 1);\
	netlink_attr(&nlmsg, DEVLINK_ATTR_DEV_NAME, dev_name, strlen(dev_name) + 1);\
	err = netlink_send_ext(&nlmsg, sock, id, &total_len, true);\
	if (err < 0) \{\
		goto error;\
	\}\
	offset = 0;\
	netdev_index = 0;\
	while ((len = netlink_next_msg(&nlmsg, offset, total_len)) != -1) \{\
		struct nlattr* attr = (struct nlattr*)(nlmsg.buf + offset + NLMSG_HDRLEN + NLMSG_ALIGN(sizeof(genlhdr)));\
		for (; (char*)attr < nlmsg.buf + offset + len; attr = (struct nlattr*)((char*)attr + NLMSG_ALIGN(attr->nla_len))) \{\
			if (attr->nla_type == DEVLINK_ATTR_NETDEV_NAME) \{\
				char* port_name;\
				char netdev_name[IFNAMSIZ];\
				port_name = (char*)(attr + 1);\
				snprintf(netdev_name, sizeof(netdev_name), "%s%d", netdev_prefix, netdev_index);\
				netlink_device_change(&nlmsg2, rtsock, port_name, true, 0, 0, 0, netdev_name);\
				break;\
			\}\
		\}\
		offset += len;\
		netdev_index++;\
	\}\
error:\
	close(rtsock);\
	close(sock);\
\}\
\
#define DEV_IPV4 "172.20.20.%d"\
#define DEV_IPV6 "fe80::%02x"\
#define DEV_MAC 0x00aaaaaaaaaa\
\
static void netdevsim_add(unsigned int addr, unsigned int port_count)\
\{\
	char buf[16];\
	sprintf(buf, "%u %u", addr, port_count);\
	if (write_file("/sys/bus/netdevsim/new_device", buf)) \{\
		snprintf(buf, sizeof(buf), "netdevsim%d", addr);\
		initialize_devlink_ports("netdevsim", buf, "netdevsim");\
	\}\
\}\
\
#define WG_GENL_NAME "wireguard"\
enum wg_cmd \{\
	WG_CMD_GET_DEVICE,\
	WG_CMD_SET_DEVICE,\
\};\
enum wgdevice_attribute \{\
	WGDEVICE_A_UNSPEC,\
	WGDEVICE_A_IFINDEX,\
	WGDEVICE_A_IFNAME,\
	WGDEVICE_A_PRIVATE_KEY,\
	WGDEVICE_A_PUBLIC_KEY,\
	WGDEVICE_A_FLAGS,\
	WGDEVICE_A_LISTEN_PORT,\
	WGDEVICE_A_FWMARK,\
	WGDEVICE_A_PEERS,\
\};\
enum wgpeer_attribute \{\
	WGPEER_A_UNSPEC,\
	WGPEER_A_PUBLIC_KEY,\
	WGPEER_A_PRESHARED_KEY,\
	WGPEER_A_FLAGS,\
	WGPEER_A_ENDPOINT,\
	WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL,\
	WGPEER_A_LAST_HANDSHAKE_TIME,\
	WGPEER_A_RX_BYTES,\
	WGPEER_A_TX_BYTES,\
	WGPEER_A_ALLOWEDIPS,\
	WGPEER_A_PROTOCOL_VERSION,\
\};\
enum wgallowedip_attribute \{\
	WGALLOWEDIP_A_UNSPEC,\
	WGALLOWEDIP_A_FAMILY,\
	WGALLOWEDIP_A_IPADDR,\
	WGALLOWEDIP_A_CIDR_MASK,\
\};\
\
static void netlink_wireguard_setup(void)\
\{\
	const char ifname_a[] = "wg0";\
	const char ifname_b[] = "wg1";\
	const char ifname_c[] = "wg2";\
	const char private_a[] = "\\xa0\\x5c\\xa8\\x4f\\x6c\\x9c\\x8e\\x38\\x53\\xe2\\xfd\\x7a\\x70\\xae\\x0f\\xb2\\x0f\\xa1\\x52\\x60\\x0c\\xb0\\x08\\x45\\x17\\x4f\\x08\\x07\\x6f\\x8d\\x78\\x43";\
	const char private_b[] = "\\xb0\\x80\\x73\\xe8\\xd4\\x4e\\x91\\xe3\\xda\\x92\\x2c\\x22\\x43\\x82\\x44\\xbb\\x88\\x5c\\x69\\xe2\\x69\\xc8\\xe9\\xd8\\x35\\xb1\\x14\\x29\\x3a\\x4d\\xdc\\x6e";\
	const char private_c[] = "\\xa0\\xcb\\x87\\x9a\\x47\\xf5\\xbc\\x64\\x4c\\x0e\\x69\\x3f\\xa6\\xd0\\x31\\xc7\\x4a\\x15\\x53\\xb6\\xe9\\x01\\xb9\\xff\\x2f\\x51\\x8c\\x78\\x04\\x2f\\xb5\\x42";\
	const char public_a[] = "\\x97\\x5c\\x9d\\x81\\xc9\\x83\\xc8\\x20\\x9e\\xe7\\x81\\x25\\x4b\\x89\\x9f\\x8e\\xd9\\x25\\xae\\x9f\\x09\\x23\\xc2\\x3c\\x62\\xf5\\x3c\\x57\\xcd\\xbf\\x69\\x1c";\
	const char public_b[] = "\\xd1\\x73\\x28\\x99\\xf6\\x11\\xcd\\x89\\x94\\x03\\x4d\\x7f\\x41\\x3d\\xc9\\x57\\x63\\x0e\\x54\\x93\\xc2\\x85\\xac\\xa4\\x00\\x65\\xcb\\x63\\x11\\xbe\\x69\\x6b";\
	const char public_c[] = "\\xf4\\x4d\\xa3\\x67\\xa8\\x8e\\xe6\\x56\\x4f\\x02\\x02\\x11\\x45\\x67\\x27\\x08\\x2f\\x5c\\xeb\\xee\\x8b\\x1b\\xf5\\xeb\\x73\\x37\\x34\\x1b\\x45\\x9b\\x39\\x22";\
	const uint16_t listen_a = 20001;\
	const uint16_t listen_b = 20002;\
	const uint16_t listen_c = 20003;\
	const uint16_t af_inet = AF_INET;\
	const uint16_t af_inet6 = AF_INET6;\
	const struct sockaddr_in endpoint_b_v4 = \{\
	    .sin_family = AF_INET,\
	    .sin_port = htons(listen_b),\
	    .sin_addr = \{htonl(INADDR_LOOPBACK)\}\};\
	const struct sockaddr_in endpoint_c_v4 = \{\
	    .sin_family = AF_INET,\
	    .sin_port = htons(listen_c),\
	    .sin_addr = \{htonl(INADDR_LOOPBACK)\}\};\
	struct sockaddr_in6 endpoint_a_v6 = \{\
	    .sin6_family = AF_INET6,\
	    .sin6_port = htons(listen_a)\};\
	endpoint_a_v6.sin6_addr = in6addr_loopback;\
	struct sockaddr_in6 endpoint_c_v6 = \{\
	    .sin6_family = AF_INET6,\
	    .sin6_port = htons(listen_c)\};\
	endpoint_c_v6.sin6_addr = in6addr_loopback;\
	const struct in_addr first_half_v4 = \{0\};\
	const struct in_addr second_half_v4 = \{(uint32_t)htonl(128 << 24)\};\
	const struct in6_addr first_half_v6 = \{\{\{0\}\}\};\
	const struct in6_addr second_half_v6 = \{\{\{0x80\}\}\};\
	const uint8_t half_cidr = 1;\
	const uint16_t persistent_keepalives[] = \{1, 3, 7, 9, 14, 19\};\
	struct genlmsghdr genlhdr = \{\
	    .cmd = WG_CMD_SET_DEVICE,\
	    .version = 1\};\
	int sock;\
	int id, err;\
	sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);\
	if (sock == -1) \{\
		return;\
	\}\
	id = netlink_query_family_id(&nlmsg, sock, WG_GENL_NAME, true);\
	if (id == -1)\
		goto error;\
	netlink_init(&nlmsg, id, 0, &genlhdr, sizeof(genlhdr));\
	netlink_attr(&nlmsg, WGDEVICE_A_IFNAME, ifname_a, strlen(ifname_a) + 1);\
	netlink_attr(&nlmsg, WGDEVICE_A_PRIVATE_KEY, private_a, 32);\
	netlink_attr(&nlmsg, WGDEVICE_A_LISTEN_PORT, &listen_a, 2);\
	netlink_nest(&nlmsg, NLA_F_NESTED | WGDEVICE_A_PEERS);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGPEER_A_PUBLIC_KEY, public_b, 32);\
	netlink_attr(&nlmsg, WGPEER_A_ENDPOINT, &endpoint_b_v4, sizeof(endpoint_b_v4));\
	netlink_attr(&nlmsg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, &persistent_keepalives[0], 2);\
	netlink_nest(&nlmsg, NLA_F_NESTED | WGPEER_A_ALLOWEDIPS);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet, 2);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &first_half_v4, sizeof(first_half_v4));\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1);\
	netlink_done(&nlmsg);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet6, 2);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &first_half_v6, sizeof(first_half_v6));\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1);\
	netlink_done(&nlmsg);\
	netlink_done(&nlmsg);\
	netlink_done(&nlmsg);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGPEER_A_PUBLIC_KEY, public_c, 32);\
	netlink_attr(&nlmsg, WGPEER_A_ENDPOINT, &endpoint_c_v6, sizeof(endpoint_c_v6));\
	netlink_attr(&nlmsg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, &persistent_keepalives[1], 2);\
	netlink_nest(&nlmsg, NLA_F_NESTED | WGPEER_A_ALLOWEDIPS);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet, 2);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &second_half_v4, sizeof(second_half_v4));\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1);\
	netlink_done(&nlmsg);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet6, 2);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &second_half_v6, sizeof(second_half_v6));\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1);\
	netlink_done(&nlmsg);\
	netlink_done(&nlmsg);\
	netlink_done(&nlmsg);\
	netlink_done(&nlmsg);\
	err = netlink_send(&nlmsg, sock);\
	if (err < 0) \{\
	\}\
	netlink_init(&nlmsg, id, 0, &genlhdr, sizeof(genlhdr));\
	netlink_attr(&nlmsg, WGDEVICE_A_IFNAME, ifname_b, strlen(ifname_b) + 1);\
	netlink_attr(&nlmsg, WGDEVICE_A_PRIVATE_KEY, private_b, 32);\
	netlink_attr(&nlmsg, WGDEVICE_A_LISTEN_PORT, &listen_b, 2);\
	netlink_nest(&nlmsg, NLA_F_NESTED | WGDEVICE_A_PEERS);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGPEER_A_PUBLIC_KEY, public_a, 32);\
	netlink_attr(&nlmsg, WGPEER_A_ENDPOINT, &endpoint_a_v6, sizeof(endpoint_a_v6));\
	netlink_attr(&nlmsg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, &persistent_keepalives[2], 2);\
	netlink_nest(&nlmsg, NLA_F_NESTED | WGPEER_A_ALLOWEDIPS);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet, 2);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &first_half_v4, sizeof(first_half_v4));\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1);\
	netlink_done(&nlmsg);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet6, 2);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &first_half_v6, sizeof(first_half_v6));\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1);\
	netlink_done(&nlmsg);\
	netlink_done(&nlmsg);\
	netlink_done(&nlmsg);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGPEER_A_PUBLIC_KEY, public_c, 32);\
	netlink_attr(&nlmsg, WGPEER_A_ENDPOINT, &endpoint_c_v4, sizeof(endpoint_c_v4));\
	netlink_attr(&nlmsg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, &persistent_keepalives[3], 2);\
	netlink_nest(&nlmsg, NLA_F_NESTED | WGPEER_A_ALLOWEDIPS);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet, 2);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &second_half_v4, sizeof(second_half_v4));\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1);\
	netlink_done(&nlmsg);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet6, 2);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &second_half_v6, sizeof(second_half_v6));\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1);\
	netlink_done(&nlmsg);\
	netlink_done(&nlmsg);\
	netlink_done(&nlmsg);\
	netlink_done(&nlmsg);\
	err = netlink_send(&nlmsg, sock);\
	if (err < 0) \{\
	\}\
	netlink_init(&nlmsg, id, 0, &genlhdr, sizeof(genlhdr));\
	netlink_attr(&nlmsg, WGDEVICE_A_IFNAME, ifname_c, strlen(ifname_c) + 1);\
	netlink_attr(&nlmsg, WGDEVICE_A_PRIVATE_KEY, private_c, 32);\
	netlink_attr(&nlmsg, WGDEVICE_A_LISTEN_PORT, &listen_c, 2);\
	netlink_nest(&nlmsg, NLA_F_NESTED | WGDEVICE_A_PEERS);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGPEER_A_PUBLIC_KEY, public_a, 32);\
	netlink_attr(&nlmsg, WGPEER_A_ENDPOINT, &endpoint_a_v6, sizeof(endpoint_a_v6));\
	netlink_attr(&nlmsg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, &persistent_keepalives[4], 2);\
	netlink_nest(&nlmsg, NLA_F_NESTED | WGPEER_A_ALLOWEDIPS);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet, 2);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &first_half_v4, sizeof(first_half_v4));\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1);\
	netlink_done(&nlmsg);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet6, 2);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &first_half_v6, sizeof(first_half_v6));\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1);\
	netlink_done(&nlmsg);\
	netlink_done(&nlmsg);\
	netlink_done(&nlmsg);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGPEER_A_PUBLIC_KEY, public_b, 32);\
	netlink_attr(&nlmsg, WGPEER_A_ENDPOINT, &endpoint_b_v4, sizeof(endpoint_b_v4));\
	netlink_attr(&nlmsg, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, &persistent_keepalives[5], 2);\
	netlink_nest(&nlmsg, NLA_F_NESTED | WGPEER_A_ALLOWEDIPS);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet, 2);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &second_half_v4, sizeof(second_half_v4));\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1);\
	netlink_done(&nlmsg);\
	netlink_nest(&nlmsg, NLA_F_NESTED | 0);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_FAMILY, &af_inet6, 2);\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_IPADDR, &second_half_v6, sizeof(second_half_v6));\
	netlink_attr(&nlmsg, WGALLOWEDIP_A_CIDR_MASK, &half_cidr, 1);\
	netlink_done(&nlmsg);\
	netlink_done(&nlmsg);\
	netlink_done(&nlmsg);\
	netlink_done(&nlmsg);\
	err = netlink_send(&nlmsg, sock);\
	if (err < 0) \{\
	\}\
\
error:\
	close(sock);\
\}\
static void initialize_netdevices(void)\
\{\
	char netdevsim[16];\
	sprintf(netdevsim, "netdevsim%d", (int)procid);\
	struct \{\
		const char* type;\
		const char* dev;\
	\} devtypes[] = \{\
	    \{"ip6gretap", "ip6gretap0"\},\
	    \{"bridge", "bridge0"\},\
	    \{"vcan", "vcan0"\},\
	    \{"bond", "bond0"\},\
	    \{"team", "team0"\},\
	    \{"dummy", "dummy0"\},\
	    \{"nlmon", "nlmon0"\},\
	    \{"caif", "caif0"\},\
	    \{"batadv", "batadv0"\},\
	    \{"vxcan", "vxcan1"\},\
	    \{"netdevsim", netdevsim\},\
	    \{"veth", 0\},\
	    \{"xfrm", "xfrm0"\},\
	    \{"wireguard", "wg0"\},\
	    \{"wireguard", "wg1"\},\
	    \{"wireguard", "wg2"\},\
	\};\
	const char* devmasters[] = \{"bridge", "bond", "team", "batadv"\};\
	struct \{\
		const char* name;\
		int macsize;\
		bool noipv6;\
	\} devices[] = \{\
	    \{"lo", ETH_ALEN\},\
	    \{"sit0", 0\},\
	    \{"bridge0", ETH_ALEN\},\
	    \{"vcan0", 0, true\},\
	    \{"tunl0", 0\},\
	    \{"gre0", 0\},\
	    \{"gretap0", ETH_ALEN\},\
	    \{"ip_vti0", 0\},\
	    \{"ip6_vti0", 0\},\
	    \{"ip6tnl0", 0\},\
	    \{"ip6gre0", 0\},\
	    \{"ip6gretap0", ETH_ALEN\},\
	    \{"erspan0", ETH_ALEN\},\
	    \{"bond0", ETH_ALEN\},\
	    \{"veth0", ETH_ALEN\},\
	    \{"veth1", ETH_ALEN\},\
	    \{"team0", ETH_ALEN\},\
	    \{"veth0_to_bridge", ETH_ALEN\},\
	    \{"veth1_to_bridge", ETH_ALEN\},\
	    \{"veth0_to_bond", ETH_ALEN\},\
	    \{"veth1_to_bond", ETH_ALEN\},\
	    \{"veth0_to_team", ETH_ALEN\},\
	    \{"veth1_to_team", ETH_ALEN\},\
	    \{"veth0_to_hsr", ETH_ALEN\},\
	    \{"veth1_to_hsr", ETH_ALEN\},\
	    \{"hsr0", 0\},\
	    \{"dummy0", ETH_ALEN\},\
	    \{"nlmon0", 0\},\
	    \{"vxcan0", 0, true\},\
	    \{"vxcan1", 0, true\},\
	    \{"caif0", ETH_ALEN\},\
	    \{"batadv0", ETH_ALEN\},\
	    \{netdevsim, ETH_ALEN\},\
	    \{"xfrm0", ETH_ALEN\},\
	    \{"veth0_virt_wifi", ETH_ALEN\},\
	    \{"veth1_virt_wifi", ETH_ALEN\},\
	    \{"virt_wifi0", ETH_ALEN\},\
	    \{"veth0_vlan", ETH_ALEN\},\
	    \{"veth1_vlan", ETH_ALEN\},\
	    \{"vlan0", ETH_ALEN\},\
	    \{"vlan1", ETH_ALEN\},\
	    \{"macvlan0", ETH_ALEN\},\
	    \{"macvlan1", ETH_ALEN\},\
	    \{"ipvlan0", ETH_ALEN\},\
	    \{"ipvlan1", ETH_ALEN\},\
	    \{"veth0_macvtap", ETH_ALEN\},\
	    \{"veth1_macvtap", ETH_ALEN\},\
	    \{"macvtap0", ETH_ALEN\},\
	    \{"macsec0", ETH_ALEN\},\
	    \{"veth0_to_batadv", ETH_ALEN\},\
	    \{"veth1_to_batadv", ETH_ALEN\},\
	    \{"batadv_slave_0", ETH_ALEN\},\
	    \{"batadv_slave_1", ETH_ALEN\},\
	    \{"geneve0", ETH_ALEN\},\
	    \{"geneve1", ETH_ALEN\},\
	    \{"wg0", 0\},\
	    \{"wg1", 0\},\
	    \{"wg2", 0\},\
	\};\
	int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);\
	if (sock == -1)\
	exit(1);\
	unsigned i;\
	for (i = 0; i < sizeof(devtypes) / sizeof(devtypes[0]); i++)\
		netlink_add_device(&nlmsg, sock, devtypes[i].type, devtypes[i].dev);\
	for (i = 0; i < sizeof(devmasters) / (sizeof(devmasters[0])); i++) \{\
		char master[32], slave0[32], veth0[32], slave1[32], veth1[32];\
		sprintf(slave0, "%s_slave_0", devmasters[i]);\
		sprintf(veth0, "veth0_to_%s", devmasters[i]);\
		netlink_add_veth(&nlmsg, sock, slave0, veth0);\
		sprintf(slave1, "%s_slave_1", devmasters[i]);\
		sprintf(veth1, "veth1_to_%s", devmasters[i]);\
		netlink_add_veth(&nlmsg, sock, slave1, veth1);\
		sprintf(master, "%s0", devmasters[i]);\
		netlink_device_change(&nlmsg, sock, slave0, false, master, 0, 0, NULL);\
		netlink_device_change(&nlmsg, sock, slave1, false, master, 0, 0, NULL);\
	\}\
	netlink_device_change(&nlmsg, sock, "bridge_slave_0", true, 0, 0, 0, NULL);\
	netlink_device_change(&nlmsg, sock, "bridge_slave_1", true, 0, 0, 0, NULL);\
	netlink_add_veth(&nlmsg, sock, "hsr_slave_0", "veth0_to_hsr");\
	netlink_add_veth(&nlmsg, sock, "hsr_slave_1", "veth1_to_hsr");\
	netlink_add_hsr(&nlmsg, sock, "hsr0", "hsr_slave_0", "hsr_slave_1");\
	netlink_device_change(&nlmsg, sock, "hsr_slave_0", true, 0, 0, 0, NULL);\
	netlink_device_change(&nlmsg, sock, "hsr_slave_1", true, 0, 0, 0, NULL);\
	netlink_add_veth(&nlmsg, sock, "veth0_virt_wifi", "veth1_virt_wifi");\
	netlink_add_linked(&nlmsg, sock, "virt_wifi", "virt_wifi0", "veth1_virt_wifi");\
	netlink_add_veth(&nlmsg, sock, "veth0_vlan", "veth1_vlan");\
	netlink_add_vlan(&nlmsg, sock, "vlan0", "veth0_vlan", 0, htons(ETH_P_8021Q));\
	netlink_add_vlan(&nlmsg, sock, "vlan1", "veth0_vlan", 1, htons(ETH_P_8021AD));\
	netlink_add_macvlan(&nlmsg, sock, "macvlan0", "veth1_vlan");\
	netlink_add_macvlan(&nlmsg, sock, "macvlan1", "veth1_vlan");\
	netlink_add_ipvlan(&nlmsg, sock, "ipvlan0", "veth0_vlan", IPVLAN_MODE_L2, 0);\
	netlink_add_ipvlan(&nlmsg, sock, "ipvlan1", "veth0_vlan", IPVLAN_MODE_L3S, IPVLAN_F_VEPA);\
	netlink_add_veth(&nlmsg, sock, "veth0_macvtap", "veth1_macvtap");\
	netlink_add_linked(&nlmsg, sock, "macvtap", "macvtap0", "veth0_macvtap");\
	netlink_add_linked(&nlmsg, sock, "macsec", "macsec0", "veth1_macvtap");\
	char addr[32];\
	sprintf(addr, DEV_IPV4, 14 + 10);\
	struct in_addr geneve_addr4;\
	if (inet_pton(AF_INET, addr, &geneve_addr4) <= 0)\
	exit(1);\
	struct in6_addr geneve_addr6;\
	if (inet_pton(AF_INET6, "fc00::01", &geneve_addr6) <= 0)\
	exit(1);\
	netlink_add_geneve(&nlmsg, sock, "geneve0", 0, &geneve_addr4, 0);\
	netlink_add_geneve(&nlmsg, sock, "geneve1", 1, 0, &geneve_addr6);\
	netdevsim_add((int)procid, 4);\
	netlink_wireguard_setup();\
	for (i = 0; i < sizeof(devices) / (sizeof(devices[0])); i++) \{\
		char addr[32];\
		sprintf(addr, DEV_IPV4, i + 10);\
		netlink_add_addr4(&nlmsg, sock, devices[i].name, addr);\
		if (!devices[i].noipv6) \{\
			sprintf(addr, DEV_IPV6, i + 10);\
			netlink_add_addr6(&nlmsg, sock, devices[i].name, addr);\
		\}\
		uint64_t macaddr = DEV_MAC + ((i + 10ull) << 40);\
		netlink_device_change(&nlmsg, sock, devices[i].name, true, 0, &macaddr, devices[i].macsize, NULL);\
	\}\
	close(sock);\
\}\
static void initialize_netdevices_init(void)\
\{\
	int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);\
	if (sock == -1)\
	exit(1);\
	struct \{\
		const char* type;\
		int macsize;\
		bool noipv6;\
		bool noup;\
	\} devtypes[] = \{\
	    \{"nr", 7, true\},\
	    \{"rose", 5, true, true\},\
	\};\
	unsigned i;\
	for (i = 0; i < sizeof(devtypes) / sizeof(devtypes[0]); i++) \{\
		char dev[32], addr[32];\
		sprintf(dev, "%s%d", devtypes[i].type, (int)procid);\
		sprintf(addr, "172.30.%d.%d", i, (int)procid + 1);\
		netlink_add_addr4(&nlmsg, sock, dev, addr);\
		if (!devtypes[i].noipv6) \{\
			sprintf(addr, "fe88::%02x:%02x", i, (int)procid + 1);\
			netlink_add_addr6(&nlmsg, sock, dev, addr);\
		\}\
		int macsize = devtypes[i].macsize;\
		uint64_t macaddr = 0xbbbbbb + ((unsigned long long)i << (8 * (macsize - 2))) +\
				 (procid << (8 * (macsize - 1)));\
		netlink_device_change(&nlmsg, sock, dev, !devtypes[i].noup, 0, &macaddr, macsize, NULL);\
	\}\
	close(sock);\
\}\
\
#define MAX_FDS 30\
\
#define BTPROTO_HCI 1\
#define ACL_LINK 1\
#define SCAN_PAGE 2\
\
typedef struct \{\
	uint8_t b[6];\
\} __attribute__((packed)) bdaddr_t;\
\
#define HCI_COMMAND_PKT 1\
#define HCI_EVENT_PKT 4\
#define HCI_VENDOR_PKT 0xff\
\
struct hci_command_hdr \{\
	uint16_t opcode;\
	uint8_t plen;\
\} __attribute__((packed));\
\
struct hci_event_hdr \{\
	uint8_t evt;\
	uint8_t plen;\
\} __attribute__((packed));\
\
#define HCI_EV_CONN_COMPLETE 0x03\
struct hci_ev_conn_complete \{\
	uint8_t status;\
	uint16_t handle;\
	bdaddr_t bdaddr;\
	uint8_t link_type;\
	uint8_t encr_mode;\
\} __attribute__((packed));\
\
#define HCI_EV_CONN_REQUEST 0x04\
struct hci_ev_conn_request \{\
	bdaddr_t bdaddr;\
	uint8_t dev_class[3];\
	uint8_t link_type;\
\} __attribute__((packed));\
\
#define HCI_EV_REMOTE_FEATURES 0x0b\
struct hci_ev_remote_features \{\
	uint8_t status;\
	uint16_t handle;\
	uint8_t features[8];\
\} __attribute__((packed));\
\
#define HCI_EV_CMD_COMPLETE 0x0e\
struct hci_ev_cmd_complete \{\
	uint8_t ncmd;\
	uint16_t opcode;\
\} __attribute__((packed));\
\
#define HCI_OP_WRITE_SCAN_ENABLE 0x0c1a\
\
#define HCI_OP_READ_BUFFER_SIZE 0x1005\
struct hci_rp_read_buffer_size \{\
	uint8_t status;\
	uint16_t acl_mtu;\
	uint8_t sco_mtu;\
	uint16_t acl_max_pkt;\
	uint16_t sco_max_pkt;\
\} __attribute__((packed));\
\
#define HCI_OP_READ_BD_ADDR 0x1009\
struct hci_rp_read_bd_addr \{\
	uint8_t status;\
	bdaddr_t bdaddr;\
\} __attribute__((packed));\
\
#define HCI_EV_LE_META 0x3e\
struct hci_ev_le_meta \{\
	uint8_t subevent;\
\} __attribute__((packed));\
\
#define HCI_EV_LE_CONN_COMPLETE 0x01\
struct hci_ev_le_conn_complete \{\
	uint8_t status;\
	uint16_t handle;\
	uint8_t role;\
	uint8_t bdaddr_type;\
	bdaddr_t bdaddr;\
	uint16_t interval;\
	uint16_t latency;\
	uint16_t supervision_timeout;\
	uint8_t clk_accurancy;\
\} __attribute__((packed));\
\
struct hci_dev_req \{\
	uint16_t dev_id;\
	uint32_t dev_opt;\
\};\
\
struct vhci_vendor_pkt \{\
	uint8_t type;\
	uint8_t opcode;\
	uint16_t id;\
\};\
\
#define HCIDEVUP _IOW('H', 201, int)\
#define HCISETSCAN _IOW('H', 221, int)\
\
static int vhci_fd = -1;\
\
static void rfkill_unblock_all()\
\{\
	int fd = open("/dev/rfkill", O_WRONLY);\
	if (fd < 0)\
	exit(1);\
	struct rfkill_event event = \{0\};\
	event.idx = 0;\
	event.type = RFKILL_TYPE_ALL;\
	event.op = RFKILL_OP_CHANGE_ALL;\
	event.soft = 0;\
	event.hard = 0;\
	if (write(fd, &event, sizeof(event)) < 0)\
	exit(1);\
	close(fd);\
\}\
\
static void hci_send_event_packet(int fd, uint8_t evt, void* data, size_t data_len)\
\{\
	struct iovec iv[3];\
	struct hci_event_hdr hdr;\
	hdr.evt = evt;\
	hdr.plen = data_len;\
	uint8_t type = HCI_EVENT_PKT;\
	iv[0].iov_base = &type;\
	iv[0].iov_len = sizeof(type);\
	iv[1].iov_base = &hdr;\
	iv[1].iov_len = sizeof(hdr);\
	iv[2].iov_base = data;\
	iv[2].iov_len = data_len;\
	if (writev(fd, iv, sizeof(iv) / sizeof(struct iovec)) < 0)\
	exit(1);\
\}\
\
static void hci_send_event_cmd_complete(int fd, uint16_t opcode, void* data, size_t data_len)\
\{\
	struct iovec iv[4];\
	struct hci_event_hdr hdr;\
	hdr.evt = HCI_EV_CMD_COMPLETE;\
	hdr.plen = sizeof(struct hci_ev_cmd_complete) + data_len;\
	struct hci_ev_cmd_complete evt_hdr;\
	evt_hdr.ncmd = 1;\
	evt_hdr.opcode = opcode;\
	uint8_t type = HCI_EVENT_PKT;\
	iv[0].iov_base = &type;\
	iv[0].iov_len = sizeof(type);\
	iv[1].iov_base = &hdr;\
	iv[1].iov_len = sizeof(hdr);\
	iv[2].iov_base = &evt_hdr;\
	iv[2].iov_len = sizeof(evt_hdr);\
	iv[3].iov_base = data;\
	iv[3].iov_len = data_len;\
	if (writev(fd, iv, sizeof(iv) / sizeof(struct iovec)) < 0)\
	exit(1);\
\}\
\
static bool process_command_pkt(int fd, char* buf, ssize_t buf_size)\
\{\
	struct hci_command_hdr* hdr = (struct hci_command_hdr*)buf;\
	if (buf_size < (ssize_t)sizeof(struct hci_command_hdr) ||\
	    hdr->plen != buf_size - sizeof(struct hci_command_hdr))\
	exit(1);\
	switch (hdr->opcode) \{\
	case HCI_OP_WRITE_SCAN_ENABLE: \{\
		uint8_t status = 0;\
		hci_send_event_cmd_complete(fd, hdr->opcode, &status, sizeof(status));\
		return true;\
	\}\
	case HCI_OP_READ_BD_ADDR: \{\
		struct hci_rp_read_bd_addr rp = \{0\};\
		rp.status = 0;\
		memset(&rp.bdaddr, 0xaa, 6);\
		hci_send_event_cmd_complete(fd, hdr->opcode, &rp, sizeof(rp));\
		return false;\
	\}\
	case HCI_OP_READ_BUFFER_SIZE: \{\
		struct hci_rp_read_buffer_size rp = \{0\};\
		rp.status = 0;\
		rp.acl_mtu = 1021;\
		rp.sco_mtu = 96;\
		rp.acl_max_pkt = 4;\
		rp.sco_max_pkt = 6;\
		hci_send_event_cmd_complete(fd, hdr->opcode, &rp, sizeof(rp));\
		return false;\
	\}\
	\}\
	char dummy[0xf9] = \{0\};\
	hci_send_event_cmd_complete(fd, hdr->opcode, dummy, sizeof(dummy));\
	return false;\
\}\
\
static void* event_thread(void* arg)\
\{\
	while (1) \{\
		char buf[1024] = \{0\};\
		ssize_t buf_size = read(vhci_fd, buf, sizeof(buf));\
		if (buf_size < 0)\
	exit(1);\
		if (buf_size > 0 && buf[0] == HCI_COMMAND_PKT) \{\
			if (process_command_pkt(vhci_fd, buf + 1, buf_size - 1))\
				break;\
		\}\
	\}\
	return NULL;\
\}\
#define HCI_HANDLE_1 200\
#define HCI_HANDLE_2 201\
\
static void initialize_vhci()\
\{\
	int hci_sock = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);\
	if (hci_sock < 0)\
	exit(1);\
	vhci_fd = open("/dev/vhci", O_RDWR);\
	if (vhci_fd == -1)\
	exit(1);\
	const int kVhciFd = 202;\
	if (dup2(vhci_fd, kVhciFd) < 0)\
	exit(1);\
	close(vhci_fd);\
	vhci_fd = kVhciFd;\
	struct vhci_vendor_pkt vendor_pkt;\
	if (read(vhci_fd, &vendor_pkt, sizeof(vendor_pkt)) != sizeof(vendor_pkt))\
	exit(1);\
	if (vendor_pkt.type != HCI_VENDOR_PKT)\
	exit(1);\
	pthread_t th;\
	if (pthread_create(&th, NULL, event_thread, NULL))\
	exit(1);\
	int ret = ioctl(hci_sock, HCIDEVUP, vendor_pkt.id);\
	if (ret) \{\
		if (errno == ERFKILL) \{\
			rfkill_unblock_all();\
			ret = ioctl(hci_sock, HCIDEVUP, vendor_pkt.id);\
		\}\
		if (ret && errno != EALREADY)\
	exit(1);\
	\}\
	struct hci_dev_req dr = \{0\};\
	dr.dev_id = vendor_pkt.id;\
	dr.dev_opt = SCAN_PAGE;\
	if (ioctl(hci_sock, HCISETSCAN, &dr))\
	exit(1);\
	struct hci_ev_conn_request request;\
	memset(&request, 0, sizeof(request));\
	memset(&request.bdaddr, 0xaa, 6);\
	*(uint8_t*)&request.bdaddr.b[5] = 0x10;\
	request.link_type = ACL_LINK;\
	hci_send_event_packet(vhci_fd, HCI_EV_CONN_REQUEST, &request, sizeof(request));\
	struct hci_ev_conn_complete complete;\
	memset(&complete, 0, sizeof(complete));\
	complete.status = 0;\
	complete.handle = HCI_HANDLE_1;\
	memset(&complete.bdaddr, 0xaa, 6);\
	*(uint8_t*)&complete.bdaddr.b[5] = 0x10;\
	complete.link_type = ACL_LINK;\
	complete.encr_mode = 0;\
	hci_send_event_packet(vhci_fd, HCI_EV_CONN_COMPLETE, &complete, sizeof(complete));\
	struct hci_ev_remote_features features;\
	memset(&features, 0, sizeof(features));\
	features.status = 0;\
	features.handle = HCI_HANDLE_1;\
	hci_send_event_packet(vhci_fd, HCI_EV_REMOTE_FEATURES, &features, sizeof(features));\
	struct \{\
		struct hci_ev_le_meta le_meta;\
		struct hci_ev_le_conn_complete le_conn;\
	\} le_conn;\
	memset(&le_conn, 0, sizeof(le_conn));\
	le_conn.le_meta.subevent = HCI_EV_LE_CONN_COMPLETE;\
	memset(&le_conn.le_conn.bdaddr, 0xaa, 6);\
	*(uint8_t*)&le_conn.le_conn.bdaddr.b[5] = 0x11;\
	le_conn.le_conn.role = 1;\
	le_conn.le_conn.handle = HCI_HANDLE_2;\
	hci_send_event_packet(vhci_fd, HCI_EV_LE_META, &le_conn, sizeof(le_conn));\
	pthread_join(th, NULL);\
	close(hci_sock);\
\}\
\
static void setup_common()\
\{\
	if (mount(0, "/sys/fs/fuse/connections", "fusectl", 0, 0)) \{\
	\}\
\}\
\
static void setup_binderfs()\
\{\
	if (mkdir("/dev/binderfs", 0777)) \{\
	\}\
	if (mount("binder", "/dev/binderfs", "binder", 0, NULL)) \{\
	\}\
	if (symlink("/dev/binderfs", "./binderfs")) \{\
	\}\
\}\
\
static void loop();\
\
static void sandbox_common()\
\{\
	prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);\
	setsid();\
	struct rlimit rlim;\
	rlim.rlim_cur = rlim.rlim_max = (200 << 20);\
	setrlimit(RLIMIT_AS, &rlim);\
	rlim.rlim_cur = rlim.rlim_max = 32 << 20;\
	setrlimit(RLIMIT_MEMLOCK, &rlim);\
	rlim.rlim_cur = rlim.rlim_max = 136 << 20;\
	setrlimit(RLIMIT_FSIZE, &rlim);\
	rlim.rlim_cur = rlim.rlim_max = 1 << 20;\
	setrlimit(RLIMIT_STACK, &rlim);\
	rlim.rlim_cur = rlim.rlim_max = 0;\
	setrlimit(RLIMIT_CORE, &rlim);\
	rlim.rlim_cur = rlim.rlim_max = 256;\
	setrlimit(RLIMIT_NOFILE, &rlim);\
	if (unshare(CLONE_NEWNS)) \{\
	\}\
	if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL)) \{\
	\}\
	if (unshare(CLONE_NEWIPC)) \{\
	\}\
	if (unshare(0x02000000)) \{\
	\}\
	if (unshare(CLONE_NEWUTS)) \{\
	\}\
	if (unshare(CLONE_SYSVSEM)) \{\
	\}\
	typedef struct \{\
		const char* name;\
		const char* value;\
	\} sysctl_t;\
	static const sysctl_t sysctls[] = \{\
	    \{"/proc/sys/kernel/shmmax", "16777216"\},\
	    \{"/proc/sys/kernel/shmall", "536870912"\},\
	    \{"/proc/sys/kernel/shmmni", "1024"\},\
	    \{"/proc/sys/kernel/msgmax", "8192"\},\
	    \{"/proc/sys/kernel/msgmni", "1024"\},\
	    \{"/proc/sys/kernel/msgmnb", "1024"\},\
	    \{"/proc/sys/kernel/sem", "1024 1048576 500 1024"\},\
	\};\
	unsigned i;\
	for (i = 0; i < sizeof(sysctls) / sizeof(sysctls[0]); i++)\
		write_file(sysctls[i].name, sysctls[i].value);\
\}\
\
static int wait_for_loop(int pid)\
\{\
	if (pid < 0)\
	exit(1);\
	int status = 0;\
	while (waitpid(-1, &status, __WALL) != pid) \{\
	\}\
	return WEXITSTATUS(status);\
\}\
\
static void drop_caps(void)\
\{\
	struct __user_cap_header_struct cap_hdr = \{\};\
	struct __user_cap_data_struct cap_data[2] = \{\};\
	cap_hdr.version = _LINUX_CAPABILITY_VERSION_3;\
	cap_hdr.pid = getpid();\
	if (syscall(SYS_capget, &cap_hdr, &cap_data))\
	exit(1);\
	const int drop = (1 << CAP_SYS_PTRACE) | (1 << CAP_SYS_NICE);\
	cap_data[0].effective &= ~drop;\
	cap_data[0].permitted &= ~drop;\
	cap_data[0].inheritable &= ~drop;\
	if (syscall(SYS_capset, &cap_hdr, &cap_data))\
	exit(1);\
\}\
\
static int do_sandbox_none(void)\
\{\
	if (unshare(CLONE_NEWPID)) \{\
	\}\
	int pid = fork();\
	if (pid != 0)\
		return wait_for_loop(pid);\
	setup_common();\
	initialize_vhci();\
	sandbox_common();\
	drop_caps();\
	initialize_netdevices_init();\
	if (unshare(CLONE_NEWNET)) \{\
	\}\
	initialize_netdevices();\
	setup_binderfs();\
	loop();\
	exit(1);\
\}\
\
static void close_fds()\
\{\
	for (int fd = 3; fd < MAX_FDS; fd++)\
		close(fd);\
\}\
\
static void setup_binfmt_misc()\
\{\
	if (mount(0, "/proc/sys/fs/binfmt_misc", "binfmt_misc", 0, 0)) \{\
	\}\
	write_file("/proc/sys/fs/binfmt_misc/register", ":syz0:M:0:\\x01::./file0:");\
	write_file("/proc/sys/fs/binfmt_misc/register", ":syz1:M:1:\\x02::./file0:POC");\
\}\
\
static void setup_sysctl()\
\{\
	char mypid[32];\
	snprintf(mypid, sizeof(mypid), "%d", getpid());\
	struct \{\
		const char* name;\
		const char* data;\
	\} files[] = \{\
		\{"/sys/kernel/debug/x86/nmi_longest_ns", "10000000000"\},\
		\{"/proc/sys/kernel/hung_task_check_interval_secs", "20"\},\
		\{"/proc/sys/net/core/bpf_jit_kallsyms", "1"\},\
		\{"/proc/sys/net/core/bpf_jit_harden", "0"\},\
		\{"/proc/sys/kernel/kptr_restrict", "0"\},\
		\{"/proc/sys/kernel/softlockup_all_cpu_backtrace", "1"\},\
		\{"/proc/sys/fs/mount-max", "100"\},\
		\{"/proc/sys/vm/oom_dump_tasks", "0"\},\
		\{"/proc/sys/debug/exception-trace", "0"\},\
		\{"/proc/sys/kernel/printk", "7 4 1 3"\},\
		\{"/proc/sys/net/ipv4/ping_group_range", "0 65535"\},\
		\{"/proc/sys/kernel/keys/gc_delay", "1"\},\
		\{"/proc/sys/vm/oom_kill_allocating_task", "1"\},\
		\{"/proc/sys/kernel/ctrl-alt-del", "0"\},\
		\{"/proc/sys/kernel/cad_pid", mypid\},\
	\};\
	for (size_t i = 0; i < sizeof(files) / sizeof(files[0]); i++) \{\
		if (!write_file(files[i].name, files[i].data))\
			printf("write to %s failed: %s\\n", files[i].name, strerror(errno));\
	\}\
\}\
\
uint64_t r[1] = \{0xffffffffffffffff\};\
\
void loop(void)\
\{\
		intptr_t res = 0;\
	res = syscall(__NR_socket, 2ul, 2ul, 0x73);\
	if (res != -1)\
		r[0] = res;\
memcpy((void*)0x20000040, "filter\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000", 32);\
*(uint32_t*)0x20000060 = 6;\
*(uint32_t*)0x20000064 = 0;\
*(uint32_t*)0x20000068 = 0x90;\
*(uint64_t*)0x20000070 = 0;\
*(uint64_t*)0x20000078 = 0x20000400;\
*(uint64_t*)0x20000080 = 0x20000430;\
*(uint64_t*)0x20000088 = 0;\
*(uint64_t*)0x20000090 = 0;\
*(uint64_t*)0x20000098 = 0;\
*(uint32_t*)0x200000a0 = 0;\
*(uint64_t*)0x200000a8 = 0;\
*(uint64_t*)0x200000b0 = 0x20000400;\
*(uint32_t*)0x20000400 = 0;\
memset((void*)0x20000404, 0, 32);\
*(uint32_t*)0x20000424 = 0;\
*(uint32_t*)0x20000428 = -1;\
*(uint32_t*)0x2000042c = 0;\
*(uint32_t*)0x20000430 = 0;\
memset((void*)0x20000434, 0, 32);\
*(uint32_t*)0x20000454 = 0;\
*(uint32_t*)0x20000458 = -1;\
*(uint32_t*)0x2000045c = 0;\
*(uint32_t*)0x20000460 = 0;\
memset((void*)0x20000464, 0, 32);\
*(uint32_t*)0x20000484 = 0;\
*(uint32_t*)0x20000488 = 0xfffffffc;\
*(uint32_t*)0x2000048c = 0;\
	syscall(__NR_setsockopt, r[0], 0, 0x80, 0x20000040ul, 0x108ul);\
	close_fds();\
\}\
int main(void)\
\{\
		syscall(__NR_mmap, 0x1ffff000ul, 0x1000ul, 0ul, 0x32ul, -1, 0ul);\
	syscall(__NR_mmap, 0x20000000ul, 0x1000000ul, 7ul, 0x32ul, -1, 0ul);\
	syscall(__NR_mmap, 0x21000000ul, 0x1000ul, 0ul, 0x32ul, -1, 0ul);\
	setup_sysctl();\
	setup_binfmt_misc();\
			do_sandbox_none();\
	return 0;\
\}\
}

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [External] : [PATCH nf] netfilter: ebtables: reject blobs that don't provide all entry points
  2022-08-20 17:35   ` [Bridge] " Florian Westphal
@ 2022-08-20 19:20     ` Harshit Mogalapalli
  -1 siblings, 0 replies; 17+ messages in thread
From: Harshit Mogalapalli @ 2022-08-20 19:20 UTC (permalink / raw)
  To: Florian Westphal, netfilter-devel
  Cc: syzkaller, george.kennedy, vegard.nossum, john.p.donnelly,
	bridge, netdev, linux-kernel

Hi Florian,

On 20/08/22 11:05 pm, Florian Westphal wrote:
> For some reason ebtables reject blobs that provide entry points that are
> not supported by the table.
> 
> What it should instead reject is the opposite, i.e. rulesets that
> DO NOT provide an entry point that is supported by the table.
> 
> t->valid_hooks is the bitmask of hooks (input, forward ...) that will
> see packets.  So, providing an entry point that is not support is
> harmless (never called/used), but the reverse is NOT, this will cause
> crash because the ebtables traverser doesn't expect a NULL blob for
> a location its receiving packets for.
> 
> Instead of fixing all the individual checks, do what iptables is doing and
> reject all blobs that doesn't provide the expected hooks.
> 
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Reported-by: Harshit Mogalapalli <harshit.m.mogalapalli@oracle.com>
> Signed-off-by: Florian Westphal <fw@strlen.de>
> ---
>   Harshit, can you check if this also silences your reproducer?
> 

Thanks for the patch, I have run the reproducer on patched kernel(this 
patch) multiple times and the problem is not seen. So it silences the 
reproducer.

Regards,
Harshit

>   Thanks!
> 
>   include/linux/netfilter_bridge/ebtables.h | 4 ----
>   net/bridge/netfilter/ebtable_broute.c     | 8 --------
>   net/bridge/netfilter/ebtable_filter.c     | 8 --------
>   net/bridge/netfilter/ebtable_nat.c        | 8 --------
>   net/bridge/netfilter/ebtables.c           | 8 +-------
>   5 files changed, 1 insertion(+), 35 deletions(-)
> 
> diff --git a/include/linux/netfilter_bridge/ebtables.h b/include/linux/netfilter_bridge/ebtables.h
> index a13296d6c7ce..fd533552a062 100644
> --- a/include/linux/netfilter_bridge/ebtables.h
> +++ b/include/linux/netfilter_bridge/ebtables.h
> @@ -94,10 +94,6 @@ struct ebt_table {
>   	struct ebt_replace_kernel *table;
>   	unsigned int valid_hooks;
>   	rwlock_t lock;
> -	/* e.g. could be the table explicitly only allows certain
> -	 * matches, targets, ... 0 == let it in */
> -	int (*check)(const struct ebt_table_info *info,
> -	   unsigned int valid_hooks);
>   	/* the data used by the kernel */
>   	struct ebt_table_info *private;
>   	struct nf_hook_ops *ops;
> diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c
> index 1a11064f9990..8f19253024b0 100644
> --- a/net/bridge/netfilter/ebtable_broute.c
> +++ b/net/bridge/netfilter/ebtable_broute.c
> @@ -36,18 +36,10 @@ static struct ebt_replace_kernel initial_table = {
>   	.entries	= (char *)&initial_chain,
>   };
>   
> -static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
> -{
> -	if (valid_hooks & ~(1 << NF_BR_BROUTING))
> -		return -EINVAL;
> -	return 0;
> -}
> -
>   static const struct ebt_table broute_table = {
>   	.name		= "broute",
>   	.table		= &initial_table,
>   	.valid_hooks	= 1 << NF_BR_BROUTING,
> -	.check		= check,
>   	.me		= THIS_MODULE,
>   };
>   
> diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c
> index cb949436bc0e..278f324e6752 100644
> --- a/net/bridge/netfilter/ebtable_filter.c
> +++ b/net/bridge/netfilter/ebtable_filter.c
> @@ -43,18 +43,10 @@ static struct ebt_replace_kernel initial_table = {
>   	.entries	= (char *)initial_chains,
>   };
>   
> -static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
> -{
> -	if (valid_hooks & ~FILTER_VALID_HOOKS)
> -		return -EINVAL;
> -	return 0;
> -}
> -
>   static const struct ebt_table frame_filter = {
>   	.name		= "filter",
>   	.table		= &initial_table,
>   	.valid_hooks	= FILTER_VALID_HOOKS,
> -	.check		= check,
>   	.me		= THIS_MODULE,
>   };
>   
> diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c
> index 5ee0531ae506..9066f7f376d5 100644
> --- a/net/bridge/netfilter/ebtable_nat.c
> +++ b/net/bridge/netfilter/ebtable_nat.c
> @@ -43,18 +43,10 @@ static struct ebt_replace_kernel initial_table = {
>   	.entries	= (char *)initial_chains,
>   };
>   
> -static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
> -{
> -	if (valid_hooks & ~NAT_VALID_HOOKS)
> -		return -EINVAL;
> -	return 0;
> -}
> -
>   static const struct ebt_table frame_nat = {
>   	.name		= "nat",
>   	.table		= &initial_table,
>   	.valid_hooks	= NAT_VALID_HOOKS,
> -	.check		= check,
>   	.me		= THIS_MODULE,
>   };
>   
> diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
> index f2dbefb61ce8..9a0ae59cdc50 100644
> --- a/net/bridge/netfilter/ebtables.c
> +++ b/net/bridge/netfilter/ebtables.c
> @@ -1040,8 +1040,7 @@ static int do_replace_finish(struct net *net, struct ebt_replace *repl,
>   		goto free_iterate;
>   	}
>   
> -	/* the table doesn't like it */
> -	if (t->check && (ret = t->check(newinfo, repl->valid_hooks)))
> +	if (repl->valid_hooks != t->valid_hooks)
>   		goto free_unlock;
>   
>   	if (repl->num_counters && repl->num_counters != t->private->nentries) {
> @@ -1231,11 +1230,6 @@ int ebt_register_table(struct net *net, const struct ebt_table *input_table,
>   	if (ret != 0)
>   		goto free_chainstack;
>   
> -	if (table->check && table->check(newinfo, table->valid_hooks)) {
> -		ret = -EINVAL;
> -		goto free_chainstack;
> -	}
> -
>   	table->private = newinfo;
>   	rwlock_init(&table->lock);
>   	mutex_lock(&ebt_mutex);


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [Bridge] [External] : [PATCH nf] netfilter: ebtables: reject blobs that don't provide all entry points
@ 2022-08-20 19:20     ` Harshit Mogalapalli
  0 siblings, 0 replies; 17+ messages in thread
From: Harshit Mogalapalli @ 2022-08-20 19:20 UTC (permalink / raw)
  To: Florian Westphal, netfilter-devel
  Cc: john.p.donnelly, vegard.nossum, netdev, bridge, linux-kernel,
	george.kennedy, syzkaller

Hi Florian,

On 20/08/22 11:05 pm, Florian Westphal wrote:
> For some reason ebtables reject blobs that provide entry points that are
> not supported by the table.
> 
> What it should instead reject is the opposite, i.e. rulesets that
> DO NOT provide an entry point that is supported by the table.
> 
> t->valid_hooks is the bitmask of hooks (input, forward ...) that will
> see packets.  So, providing an entry point that is not support is
> harmless (never called/used), but the reverse is NOT, this will cause
> crash because the ebtables traverser doesn't expect a NULL blob for
> a location its receiving packets for.
> 
> Instead of fixing all the individual checks, do what iptables is doing and
> reject all blobs that doesn't provide the expected hooks.
> 
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Reported-by: Harshit Mogalapalli <harshit.m.mogalapalli@oracle.com>
> Signed-off-by: Florian Westphal <fw@strlen.de>
> ---
>   Harshit, can you check if this also silences your reproducer?
> 

Thanks for the patch, I have run the reproducer on patched kernel(this 
patch) multiple times and the problem is not seen. So it silences the 
reproducer.

Regards,
Harshit

>   Thanks!
> 
>   include/linux/netfilter_bridge/ebtables.h | 4 ----
>   net/bridge/netfilter/ebtable_broute.c     | 8 --------
>   net/bridge/netfilter/ebtable_filter.c     | 8 --------
>   net/bridge/netfilter/ebtable_nat.c        | 8 --------
>   net/bridge/netfilter/ebtables.c           | 8 +-------
>   5 files changed, 1 insertion(+), 35 deletions(-)
> 
> diff --git a/include/linux/netfilter_bridge/ebtables.h b/include/linux/netfilter_bridge/ebtables.h
> index a13296d6c7ce..fd533552a062 100644
> --- a/include/linux/netfilter_bridge/ebtables.h
> +++ b/include/linux/netfilter_bridge/ebtables.h
> @@ -94,10 +94,6 @@ struct ebt_table {
>   	struct ebt_replace_kernel *table;
>   	unsigned int valid_hooks;
>   	rwlock_t lock;
> -	/* e.g. could be the table explicitly only allows certain
> -	 * matches, targets, ... 0 == let it in */
> -	int (*check)(const struct ebt_table_info *info,
> -	   unsigned int valid_hooks);
>   	/* the data used by the kernel */
>   	struct ebt_table_info *private;
>   	struct nf_hook_ops *ops;
> diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c
> index 1a11064f9990..8f19253024b0 100644
> --- a/net/bridge/netfilter/ebtable_broute.c
> +++ b/net/bridge/netfilter/ebtable_broute.c
> @@ -36,18 +36,10 @@ static struct ebt_replace_kernel initial_table = {
>   	.entries	= (char *)&initial_chain,
>   };
>   
> -static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
> -{
> -	if (valid_hooks & ~(1 << NF_BR_BROUTING))
> -		return -EINVAL;
> -	return 0;
> -}
> -
>   static const struct ebt_table broute_table = {
>   	.name		= "broute",
>   	.table		= &initial_table,
>   	.valid_hooks	= 1 << NF_BR_BROUTING,
> -	.check		= check,
>   	.me		= THIS_MODULE,
>   };
>   
> diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c
> index cb949436bc0e..278f324e6752 100644
> --- a/net/bridge/netfilter/ebtable_filter.c
> +++ b/net/bridge/netfilter/ebtable_filter.c
> @@ -43,18 +43,10 @@ static struct ebt_replace_kernel initial_table = {
>   	.entries	= (char *)initial_chains,
>   };
>   
> -static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
> -{
> -	if (valid_hooks & ~FILTER_VALID_HOOKS)
> -		return -EINVAL;
> -	return 0;
> -}
> -
>   static const struct ebt_table frame_filter = {
>   	.name		= "filter",
>   	.table		= &initial_table,
>   	.valid_hooks	= FILTER_VALID_HOOKS,
> -	.check		= check,
>   	.me		= THIS_MODULE,
>   };
>   
> diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c
> index 5ee0531ae506..9066f7f376d5 100644
> --- a/net/bridge/netfilter/ebtable_nat.c
> +++ b/net/bridge/netfilter/ebtable_nat.c
> @@ -43,18 +43,10 @@ static struct ebt_replace_kernel initial_table = {
>   	.entries	= (char *)initial_chains,
>   };
>   
> -static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
> -{
> -	if (valid_hooks & ~NAT_VALID_HOOKS)
> -		return -EINVAL;
> -	return 0;
> -}
> -
>   static const struct ebt_table frame_nat = {
>   	.name		= "nat",
>   	.table		= &initial_table,
>   	.valid_hooks	= NAT_VALID_HOOKS,
> -	.check		= check,
>   	.me		= THIS_MODULE,
>   };
>   
> diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
> index f2dbefb61ce8..9a0ae59cdc50 100644
> --- a/net/bridge/netfilter/ebtables.c
> +++ b/net/bridge/netfilter/ebtables.c
> @@ -1040,8 +1040,7 @@ static int do_replace_finish(struct net *net, struct ebt_replace *repl,
>   		goto free_iterate;
>   	}
>   
> -	/* the table doesn't like it */
> -	if (t->check && (ret = t->check(newinfo, repl->valid_hooks)))
> +	if (repl->valid_hooks != t->valid_hooks)
>   		goto free_unlock;
>   
>   	if (repl->num_counters && repl->num_counters != t->private->nentries) {
> @@ -1231,11 +1230,6 @@ int ebt_register_table(struct net *net, const struct ebt_table *input_table,
>   	if (ret != 0)
>   		goto free_chainstack;
>   
> -	if (table->check && table->check(newinfo, table->valid_hooks)) {
> -		ret = -EINVAL;
> -		goto free_chainstack;
> -	}
> -
>   	table->private = newinfo;
>   	rwlock_init(&table->lock);
>   	mutex_lock(&ebt_mutex);


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH nf] netfilter: ebtables: reject blobs that don't provide all entry points
  2022-08-20 17:35   ` [Bridge] " Florian Westphal
  (?)
  (?)
@ 2022-08-22 10:55   ` John Donnelly
  -1 siblings, 0 replies; 17+ messages in thread
From: John Donnelly @ 2022-08-22 10:55 UTC (permalink / raw)
  To: Florian Westphal, netfilter-devel; +Cc: syzkaller

On 8/20/22 12:35, Florian Westphal wrote:
> For some reason ebtables reject blobs that provide entry points that are
> not supported by the table.

Hi,

Could you include the panic dump noted in the original message and note 
it was found by KASAN+Syzkaller testing in the final version ?

Thank you.


> 
> What it should instead reject is the opposite, i.e. rulesets that
> DO NOT provide an entry point that is supported by the table.
> 
> t->valid_hooks is the bitmask of hooks (input, forward ...) that will
> see packets.  So, providing an entry point that is not support is
> harmless (never called/used), but the reverse is NOT, this will cause
> crash because the ebtables traverser doesn't expect a NULL blob for
> a location its receiving packets for.
> 
> Instead of fixing all the individual checks, do what iptables is doing and
> reject all blobs that doesn't provide the expected hooks.
> 
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Reported-by: Harshit Mogalapalli <harshit.m.mogalapalli@oracle.com>
> Signed-off-by: Florian Westphal <fw@strlen.de>
> ---
>   Harshit, can you check if this also silences your reproducer?
> 
>   Thanks!
> 
>   include/linux/netfilter_bridge/ebtables.h | 4 ----
>   net/bridge/netfilter/ebtable_broute.c     | 8 --------
>   net/bridge/netfilter/ebtable_filter.c     | 8 --------
>   net/bridge/netfilter/ebtable_nat.c        | 8 --------
>   net/bridge/netfilter/ebtables.c           | 8 +-------
>   5 files changed, 1 insertion(+), 35 deletions(-)
> 
> diff --git a/include/linux/netfilter_bridge/ebtables.h b/include/linux/netfilter_bridge/ebtables.h
> index a13296d6c7ce..fd533552a062 100644
> --- a/include/linux/netfilter_bridge/ebtables.h
> +++ b/include/linux/netfilter_bridge/ebtables.h
> @@ -94,10 +94,6 @@ struct ebt_table {
>   	struct ebt_replace_kernel *table;
>   	unsigned int valid_hooks;
>   	rwlock_t lock;
> -	/* e.g. could be the table explicitly only allows certain
> -	 * matches, targets, ... 0 == let it in */
> -	int (*check)(const struct ebt_table_info *info,
> -	   unsigned int valid_hooks);
>   	/* the data used by the kernel */
>   	struct ebt_table_info *private;
>   	struct nf_hook_ops *ops;
> diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c
> index 1a11064f9990..8f19253024b0 100644
> --- a/net/bridge/netfilter/ebtable_broute.c
> +++ b/net/bridge/netfilter/ebtable_broute.c
> @@ -36,18 +36,10 @@ static struct ebt_replace_kernel initial_table = {
>   	.entries	= (char *)&initial_chain,
>   };
>   
> -static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
> -{
> -	if (valid_hooks & ~(1 << NF_BR_BROUTING))
> -		return -EINVAL;
> -	return 0;
> -}
> -
>   static const struct ebt_table broute_table = {
>   	.name		= "broute",
>   	.table		= &initial_table,
>   	.valid_hooks	= 1 << NF_BR_BROUTING,
> -	.check		= check,
>   	.me		= THIS_MODULE,
>   };
>   
> diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c
> index cb949436bc0e..278f324e6752 100644
> --- a/net/bridge/netfilter/ebtable_filter.c
> +++ b/net/bridge/netfilter/ebtable_filter.c
> @@ -43,18 +43,10 @@ static struct ebt_replace_kernel initial_table = {
>   	.entries	= (char *)initial_chains,
>   };
>   
> -static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
> -{
> -	if (valid_hooks & ~FILTER_VALID_HOOKS)
> -		return -EINVAL;
> -	return 0;
> -}
> -
>   static const struct ebt_table frame_filter = {
>   	.name		= "filter",
>   	.table		= &initial_table,
>   	.valid_hooks	= FILTER_VALID_HOOKS,
> -	.check		= check,
>   	.me		= THIS_MODULE,
>   };
>   
> diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c
> index 5ee0531ae506..9066f7f376d5 100644
> --- a/net/bridge/netfilter/ebtable_nat.c
> +++ b/net/bridge/netfilter/ebtable_nat.c
> @@ -43,18 +43,10 @@ static struct ebt_replace_kernel initial_table = {
>   	.entries	= (char *)initial_chains,
>   };
>   
> -static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
> -{
> -	if (valid_hooks & ~NAT_VALID_HOOKS)
> -		return -EINVAL;
> -	return 0;
> -}
> -
>   static const struct ebt_table frame_nat = {
>   	.name		= "nat",
>   	.table		= &initial_table,
>   	.valid_hooks	= NAT_VALID_HOOKS,
> -	.check		= check,
>   	.me		= THIS_MODULE,
>   };
>   
> diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
> index f2dbefb61ce8..9a0ae59cdc50 100644
> --- a/net/bridge/netfilter/ebtables.c
> +++ b/net/bridge/netfilter/ebtables.c
> @@ -1040,8 +1040,7 @@ static int do_replace_finish(struct net *net, struct ebt_replace *repl,
>   		goto free_iterate;
>   	}
>   
> -	/* the table doesn't like it */
> -	if (t->check && (ret = t->check(newinfo, repl->valid_hooks)))
> +	if (repl->valid_hooks != t->valid_hooks)
>   		goto free_unlock;
>   
>   	if (repl->num_counters && repl->num_counters != t->private->nentries) {
> @@ -1231,11 +1230,6 @@ int ebt_register_table(struct net *net, const struct ebt_table *input_table,
>   	if (ret != 0)
>   		goto free_chainstack;
>   
> -	if (table->check && table->check(newinfo, table->valid_hooks)) {
> -		ret = -EINVAL;
> -		goto free_chainstack;
> -	}
> -
>   	table->private = newinfo;
>   	rwlock_init(&table->lock);
>   	mutex_lock(&ebt_mutex);


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH nf] netfilter: ebtables: reject blobs that don't provide all entry points
  2022-08-20 17:35   ` [Bridge] " Florian Westphal
@ 2022-08-29 13:57     ` john.p.donnelly
  -1 siblings, 0 replies; 17+ messages in thread
From: john.p.donnelly @ 2022-08-29 13:57 UTC (permalink / raw)
  To: Florian Westphal, netfilter-devel
  Cc: syzkaller, george.kennedy, vegard.nossum, bridge, netdev,
	linux-kernel, Harshit Mogalapalli

On 8/20/22 12:35 PM, Florian Westphal wrote:
> For some reason ebtables reject blobs that provide entry points that are
> not supported by the table.
> 
> What it should instead reject is the opposite, i.e. rulesets that
> DO NOT provide an entry point that is supported by the table.
> 
> t->valid_hooks is the bitmask of hooks (input, forward ...) that will
> see packets.  So, providing an entry point that is not support is
> harmless (never called/used), but the reverse is NOT, this will cause
> crash because the ebtables traverser doesn't expect a NULL blob for
> a location its receiving packets for.
> 
> Instead of fixing all the individual checks, do what iptables is doing and
> reject all blobs that doesn't provide the expected hooks.
> 
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Reported-by: Harshit Mogalapalli <harshit.m.mogalapalli@oracle.com>
> Signed-off-by: Florian Westphal <fw@strlen.de>

Hi,

  Could you please add the panic stack mentioned above  and syzkaller 
reproducer ID to the commit text ?



> ---
>   Harshit, can you check if this also silences your reproducer?
> 
>   Thanks!
> 
>   include/linux/netfilter_bridge/ebtables.h | 4 ----
>   net/bridge/netfilter/ebtable_broute.c     | 8 --------
>   net/bridge/netfilter/ebtable_filter.c     | 8 --------
>   net/bridge/netfilter/ebtable_nat.c        | 8 --------
>   net/bridge/netfilter/ebtables.c           | 8 +-------
>   5 files changed, 1 insertion(+), 35 deletions(-)
> 
> diff --git a/include/linux/netfilter_bridge/ebtables.h b/include/linux/netfilter_bridge/ebtables.h
> index a13296d6c7ce..fd533552a062 100644
> --- a/include/linux/netfilter_bridge/ebtables.h
> +++ b/include/linux/netfilter_bridge/ebtables.h
> @@ -94,10 +94,6 @@ struct ebt_table {
>   	struct ebt_replace_kernel *table;
>   	unsigned int valid_hooks;
>   	rwlock_t lock;
> -	/* e.g. could be the table explicitly only allows certain
> -	 * matches, targets, ... 0 == let it in */
> -	int (*check)(const struct ebt_table_info *info,
> -	   unsigned int valid_hooks);
>   	/* the data used by the kernel */
>   	struct ebt_table_info *private;
>   	struct nf_hook_ops *ops;
> diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c
> index 1a11064f9990..8f19253024b0 100644
> --- a/net/bridge/netfilter/ebtable_broute.c
> +++ b/net/bridge/netfilter/ebtable_broute.c
> @@ -36,18 +36,10 @@ static struct ebt_replace_kernel initial_table = {
>   	.entries	= (char *)&initial_chain,
>   };
>   
> -static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
> -{
> -	if (valid_hooks & ~(1 << NF_BR_BROUTING))
> -		return -EINVAL;
> -	return 0;
> -}
> -
>   static const struct ebt_table broute_table = {
>   	.name		= "broute",
>   	.table		= &initial_table,
>   	.valid_hooks	= 1 << NF_BR_BROUTING,
> -	.check		= check,
>   	.me		= THIS_MODULE,
>   };
>   
> diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c
> index cb949436bc0e..278f324e6752 100644
> --- a/net/bridge/netfilter/ebtable_filter.c
> +++ b/net/bridge/netfilter/ebtable_filter.c
> @@ -43,18 +43,10 @@ static struct ebt_replace_kernel initial_table = {
>   	.entries	= (char *)initial_chains,
>   };
>   
> -static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
> -{
> -	if (valid_hooks & ~FILTER_VALID_HOOKS)
> -		return -EINVAL;
> -	return 0;
> -}
> -
>   static const struct ebt_table frame_filter = {
>   	.name		= "filter",
>   	.table		= &initial_table,
>   	.valid_hooks	= FILTER_VALID_HOOKS,
> -	.check		= check,
>   	.me		= THIS_MODULE,
>   };
>   
> diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c
> index 5ee0531ae506..9066f7f376d5 100644
> --- a/net/bridge/netfilter/ebtable_nat.c
> +++ b/net/bridge/netfilter/ebtable_nat.c
> @@ -43,18 +43,10 @@ static struct ebt_replace_kernel initial_table = {
>   	.entries	= (char *)initial_chains,
>   };
>   
> -static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
> -{
> -	if (valid_hooks & ~NAT_VALID_HOOKS)
> -		return -EINVAL;
> -	return 0;
> -}
> -
>   static const struct ebt_table frame_nat = {
>   	.name		= "nat",
>   	.table		= &initial_table,
>   	.valid_hooks	= NAT_VALID_HOOKS,
> -	.check		= check,
>   	.me		= THIS_MODULE,
>   };
>   
> diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
> index f2dbefb61ce8..9a0ae59cdc50 100644
> --- a/net/bridge/netfilter/ebtables.c
> +++ b/net/bridge/netfilter/ebtables.c
> @@ -1040,8 +1040,7 @@ static int do_replace_finish(struct net *net, struct ebt_replace *repl,
>   		goto free_iterate;
>   	}
>   
> -	/* the table doesn't like it */
> -	if (t->check && (ret = t->check(newinfo, repl->valid_hooks)))
> +	if (repl->valid_hooks != t->valid_hooks)
>   		goto free_unlock;
>   
>   	if (repl->num_counters && repl->num_counters != t->private->nentries) {
> @@ -1231,11 +1230,6 @@ int ebt_register_table(struct net *net, const struct ebt_table *input_table,
>   	if (ret != 0)
>   		goto free_chainstack;
>   
> -	if (table->check && table->check(newinfo, table->valid_hooks)) {
> -		ret = -EINVAL;
> -		goto free_chainstack;
> -	}
> -
>   	table->private = newinfo;
>   	rwlock_init(&table->lock);
>   	mutex_lock(&ebt_mutex);


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [Bridge] [PATCH nf] netfilter: ebtables: reject blobs that don't provide all entry points
@ 2022-08-29 13:57     ` john.p.donnelly
  0 siblings, 0 replies; 17+ messages in thread
From: john.p.donnelly @ 2022-08-29 13:57 UTC (permalink / raw)
  To: Florian Westphal, netfilter-devel
  Cc: vegard.nossum, netdev, bridge, linux-kernel, george.kennedy,
	syzkaller, Harshit Mogalapalli

On 8/20/22 12:35 PM, Florian Westphal wrote:
> For some reason ebtables reject blobs that provide entry points that are
> not supported by the table.
> 
> What it should instead reject is the opposite, i.e. rulesets that
> DO NOT provide an entry point that is supported by the table.
> 
> t->valid_hooks is the bitmask of hooks (input, forward ...) that will
> see packets.  So, providing an entry point that is not support is
> harmless (never called/used), but the reverse is NOT, this will cause
> crash because the ebtables traverser doesn't expect a NULL blob for
> a location its receiving packets for.
> 
> Instead of fixing all the individual checks, do what iptables is doing and
> reject all blobs that doesn't provide the expected hooks.
> 
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Reported-by: Harshit Mogalapalli <harshit.m.mogalapalli@oracle.com>
> Signed-off-by: Florian Westphal <fw@strlen.de>

Hi,

  Could you please add the panic stack mentioned above  and syzkaller 
reproducer ID to the commit text ?



> ---
>   Harshit, can you check if this also silences your reproducer?
> 
>   Thanks!
> 
>   include/linux/netfilter_bridge/ebtables.h | 4 ----
>   net/bridge/netfilter/ebtable_broute.c     | 8 --------
>   net/bridge/netfilter/ebtable_filter.c     | 8 --------
>   net/bridge/netfilter/ebtable_nat.c        | 8 --------
>   net/bridge/netfilter/ebtables.c           | 8 +-------
>   5 files changed, 1 insertion(+), 35 deletions(-)
> 
> diff --git a/include/linux/netfilter_bridge/ebtables.h b/include/linux/netfilter_bridge/ebtables.h
> index a13296d6c7ce..fd533552a062 100644
> --- a/include/linux/netfilter_bridge/ebtables.h
> +++ b/include/linux/netfilter_bridge/ebtables.h
> @@ -94,10 +94,6 @@ struct ebt_table {
>   	struct ebt_replace_kernel *table;
>   	unsigned int valid_hooks;
>   	rwlock_t lock;
> -	/* e.g. could be the table explicitly only allows certain
> -	 * matches, targets, ... 0 == let it in */
> -	int (*check)(const struct ebt_table_info *info,
> -	   unsigned int valid_hooks);
>   	/* the data used by the kernel */
>   	struct ebt_table_info *private;
>   	struct nf_hook_ops *ops;
> diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c
> index 1a11064f9990..8f19253024b0 100644
> --- a/net/bridge/netfilter/ebtable_broute.c
> +++ b/net/bridge/netfilter/ebtable_broute.c
> @@ -36,18 +36,10 @@ static struct ebt_replace_kernel initial_table = {
>   	.entries	= (char *)&initial_chain,
>   };
>   
> -static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
> -{
> -	if (valid_hooks & ~(1 << NF_BR_BROUTING))
> -		return -EINVAL;
> -	return 0;
> -}
> -
>   static const struct ebt_table broute_table = {
>   	.name		= "broute",
>   	.table		= &initial_table,
>   	.valid_hooks	= 1 << NF_BR_BROUTING,
> -	.check		= check,
>   	.me		= THIS_MODULE,
>   };
>   
> diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c
> index cb949436bc0e..278f324e6752 100644
> --- a/net/bridge/netfilter/ebtable_filter.c
> +++ b/net/bridge/netfilter/ebtable_filter.c
> @@ -43,18 +43,10 @@ static struct ebt_replace_kernel initial_table = {
>   	.entries	= (char *)initial_chains,
>   };
>   
> -static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
> -{
> -	if (valid_hooks & ~FILTER_VALID_HOOKS)
> -		return -EINVAL;
> -	return 0;
> -}
> -
>   static const struct ebt_table frame_filter = {
>   	.name		= "filter",
>   	.table		= &initial_table,
>   	.valid_hooks	= FILTER_VALID_HOOKS,
> -	.check		= check,
>   	.me		= THIS_MODULE,
>   };
>   
> diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c
> index 5ee0531ae506..9066f7f376d5 100644
> --- a/net/bridge/netfilter/ebtable_nat.c
> +++ b/net/bridge/netfilter/ebtable_nat.c
> @@ -43,18 +43,10 @@ static struct ebt_replace_kernel initial_table = {
>   	.entries	= (char *)initial_chains,
>   };
>   
> -static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
> -{
> -	if (valid_hooks & ~NAT_VALID_HOOKS)
> -		return -EINVAL;
> -	return 0;
> -}
> -
>   static const struct ebt_table frame_nat = {
>   	.name		= "nat",
>   	.table		= &initial_table,
>   	.valid_hooks	= NAT_VALID_HOOKS,
> -	.check		= check,
>   	.me		= THIS_MODULE,
>   };
>   
> diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
> index f2dbefb61ce8..9a0ae59cdc50 100644
> --- a/net/bridge/netfilter/ebtables.c
> +++ b/net/bridge/netfilter/ebtables.c
> @@ -1040,8 +1040,7 @@ static int do_replace_finish(struct net *net, struct ebt_replace *repl,
>   		goto free_iterate;
>   	}
>   
> -	/* the table doesn't like it */
> -	if (t->check && (ret = t->check(newinfo, repl->valid_hooks)))
> +	if (repl->valid_hooks != t->valid_hooks)
>   		goto free_unlock;
>   
>   	if (repl->num_counters && repl->num_counters != t->private->nentries) {
> @@ -1231,11 +1230,6 @@ int ebt_register_table(struct net *net, const struct ebt_table *input_table,
>   	if (ret != 0)
>   		goto free_chainstack;
>   
> -	if (table->check && table->check(newinfo, table->valid_hooks)) {
> -		ret = -EINVAL;
> -		goto free_chainstack;
> -	}
> -
>   	table->private = newinfo;
>   	rwlock_init(&table->lock);
>   	mutex_lock(&ebt_mutex);


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH nf] netfilter: ebtables: reject blobs that don't provide all entry points
  2022-08-29 13:57     ` [Bridge] " john.p.donnelly
@ 2022-08-29 14:03       ` Florian Westphal
  -1 siblings, 0 replies; 17+ messages in thread
From: Florian Westphal @ 2022-08-29 14:03 UTC (permalink / raw)
  To: john.p.donnelly
  Cc: Florian Westphal, netfilter-devel, syzkaller, george.kennedy,
	vegard.nossum, bridge, netdev, linux-kernel, Harshit Mogalapalli

john.p.donnelly@oracle.com <john.p.donnelly@oracle.com> wrote:
> On 8/20/22 12:35 PM, Florian Westphal wrote:
> > For some reason ebtables reject blobs that provide entry points that are
> > not supported by the table.
> > 
> > What it should instead reject is the opposite, i.e. rulesets that
> > DO NOT provide an entry point that is supported by the table.
> > 
> > t->valid_hooks is the bitmask of hooks (input, forward ...) that will
> > see packets.  So, providing an entry point that is not support is
> > harmless (never called/used), but the reverse is NOT, this will cause
> > crash because the ebtables traverser doesn't expect a NULL blob for
> > a location its receiving packets for.
> > 
> > Instead of fixing all the individual checks, do what iptables is doing and
> > reject all blobs that doesn't provide the expected hooks.
> > 
> > Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> > Reported-by: Harshit Mogalapalli <harshit.m.mogalapalli@oracle.com>
> > Signed-off-by: Florian Westphal <fw@strlen.de>
> 
> Hi,
> 
>  Could you please add the panic stack mentioned above  and syzkaller
> reproducer ID to the commit text ?

I did not see a reproducer ID.  What ended up in the tree is this:

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=7997eff82828304b780dc0a39707e1946d6f1ebf

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [Bridge] [PATCH nf] netfilter: ebtables: reject blobs that don't provide all entry points
@ 2022-08-29 14:03       ` Florian Westphal
  0 siblings, 0 replies; 17+ messages in thread
From: Florian Westphal @ 2022-08-29 14:03 UTC (permalink / raw)
  To: john.p.donnelly
  Cc: vegard.nossum, netdev, bridge, Florian Westphal, linux-kernel,
	george.kennedy, syzkaller, netfilter-devel, Harshit Mogalapalli

john.p.donnelly@oracle.com <john.p.donnelly@oracle.com> wrote:
> On 8/20/22 12:35 PM, Florian Westphal wrote:
> > For some reason ebtables reject blobs that provide entry points that are
> > not supported by the table.
> > 
> > What it should instead reject is the opposite, i.e. rulesets that
> > DO NOT provide an entry point that is supported by the table.
> > 
> > t->valid_hooks is the bitmask of hooks (input, forward ...) that will
> > see packets.  So, providing an entry point that is not support is
> > harmless (never called/used), but the reverse is NOT, this will cause
> > crash because the ebtables traverser doesn't expect a NULL blob for
> > a location its receiving packets for.
> > 
> > Instead of fixing all the individual checks, do what iptables is doing and
> > reject all blobs that doesn't provide the expected hooks.
> > 
> > Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> > Reported-by: Harshit Mogalapalli <harshit.m.mogalapalli@oracle.com>
> > Signed-off-by: Florian Westphal <fw@strlen.de>
> 
> Hi,
> 
>  Could you please add the panic stack mentioned above  and syzkaller
> reproducer ID to the commit text ?

I did not see a reproducer ID.  What ended up in the tree is this:

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=7997eff82828304b780dc0a39707e1946d6f1ebf

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH nf] netfilter: ebtables: reject blobs that don't provide all entry points
  2022-08-29 14:03       ` [Bridge] " Florian Westphal
@ 2022-08-29 14:10         ` john.p.donnelly
  -1 siblings, 0 replies; 17+ messages in thread
From: john.p.donnelly @ 2022-08-29 14:10 UTC (permalink / raw)
  To: Florian Westphal
  Cc: netfilter-devel, syzkaller, george.kennedy, vegard.nossum,
	bridge, netdev, linux-kernel, Harshit Mogalapalli

On 8/29/22 9:03 AM, Florian Westphal wrote:
> john.p.donnelly@oracle.com <john.p.donnelly@oracle.com> wrote:
>> On 8/20/22 12:35 PM, Florian Westphal wrote:
>>> For some reason ebtables reject blobs that provide entry points that are
>>> not supported by the table.
>>>
>>> What it should instead reject is the opposite, i.e. rulesets that
>>> DO NOT provide an entry point that is supported by the table.
>>>
>>> t->valid_hooks is the bitmask of hooks (input, forward ...) that will
>>> see packets.  So, providing an entry point that is not support is
>>> harmless (never called/used), but the reverse is NOT, this will cause
>>> crash because the ebtables traverser doesn't expect a NULL blob for
>>> a location its receiving packets for.
>>>
>>> Instead of fixing all the individual checks, do what iptables is doing and
>>> reject all blobs that doesn't provide the expected hooks.
>>>
>>> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
>>> Reported-by: Harshit Mogalapalli <harshit.m.mogalapalli@oracle.com>
>>> Signed-off-by: Florian Westphal <fw@strlen.de>
>>
>> Hi,
>>
>>   Could you please add the panic stack mentioned above  and syzkaller
>> reproducer ID to the commit text ?
> 
> I did not see a reproducer ID.  What ended up in the tree is this:
> 
> https://urldefense.com/v3/__https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=7997eff82828304b780dc0a39707e1946d6f1ebf__;!!ACWV5N9M2RV99hQ!JxonjgQUi7Mbcd-ouxRwPgu8Jwl6ej2rO4pTvYMtteWexclV5-hciu9e5rgtkXoB7dyAdLCyZ4EQ9HQj$

Thank you !



^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [Bridge] [PATCH nf] netfilter: ebtables: reject blobs that don't provide all entry points
@ 2022-08-29 14:10         ` john.p.donnelly
  0 siblings, 0 replies; 17+ messages in thread
From: john.p.donnelly @ 2022-08-29 14:10 UTC (permalink / raw)
  To: Florian Westphal
  Cc: vegard.nossum, netdev, bridge, linux-kernel, george.kennedy,
	syzkaller, netfilter-devel, Harshit Mogalapalli

On 8/29/22 9:03 AM, Florian Westphal wrote:
> john.p.donnelly@oracle.com <john.p.donnelly@oracle.com> wrote:
>> On 8/20/22 12:35 PM, Florian Westphal wrote:
>>> For some reason ebtables reject blobs that provide entry points that are
>>> not supported by the table.
>>>
>>> What it should instead reject is the opposite, i.e. rulesets that
>>> DO NOT provide an entry point that is supported by the table.
>>>
>>> t->valid_hooks is the bitmask of hooks (input, forward ...) that will
>>> see packets.  So, providing an entry point that is not support is
>>> harmless (never called/used), but the reverse is NOT, this will cause
>>> crash because the ebtables traverser doesn't expect a NULL blob for
>>> a location its receiving packets for.
>>>
>>> Instead of fixing all the individual checks, do what iptables is doing and
>>> reject all blobs that doesn't provide the expected hooks.
>>>
>>> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
>>> Reported-by: Harshit Mogalapalli <harshit.m.mogalapalli@oracle.com>
>>> Signed-off-by: Florian Westphal <fw@strlen.de>
>>
>> Hi,
>>
>>   Could you please add the panic stack mentioned above  and syzkaller
>> reproducer ID to the commit text ?
> 
> I did not see a reproducer ID.  What ended up in the tree is this:
> 
> https://urldefense.com/v3/__https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=7997eff82828304b780dc0a39707e1946d6f1ebf__;!!ACWV5N9M2RV99hQ!JxonjgQUi7Mbcd-ouxRwPgu8Jwl6ej2rO4pTvYMtteWexclV5-hciu9e5rgtkXoB7dyAdLCyZ4EQ9HQj$

Thank you !



^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2022-08-29 14:10 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-20  7:03 [PATCH] netfilter: ebtables: fix a NULL pointer dereference in ebt_do_table() Harshit Mogalapalli
2022-08-20  7:03 ` [Bridge] " Harshit Mogalapalli
2022-08-20 16:26 ` Florian Westphal
2022-08-20 16:26   ` [Bridge] " Florian Westphal
2022-08-20 19:18   ` [External] : " Harshit Mogalapalli
2022-08-20 19:18     ` [Bridge] " Harshit Mogalapalli
2022-08-20 17:35 ` [PATCH nf] netfilter: ebtables: reject blobs that don't provide all entry points Florian Westphal
2022-08-20 17:35   ` [Bridge] " Florian Westphal
2022-08-20 19:20   ` [External] : " Harshit Mogalapalli
2022-08-20 19:20     ` [Bridge] " Harshit Mogalapalli
2022-08-22 10:55   ` John Donnelly
2022-08-29 13:57   ` john.p.donnelly
2022-08-29 13:57     ` [Bridge] " john.p.donnelly
2022-08-29 14:03     ` Florian Westphal
2022-08-29 14:03       ` [Bridge] " Florian Westphal
2022-08-29 14:10       ` john.p.donnelly
2022-08-29 14:10         ` [Bridge] " john.p.donnelly

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.