Making tc Run

Semidragon·2023년 7월 18일
0

1. trace function calls (Bottom-up // Top-down)

https://velog.io/@brian11hwang/Tracing-mlx5esetuptc

2. Make kernel have userspace application run

2.1 Utilize call_usermodehelper()

#include <linux/kmod.h>

#device name and Gbit limit should be variables.

char *argv[] = { "/sbin/tc", "qdisc", "add", "dev", "enp6s0", "root", "handle", "1:", "htb", "default", "10", NULL };
static char* envp[] = {
    "HOME=/",
    "PATH=/sbin:/bin:/usr/sbin:/usr/bin",
    NULL 
};

call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);

Yet, when trying to test tc, the driver would die, due placing the code inside the __init functions, which:
1) states all initialization functions
2) runs them in thread workqueue

Thus, tc applications are executed before device is fully ready.

Therefore, we will first test with a simple logger:

	 char *argv[] = { "/usr/bin/logger", "help!", NULL };
	 static char *envp[] = {
			"HOME=/",
			"TERM=linux",
			"PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL };ㅓㅓ

	 return call_usermodehelper( argv[0], argv, envp, UMH_WAIT_PROC );

Or, we could also run .sh files

					    printk("IS AFTER ALL FINISH?");
					   	char *argv[] = { "/usr/bin/tc.sh", NULL };
						static char *envp[] = {
								"HOME=/",
								"TERM=linux",
								"PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL };	

	                    call_usermodehelper( argv[0], argv, envp, UMH_WAIT_PROC );

where .sh file is:

#!/bin/bash
sudo logger -p kern.notice "Test for call usermodhelper"

Therefore, once the backchannel is made, we can do something like:

#include <linux/kmod.h>

// Define interface and bandwidth limit
char *interface = "enp6s0";
char *bw_limit = "20Gbit";

// Define command arrays
char *argv1[] = { "/sbin/tc", "qdisc", "del", "dev", interface, "root", NULL };
char *argv2[] = { "/sbin/tc", "qdisc", "add", "dev", interface, "root", "handle", "1:", "htb", "default", "10", NULL };
char *argv3[] = { "/sbin/tc", "class", "add", "dev", interface, "parent", "1:", "classid", "1:1", "htb", "rate", bw_limit, NULL };
char *argv4[] = { "/sbin/tc", "class", "add", "dev", interface, "parent", "1:1", "classid", "1:10", "htb", "rate", bw_limit, NULL };

// Define environment
static char* envp[] = {
    "HOME=/",
    "PATH=/sbin:/bin:/usr/sbin:/usr/bin",
    NULL 
};

// Run the commands
call_usermodehelper(argv1[0], argv1, envp, UMH_WAIT_PROC);
call_usermodehelper(argv2[0], argv2, envp, UMH_WAIT_PROC);
call_usermodehelper(argv3[0], argv3, envp, UMH_WAIT_PROC);
call_usermodehelper(argv4[0], argv4, envp, UMH_WAIT_PROC);

which will run:

2.2 Create a user-space application (daemon) which monitors a sysfs or procfs file

This approach is to create a user-space application (daemon) which monitors a sysfs or procfs file, and when a change is noted, it will apply the tc command. The kernel module will write to the sysfs/procfs file to trigger the application.

  1. Kernel Space
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/module.h>

static ssize_t my_sysfs_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
{
    return count;
}

static struct kobj_attribute my_sysfs_attribute =
    __ATTR(my_sysfs_file, 0220, NULL, my_sysfs_store);

static struct kobject *my_kobject;

static int __init my_module_init (void)
{
    int error = 0;

    my_kobject = kobject_create_and_add("my_kobject", kernel_kobj);
    if(!my_kobject)
        return -ENOMEM;

    error = sysfs_create_file(my_kobject, &my_sysfs_attribute.attr);
    if (error) {
        printk("failed to create the my_sysfs_file file in /sys/kernel/my_kobject \n");
    }

    return error;
}

my_sysfs_store is a function that is called when data is written to the my_sysfs_file file in sysfs. The parameters are:

- kobj points to the struct kobject that the attribute belongs to.
- attr points to the struct kobj_attribute for the specific attribute that was written.
- buf points to the buffer that contains the written data.
- count is the number of bytes that were written.

my_sysfs_attribute is a struct of type struct kobj_attribute. The __ATTR macro is used to initialize it. The macro takes four arguments:

- The name of the attribute (my_sysfs_file).
- The mode of the attribute (0220, write permission for owner and group).
- A pointer to the show method for the attribute (not used here, set to NULL).
- A pointer to the store method for the attribute (my_sysfs_store).

  1. User-Space:
#!/bin/bash

interface="enp6s0"
bw_limit="20Gbit"

while inotifywait -e modify /sys/kernel/my_kobject/my_sysfs_file; do
    /sbin/tc qdisc del dev $interface root
    /sbin/tc qdisc add dev $interface root handle 1: htb default 10
    /sbin/tc class add dev $interface parent 1: classid 1:1 htb rate $bw_limit
    /sbin/tc class add dev $interface parent 1:1 classid 1:10 htb rate $bw_limit
done
profile
Semidragon's network [CS undergrad @ Sungkyunkwan University | Networks + System @ CSI]

1개의 댓글

comment-user-thumbnail
2023년 7월 18일

소중한 정보 감사드립니다!

답글 달기