I have just made a driver program, and when I execute make, it said
make error aliased to undefined symbol devone_init
I find many sites and cannot find the same error.Because I am new to
device programing , I am not clear about init_module.
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/sched.h>
#include <asm/current.h>
#include <asm/uaccess.h>
MODULE_LICENCE("Dual BSD/GPL");
#define DRIVER_NAME "devone";
static int devone_devs = 2;
static int devone_major = 0;
module_param(devone_major. uint, 0);
static struct cdev devone_cdev;
static int devone_open(struct inode *inode, struct file *file)
{
printk("%s: major %d minor %d (pid %d)\n", __func__, imajor(inode), iminor(inode), current->pid);
inode->i_private = inode;
file->private_data = file;
printk("i_private=%p private_data=%p\n", inode->i_private, file->private_data);
return 0;
}
static int devone_close(struct inode *inode, struct file *file)
{
printk("%s: major %d minor %d (pid %d)\n", __func__, imajor(inode), iminor(inode), current->pid);
inode->i_private = inode;
file->private_data = file;
printk("i_private=%p private_data=%p\n", inode->i_private, file->private_data);
return 0;
}
struct file_operations devone_fops = {
.open = devone_opens;
.release = devone_close;
}
static int devone_init(void)
{
dev_t dev = MKDEV(devone_major, 0);
int alloc_ret = 0;
int major;
int cdev_err = 0;
alloc_ret = alloc_chrdev_region(&dev, 0, devone_devs, DRIVER_NAME);
if(alloc_ret)
goto error;
devone_major = major = MAJOR(dev);
cdev_init(&devone_cdev, &devone_fops);
devone_cdev.owner = THIS_MODULE;
cdev_err = cdev_add(&devone_cdev, MKDEV(devone_major, 0), devone_devs);
if(cdev_err)
goto error;
printk(KERN_ALERT "%s driver(%d) loaded\n", DRIVER_NAME, major);
return 0;
error:
if(cdev_err == 0)
cdev_del(devone_cdev);
if(alloc_ret == 0)
unregister_chrdev_region(dev, devone_devs);
return -1;
}
static void devone_exit(void)
{
dev_t dev = MKDEV(devone_major, 0);
cdev_del(&devone_cdev);
cdev_del(&devone_cdev);
unregister_chrdev_region(dev, devone_devs);
printk(KERN_ALERT "%s driver unloaded\n", DRIVER_NAME);
}
module_init(devone_init);
module_exit(devone_exit);
here is my makefile.When I add cflags , it returned cflags was changed.
#CFLAGS += -Wall
CFILES = devone.c
obj-m += sample.o
KDIR =/lib/modules/$(shell uname -r)/build
sample-objs := $(CFILES:.c=.o)
all:
$(MAKE) -C $(KDIR) M=$(shell pwd) modules
clean:
rm -rf *.o *.ko *.mod.* *.symvers *.order
There are several minute problems in the code which is preventing it from compilation. Modified code which compiles properly is below. you can compare and find out the diff.
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/sched.h>
#include <asm/current.h>
#include <asm/uaccess.h>
#define DRIVER_NAME "devone"
static int devone_devs = 2;
static int devone_major = 0;
module_param(devone_major, uint, 0);
static struct cdev devone_cdev;
static int devone_open(struct inode *inode, struct file *file)
{
printk("%s: major %d minor %d (pid %d)\n", __func__, imajor(inode), iminor(inode), current->pid);
inode->i_private = inode;
file->private_data = file;
printk("i_private=%p private_data=%p\n", inode->i_private, file->private_data);
return 0;
}
static int devone_close(struct inode *inode, struct file *file)
{
printk("%s: major %d minor %d (pid %d)\n", __func__, imajor(inode), iminor(inode), current->pid);
inode->i_private = inode;
file->private_data = file;
printk("i_private=%p private_data=%p\n", inode->i_private, file->private_data);
return 0;
}
struct file_operations devone_fops = {
.open = devone_open,
.release = devone_close,
};
static int devone_init(void)
{
dev_t dev = MKDEV(devone_major, 0);
int alloc_ret = 0;
int major;
int cdev_err = 0;
alloc_ret = alloc_chrdev_region(&dev, 0, devone_devs, DRIVER_NAME);
if(alloc_ret)
goto error;
devone_major = major = MAJOR(dev);
cdev_init(&devone_cdev, &devone_fops);
devone_cdev.owner = THIS_MODULE;
cdev_err = cdev_add(&devone_cdev, MKDEV(devone_major, 0), devone_devs);
if(cdev_err)
goto error;
printk(KERN_ALERT "%s driver(%d) loaded\n", DRIVER_NAME, major);
return 0;
error:
if(cdev_err == 0)
cdev_del(&devone_cdev);
if(alloc_ret == 0)
unregister_chrdev_region(dev, devone_devs);
return -1;
}
static void devone_exit(void)
{
dev_t dev = MKDEV(devone_major, 0);
cdev_del(&devone_cdev);
cdev_del(&devone_cdev);
unregister_chrdev_region(dev, devone_devs);
printk(KERN_ALERT "%s driver unloaded\n", DRIVER_NAME);
}
module_init(devone_init);
module_exit(devone_exit);
MODULE_LICENSE("Dual BSD/GPL");
Related
I wrote a simple ebpf so I opened it with obj = bpf_object__open_file(filename, NULL);
then when I do prog = bpf_object__find_program_by_name(obj, "kprobe/__x64_sys_write");
This function returns NULL and prints message that printf("finding a prog in obj file failed\n");. so basically my function or program could not be found in object file, I like to know what might be the reason for this so I also found after the call to bpf_object__open_file my errno got set to 2 so I like to know what is exactly happening why my errno set to 2 right after bpf_object__open_file and I made sure that file does exists
this condition is also not fullfilling
if (libbpf_get_error(obj)) {
printf("ERROR: opening BPF object file failed\n");
This is a loader program
// SPDX-License-Identifier: GPL-2.0
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <sys/prctl.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include <sys/resource.h>
#include <errno.h>
//#include "trace_helpers.h"
#ifdef __mips__
#define MAX_ENTRIES 6000 /* MIPS n64 syscalls start at 5000 */
#else
#define MAX_ENTRIES 1024
#endif
/* install fake seccomp program to enable seccomp code path inside the kernel,
* so that our kprobe attached to seccomp_phase1() can be triggered
*/
static void install_accept_all_seccomp(void)
{
struct sock_filter filter[] = {
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
};
struct sock_fprog prog = {
.len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
.filter = filter,
};
if (prctl(PR_SET_SECCOMP, 2, &prog))
perror("prctl");
}
int main(int ac, char **argv)
{
struct bpf_link *link = NULL;
struct bpf_program *prog;
struct bpf_object *obj;
int key, fd, progs_fd;
const char *section;
char filename[256]="/home/fawad/bpf/linux-5.13.1/samples/bpf/kern5.o";
printf("%s\n",filename);
FILE *f;
//snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
printf("errono before %d\n",errno);
obj = bpf_object__open_file(filename, NULL);
printf("errono after %d\n",errno);
if (libbpf_get_error(obj)) {
printf("ERROR: opening BPF object file failed\n");
return 0;
}
printf("errno before %d\n",errno);
prog = bpf_object__find_program_by_name(obj, "kprobe/__x64_sys_write");
printf("errno after %d\n",errno);
if (!prog) {
printf("finding a prog in obj file failed\n");
goto cleanup;
}
/* load BPF program */
if (bpf_object__load(obj)) {
fprintf(stderr, "ERROR: loading BPF object file failed\n");
goto cleanup;
}
link = bpf_program__attach(prog);
if (libbpf_get_error(link)) {
fprintf(stderr, "ERROR: bpf_program__attach failed\n");
link = NULL;
goto cleanup;
}
progs_fd = bpf_object__find_map_fd_by_name(obj, "progs");
if (progs_fd < 0) {
fprintf(stderr, "ERROR: finding a map in obj file failed\n");
goto cleanup;
}
bpf_object__for_each_program(prog, obj) {
section = bpf_program__section_name(prog);
/* register only syscalls to PROG_ARRAY */
if (sscanf(section, "kprobe/%d", &key) != 1)
continue;
fd = bpf_program__fd(prog);
bpf_map_update_elem(progs_fd, &key, &fd, BPF_ANY);
}
install_accept_all_seccomp();
f = popen("dd if=/dev/zero of=/dev/null count=5", "r");
(void) f;
// read_trace_pipe();
cleanup:
bpf_link__destroy(link);
bpf_object__close(obj);
return 0;
}
and this is ebpf program
/* Copyright (c) 2015 PLUMgrid, http://plumgrid.com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*/
#include <linux/ptrace.h>
#include <linux/version.h>
#include <linux/bpf.h>
#include <linux/seccomp.h>
#include <linux/unistd.h>
//#include "syscall_nrs.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#define PROG(F) SEC("kprobe/"__stringify(F)) int bpf_func_##F
struct {
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
__uint(key_size, sizeof(int));
__uint(value_size, sizeof(int));
#ifdef __mips__
__uint(max_entries, 6000); /* MIPS n64 syscalls start at 5000 */
#else
__uint(max_entries, 1024);
#endif
} progs SEC(".maps");
/*SEC("kprobe/SYS__NR_write")
int bpf_prog2(struct pt_regs *ctx)
{
struct seccomp_data sd;
bpf_probe_read_kernel(&sd, sizeof(sd), (void *)PT_REGS_PARM2(ctx));
if (sd.args[2] == 512) {
char fmt[] = "write(fd=%d, buf=%p, size=%d)\n";
bpf_trace_printk(fmt, sizeof(fmt),
sd.args[0], sd.args[1], sd.args[2]);
}
return 0;
}*/
SEC("kprobe/__seccomp_filter")
int bpf_prog1(struct pt_regs *ctx)
{
int sc_nr = (int)PT_REGS_PARM1(ctx);
/* dispatch into next BPF program depending on syscall number */
bpf_tail_call(ctx, &progs, sc_nr);
/* fall through -> unknown syscall */
if (sc_nr >= __NR_getuid && sc_nr <= __NR_getsid) {
char fmt[] = "syscall=%d (one of get/set uid/pid/gid)\n";
bpf_trace_printk(fmt, sizeof(fmt), sc_nr);
}
return 0;
}
SEC("kprobe/__x64_sys_write")
/* we jump here when syscall number == __NR_write */
//PROG(SYS__NR_write)(struct pt_regs *ctx)
int __x64_sys_write(struct pt_regs *ctx)
{
struct seccomp_data sd;
bpf_probe_read_kernel(&sd, sizeof(sd), (void *)PT_REGS_PARM2(ctx));
if (sd.args[2] == 512) {
char fmt[] = "write(fd=%d, buf=%p, size=%d)\n";
bpf_trace_printk(fmt, sizeof(fmt),
sd.args[0], sd.args[1], sd.args[2]);
}
return 0;
}
/*
PROG(SYS__NR_read)(struct pt_regs *ctx)
{
struct seccomp_data sd;
bpf_probe_read_kernel(&sd, sizeof(sd), (void *)PT_REGS_PARM2(ctx));
if (sd.args[2] > 128 && sd.args[2] <= 1024) {
char fmt[] = "read(fd=%d, buf=%p, size=%d)\n";
bpf_trace_printk(fmt, sizeof(fmt),
sd.args[0], sd.args[1], sd.args[2]);
}
return 0;
}
#ifdef __NR_mmap2
PROG(SYS__NR_mmap2)(struct pt_regs *ctx)
{
char fmt[] = "mmap2\n";
bpf_trace_printk(fmt, sizeof(fmt));
return 0;
}
#endif
#ifdef __NR_mmap
PROG(SYS__NR_mmap)(struct pt_regs *ctx)
{
char fmt[] = "mmap\n";
bpf_trace_printk(fmt, sizeof(fmt));
return 0;
}
#endif
*/
char _license[] SEC("license") = "GPL";
int _version SEC("version") = LINUX_VERSION_CODE;
compiling userspace loader like
clang -L /usr/lib/usr/lib64/ -I /usr/include/ -I /usr/lib/usr/include/ tracex5_user.c -o user1.o -lbpf
compiling kernel ebpf program like
sudo clang -Wall -O2 -g -target bpf -I /usr/lib/usr/include -I /usr/include/x86_64-linux-gnu/ -I /usr/include/linux/ -c syscall_tp_kern.c -o kern5.o
Please try this command
bpftool prog load bpf_test.o /sys/fs/bpf/vfs_create
In my scenario, I got the error:
libbpf: failed to find BTF for extern 'PT_REGS_PARM2': -3
I was writing a code for my driver and i cannot check it because i cannot compile it. I tried to reinstall ubuntu and used all this commands on a clean OS but i faced the same problem so i suppose it's only a problem with library installation.
Code for my driver:
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h> // kmalloc()
#include <linux/uaccess.h> // copy_to/from_user()
#include <linux/proc_fs.h>
#include <linux/pid.h>
#include <linux/sched.h>
#include <linux/sched/signal.h>
#include <linux/netdevice.h>
#include <linux/device.h>
#define BUF_SIZE 1024
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("Stab linux module for operating system's lab");
MODULE_VERSION("1.0");
static int pid = 1;
static int struct_id = 1;
static struct proc_dir_entry *parent;
/*
** Function Prototypes
*/
static int __init lab_driver_init(void);
static void __exit lab_driver_exit(void);
/***************** Procfs Functions *******************/
static int open_proc(struct inode *inode, struct file *file);
static int release_proc(struct inode *inode, struct file *file);
static ssize_t read_proc(struct file *filp, char __user *buffer, size_t length,loff_t * offset);
static ssize_t write_proc(struct file *filp, const char *buff, size_t len, loff_t * off);
/*
** procfs operation sturcture
*/
static struct proc_ops proc_fops = {
.proc_open = open_proc,
.proc_read = read_proc,
.proc_write = write_proc,
.proc_release = release_proc
};
// net_device
static size_t write_net_device_struct(char __user *ubuf){
char buf[BUF_SIZE];
size_t len = 0;
static struct net_device *dev;
read_lock(&dev_base_lock);
dev = first_net_device(&init_net);
while(dev){
len += sprintf(buf+len, "found [%s]\n", dev->name);
len += sprintf(buf+len, "base_addr = %ld\n", dev->base_addr);
len += sprintf(buf+len, "mem_start = %ld\n", dev->mem_start);
len += sprintf(buf+len, "mem_end = %ld\n\n", dev->mem_end);
dev = next_net_device(dev);
}
read_unlock(&dev_base_lock);
if (copy_to_user(ubuf, buf, len)){
return -EFAULT;
}
return len;
}
// signal_struct
static size_t write_signal_struct(char __user *ubuf, struct task_struct *task_struct_ref){
char buf[BUF_SIZE];
size_t len = 0;
struct signal_struct *signalStruct = task_struct_ref->signal;
len += sprintf(buf, "live = %d\n", atomic_read(&(signalStruct->live)));
len += sprintf(buf+len, "nr_threads = %d\n", signalStruct->nr_threads);
len += sprintf(buf+len, "group_exit_code = %d\n", signalStruct->group_exit_code);
len += sprintf(buf+len, "notify_count = %d\n", signalStruct->notify_count);
len += sprintf(buf+len, "group_stop_count = %d\n", signalStruct->group_stop_count);
len += sprintf(buf+len, "flags = %d\n", signalStruct->flags);
len += sprintf(buf+len, "is_child_subreaper = %d\n", signalStruct->is_child_subreaper);
len += sprintf(buf+len, "has_child_subreaper = %d\n", signalStruct->has_child_subreaper);
if (copy_to_user(ubuf, buf, len)){
return -EFAULT;
}
return len;
}
/*
** Эта фануция будет вызвана, когда мы ОТКРОЕМ файл procfs
*/
static int open_proc(struct inode *inode, struct file *file)
{
printk(KERN_INFO "proc file opend.....\t");
return 0;
}
/*
** Эта фануция будет вызвана, когда мы ЗАКРОЕМ файл procfs
*/
static int release_proc(struct inode *inode, struct file *file)
{
printk(KERN_INFO "proc file released.....\n");
return 0;
}
/*
** Эта фануция будет вызвана, когда мы ПРОЧИТАЕМ файл procfs
*/
static ssize_t read_proc(struct file *filp, char __user *ubuf, size_t count, loff_t *ppos) {
char buf[BUF_SIZE];
int len = 0;
struct task_struct *task_struct_ref = get_pid_task(find_get_pid(pid), PIDTYPE_PID);
printk(KERN_INFO "proc file read.....\n");
if (*ppos > 0 || count < BUF_SIZE){
return 0;
}
if (task_struct_ref == NULL){
len += sprintf(buf,"task_struct for pid %d is NULL. Can not get any information\n",pid);
if (copy_to_user(ubuf, buf, len)){
return -EFAULT;
}
*ppos = len;
return len;
}
switch(struct_id){
default:
case 0:
len = write_net_device_struct(ubuf);
break;
case 1:
len = write_signal_struct(ubuf, task_struct_ref);
break;
}
*ppos = len;
return len;
}
/*
** Эта фануция будет вызвана, когда мы ЗАПИШЕМ в файл procfs
*/
static ssize_t write_proc(struct file *filp, const char __user *ubuf, size_t count, loff_t *ppos) {
int num_of_read_digits, c, a, b;
char buf[BUF_SIZE];
printk(KERN_INFO "proc file wrote.....\n");
if (*ppos > 0 || count > BUF_SIZE){
return -EFAULT;
}
if( copy_from_user(buf, ubuf, count) ) {
return -EFAULT;
}
num_of_read_digits = sscanf(buf, "%d %d", &a, &b);
if (num_of_read_digits != 2){
return -EFAULT;
}
struct_id = a;
pid = b;
c = strlen(buf);
*ppos = c;
return c;
}
/*
** Функция инициализации Модуля
*/
static int __init lab_driver_init(void) {
/* Создание директории процесса. Она будет создана в файловой системе "/proc" */
parent = proc_mkdir("lab",NULL);
if( parent == NULL )
{
pr_info("Error creating proc entry");
return -1;
}
/* Создание записи процесса в разделе "/proc/lab/" */
proc_create("struct_info", 0666, parent, &proc_fops);
pr_info("Device Driver Insert...Done!!!\n");
return 0;
}
/*
** Функция выхода из Модуля
*/
static void __exit lab_driver_exit(void)
{
/* Удаляет 1 запись процесса */
//remove_proc_entry("lab/struct_info", parent);
/* Удяление полностью /proc/lab */
proc_remove(parent);
pr_info("Device Driver Remove...Done!!!\n");
}
module_init(lab_driver_init);
module_exit(lab_driver_exit);
My makefile:
obj-m += driver.c
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
The problem i am facing:
sudo make
make -C /lib/modules/5.11.0-37-generic/build M= modules
make[1]: Entering directory '/usr/src/linux-headers-5.11.0-37-generic'
make[2]: *** No rule to make target 'arch/x86/tools/relocs_32.c', needed by 'arch/x86/tools/relocs_32.o'. Stop.
make[1]: *** [arch/x86/Makefile:211: archscripts] Error 2
make[1]: Leaving directory '/usr/src/linux-headers-5.11.0-37-generic'
make: *** [Makefile:4: all] Error 2
Also i installed a list of tools for developing drivers: sudo apt-get install libncurses-dev flex bison openssl libssl-dev dkms libelf-dev libudev-dev libpci-dev libiberty-dev autoconf and sudo apt-get install build-essential linux-headers-'uname -r'. So i don't know how to fix this problem and maybe you know.
I try to use libfuse (cuse) to create character device and play on it like with regular tty, all is fine till I use tcgetattr.
Unfortunately, termios.tcgetattr() always raise I/O error.
cusetest.c
#define FUSE_USE_VERSION 29
#define _FILE_OFFSET_BITS 64
#include <fuse/cuse_lowlevel.h>
#include <fuse/fuse_opt.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//#include <termios.h>
#include <linux/termios.h>
#include <unistd.h>
#include <stdlib.h>
#include <strings.h>
#include <errno.h>
#define LOG(...) do { fprintf(stderr, "DEBUG: "__VA_ARGS__); puts(""); } while (0)
static void cusetest_open(fuse_req_t req, struct fuse_file_info *fi) {
LOG("cusetest_open called\n");
fuse_reply_open(req, fi);
}
static void cusetest_read(fuse_req_t req, size_t size, off_t off, struct fuse_file_info *fi) {
LOG("cusetest_read called\n");
fuse_reply_buf(req, "Hello", size > 5 ? 5 : size);
}
static void cusetest_write(fuse_req_t req, const char *buf, size_t size, off_t off, struct fuse_file_info *fi) {
LOG("cusetest_write called, size: %lu bytes\n", size);
fuse_reply_write(req, size);
}
static void cusetest_ioctl(fuse_req_t req, int cmd, void *arg, struct fuse_file_info *fi, unsigned flags, const void *in_buf, size_t in_bufsz, size_t out_bufsz) {
LOG("cusetest_ioctl called, cmd: %d insize: %lu outsize: %lu\n", cmd, in_bufsz, out_bufsz);
struct termios oldtio;
int i;
oldtio.c_iflag = 1;
oldtio.c_oflag = 2;
oldtio.c_cflag = 3;
oldtio.c_lflag = 4;
for (i = 0; i < NCCS; ++i)
{
oldtio.c_cc[i] = i;
}
printf("NCCS:%ud\n\n", NCCS);
printf("c_iflag:%ud \n", oldtio.c_iflag);
printf("c_oflag:%ud\n", oldtio.c_oflag);
printf("c_cflag:%ud\n", oldtio.c_cflag);
printf("c_lflag:%ud\n", oldtio.c_lflag);
// printk("c_ispeed:%ud\n", oldtio.ispeed);
// printk("c_ospeed:%ud\n\n", oldtio.c_ospeed);
for (i = 0; i < NCCS; ++i)
{
printf("CC: %d\n", oldtio.c_cc[i]);
}
printf("\n");
fuse_reply_ioctl(req, 21506, &oldtio, sizeof(oldtio));
}
static const struct cuse_lowlevel_ops cusetest_clop = {
.open = cusetest_open,
.read = cusetest_read,
.write = cusetest_write,
.ioctl = cusetest_ioctl,
};
struct cuse_info2 {
unsigned int dev_major;
unsigned int dev_minor;
unsigned int dev_info_argc;
char ** dev_info_argv;
unsigned int flags;
};
// char * argv[] == char ** argv
int main(int argc, char** argv) {
// -f: run in foreground, -d: debug ouput
// Compile official example and use -h
const char* cusearg[] = {"test", "-f", "-d"};
const char* devarg[] = {"DEVNAME=ttyCUSE0" };
struct cuse_info ci;
memset(&ci, 0x00, sizeof(ci));
ci.flags = CUSE_UNRESTRICTED_IOCTL;
ci.dev_info_argc=1;
ci.dev_info_argv = devarg;
//int cuse_lowlevel_main(int argc, char *argv[], const struct cuse_info *ci, const struct cuse_lowlevel_ops *clop, void *userdata);
return cuse_lowlevel_main(3, (char**) &cusearg, &ci, &cusetest_clop, NULL);
}
Here I use the same code (structure) in kernel module, and all is fine:
ttymodule.c:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/termios.h>
#include <linux/errno.h>
#define DEVICE_NAME "ttytest"
#define CLASS_NAME "ttytest"
static int major;
static struct class* tty_class = NULL;
static struct device* tty_device = NULL;
static long fake_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg){
struct termios oldtio;
int i;
oldtio.c_iflag = 1;
oldtio.c_oflag = 2;
oldtio.c_cflag = 3;
oldtio.c_lflag = 4;
for (i = 0; i < NCCS; ++i)
{
oldtio.c_cc[i] = i;
}
printk(KERN_ALERT "ttytest: ioctl called: %d -> %ld \n", cmd_in, arg);
printk(KERN_ALERT "NCCS:%ud\n\n", NCCS);
printk(KERN_ALERT "c_iflag:%ud \n", oldtio.c_iflag);
printk(KERN_ALERT "c_oflag:%ud\n", oldtio.c_oflag);
printk(KERN_ALERT "c_cflag:%ud\n", oldtio.c_cflag);
printk(KERN_ALERT "c_lflag:%ud\n", oldtio.c_lflag);
//printk(KERN_ALERT "c_ispeed:%ud\n", oldtio.ispeed);
//printk(KERN_ALERT "c_ospeed:%ud\n\n", oldtio.c_ospeed);
for (i = 0; i < NCCS; ++i)
{
printk(KERN_ALERT "CC: %d\n", oldtio.c_cc[i]);
}
printk(KERN_ALERT "\n");
return cmd_in+1;
}
static struct file_operations fops =
{
.owner = THIS_MODULE,
.unlocked_ioctl = fake_ioctl
};
static int __init tty_init(void){
printk(KERN_INFO "ttytest: Initializing ...\n");
major = register_chrdev(0, DEVICE_NAME, &fops);
if (major<0){
printk(KERN_ALERT "ttytest failed to register a major number\n");
return major;
}
tty_class = class_create(THIS_MODULE, CLASS_NAME);
if (IS_ERR(tty_class)){
unregister_chrdev(major, DEVICE_NAME);
printk(KERN_ALERT "Failed to register device class\n");
return PTR_ERR(tty_class);
}
tty_device = device_create(tty_class, NULL, MKDEV(major, 0), NULL, DEVICE_NAME);
if (IS_ERR(tty_device)){
class_destroy(tty_class);
unregister_chrdev(major, DEVICE_NAME);
printk(KERN_ALERT "Failed to create the device\n");
return PTR_ERR(tty_device);
}
return 0;
}
static void __exit tty_exit(void){
device_destroy(tty_class, MKDEV(major, 0));
class_unregister(tty_class); // unregister the device class
class_destroy(tty_class); // remove the device class
unregister_chrdev(major, DEVICE_NAME); // unregister the major number
printk(KERN_INFO "ttytest: Goodbye ...\n");
}
module_init(tty_init);
module_exit(tty_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Grzegorz Hetman");
MODULE_DESCRIPTION("A simple tty module to test cuse implementation.");
MODULE_VERSION("0.1");
Makefile:
obj-m := ttymodule.o
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all: unload clean
$(MAKE) -C $(KERNELDIR) M=$(PWD)
#make load
#make cuse
#sudo ./cusetest
clean:
#rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c *.order *.symvers cusetest
load:
#sudo insmod ttymodule.ko
#sudo chmod 777 /dev/ttytest
#sudo lsmod |grep ttymodule
unload:
#sudo rmmod ttymodule || true
#sudo rm -f /dev/ttymodule
cuse:
#gcc -Wall -g cusetest.c -lfuse -o cusetest
Result(for eg in python):
import termios
termios.tcgetattr(open('/dev/ttyCUSE0','rw+')) # error: (5, 'Input/output error')
termios.tcgetattr(open('/dev/ttytest','rw+')) # [5523920, 0, 1576586344, 32702, 8, 8, ['\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '#', 'r', '\x90', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', 'g', '\x82', '\x01', '\x00', '\x00', '\x00', '\x00', ',', 'h', 'K', '\x00', '\x00', '\x00', '\x00', '\x00', '\x90']]
As far as I know, cuse drivers aren't like regular tty drivers because they aren't terminal drivers. They are user space character oriented file system drivers.
In order to play with it as you want, it should be under the tty components like below image (Taken from LDD3). Same source as the image describes how to create terminal derivers.
By the way, there is no userspace tty driver I know.
Im working on little kernel module. Im trying to use IOCTL (in ioctl_add), but I get ENOTTY when I call it, which is checked in switch, on the bottom of main. The code is below. Has anyone got any idea what am I doing wrong?
user.c:
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <linux/ioctl.h>
#include <sys/stat.h>
#include <sys/poll.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#define IOCTL_TYPE (100)
#define IOCTL_ADD (_IO(IOCTL_TYPE, 1))
void cleanup()
{
if(f>=0) {
close(f);
}
}
int ioctl_add(int f)
{
int ret;
ret = ioctl(f, IOCTL_ADD);
printf("Add \n");
return ret;
}
int main(int argc, char * argv[])
{
int fd;
int *ptr;
fd = open(argv[1], O_RDWR);
if (fd < 0) {
perror("error");
}
posix_memalign((void **)&ptr, 4096, 4096);
* ptr = atoi(argv[2]);
write(fd, ptr, 4096);
ioctl_add(fd);
printf("data is %d\n", *ptr);
close(fd);
switch(errno){
case EBADF:
printf("errno: EBADF \n");
break;
case EFAULT:
printf("errno: EFAULT \n");
break;
case EINVAL:
printf("errno: EINVAL \n");
break;
case ENOTTY:
printf("errno: ENOTTY \n");
break;
default:
printf("errno: none \n");
return 0;
}
return 0;
}
module.c:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/sched.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/cdev.h>
//#include <linux/mm.h>
//#include <linux/config.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/poll.h>
#include <asm/io.h>
#include <asm/bitops.h>
#include <linux/ioctl.h>
#define IOCTL_TYPE (100)
#define IOCTL_ADD (_IO(IOCTL_TYPE, 1))
#include <linux/mm.h>
#include <linux/pagemap.h>
#define DEVICE_NAME "acc_priv"
MODULE_LICENSE("GPL v2");
int ress, tmp;
struct page *page;
int *myaddr;
ssize_t acc_read(struct file *filp,
char __user *buf, size_t count,loff_t * off)
{
printk (KERN_ALERT "Opened\n\r");
return 0;
}
ssize_t acc_write(struct file *filp,
const char __user *buf, size_t count,loff_t * off)
{
printk (KERN_ALERT "Write\n\r");
printk(KERN_INFO "%s\n", __FUNCTION__);
down_read(¤t->mm->mmap_sem);
ress = get_user_pages(current, current->mm,(unsigned long)buf,1,1,1,&page,NULL);
if (ress) {
printk(KERN_INFO "Got mmaped.\n");
myaddr = kmap(page);
printk(KERN_INFO "%d\n", *myaddr);
tmp = *myaddr;
tmp = tmp * 2;
printk(KERN_INFO "the result of multiplying: %d\n", tmp);
* myaddr = tmp;
page_cache_release(page);
}
up_read(¤t->mm->mmap_sem);
return (0);
}
static int acc_open(struct inode *inode,
struct file *file)
{
printk(KERN_INFO "Opened inode:%p, file:%p\n", inode, file);
return 0;
}
long acc_ioctl(struct file *filp,
unsigned int cmd,unsigned long arg)
{
if(cmd == IOCTL_ADD)
printk(KERN_INFO "Do specified job \n");
return 0;
{
int acc_release(struct inode *inode,
struct file *file)
{
printk (KERN_INFO "device_release(%p,%p)\n", inode, file);
return 0;
}
struct file_operations Fops = {
.owner=THIS_MODULE,
.read=acc_read,
.write=acc_write,
.open=acc_open,
.unlocked_ioctl=acc_ioctl,
.release=acc_release,
};
dev_t my_dev=0;
struct cdev * my_cdev = NULL;
static struct class *class_acc_priv = NULL;
void clean_up(void)
{
if(my_dev && class_acc_priv) {
device_destroy(class_acc_priv,my_dev);
}
if(my_cdev) {
cdev_del(my_cdev);
my_cdev=NULL;
}
if(my_dev) {
unregister_chrdev_region(my_dev, 1);
}
if(class_acc_priv) {
class_destroy(class_acc_priv);
class_acc_priv=NULL;
}
}
int init_acc_priv(void)
{
int res=0;
res=alloc_chrdev_region(&my_dev, 0, 1, DEVICE_NAME);
if(res) {
printk (KERN_ALERT "Alocation of the device number for %s failed\n",
DEVICE_NAME);
return res;
};
class_acc_priv = class_create(THIS_MODULE, "acc_class");
if (IS_ERR(class_acc_priv)) {
printk(KERN_ERR "Error creating rs_class.\n");
res=PTR_ERR(class_acc_priv);
goto err1;
}
my_cdev = cdev_alloc( );
my_cdev->ops = &Fops;
my_cdev->owner = THIS_MODULE;
res=cdev_add(my_cdev, my_dev, 1);
if(res) {
printk (KERN_ALERT "Registration of the device number for %s failed\n",
DEVICE_NAME);
res=-EFAULT;
goto err1;
};
device_create(class_acc_priv,NULL,my_dev,NULL,"acc_priv%d",MINOR(my_dev));
printk (KERN_ALERT "%s The major device number is %d.\n",
"Registeration is a success.",
MAJOR(my_dev));
return res;
err1:
clean_up();
return res;
}
module_init(init_acc_priv);
void cleanup_acc_priv( void )
{
clean_up();
}
module_exit(cleanup_acc_priv);
When 32bit application is run on 64bit OS, it uses compat_ioctl syscall instead of unlocked_ioctl one for perform ioctl command. The reason of special syscall is that size of ioctl argument may differ for 64bit and 32bit applications.
So you need to implement .compat_ioctl file operation.
I tried code(Using Linux Kernel Crypto APIs) for calculating HMAC-SHA512 on a file.
Surprisingly, HMAC calculated from the kernel code and from OpenSSL commands are different.
OpenSSL commands are:
openssl rand -base64 16
plakOhrXlfnBENPVBo91kg==
openssl dgst -sha512 -mac hmac -macopt key:plakOhrXlfnBENPVBo91kg== ../hello6.ko
HMAC-SHA512(../hello6.ko)= 9605f40851630f5b7a18fa30c7e5d6f1e77afb011d319efa515556d12ba6930f459825b3695a0d0d910a714724c0d99b36ccea5c878962b32a8de65dcbcc247d
HMAC-SHA512 from the kernel code is:
84306723b713379aa666fe9aa75af3192a9707d19136c25dd2286c5f6d86dfd8f76ceaa0ce147b53c0e9f7bfab485f38f5139c687c34c840db7f7fa9438bf8b0d8df8e770088ffffc8de8e770088ffffc3f027a0ffffffff672c00000088fffff0de8e770088ffff1900000000000080672c000000000000e8de8e770088ffff
How come these HMACs are different for the same content? Any pointers to get rid of this issue is very helpful.
Base64 encoded key is generated by below command and stored in a file:
openssl rand -base64 -out $Module_name.sha512key 16
HMAC is generated on a file and stored in a file.
openssl dgst -sha512 -mac hmac -macopt key:$HMAC_KEY $HMAC_PATH/$Module_name> temp.txt
Key and File content are read by below code:
static char hmac[HMAC_SHA512_SIZE];
const char *kofilename = "/home/sri/Documents/Hello/hello6.ko";
const char *hmackey_file = "/home/sri/Documents/Hello/hello6.ko.sha512key";
const char *hmac_file = "/home/sri/Documents/Hello/hello6.ko.sha512";
unsigned char *data = NULL;
int readkostatus;
int readkeystatus;
int readhmacstatus;
unsigned char *hmackey = NULL;
unsigned char *stored_hmac = NULL;
readkeystatus = read_file(hmackey_file, &hmackey);
if(readkeystatus < 0)
goto readkeyerr;
printk(KERN_INFO "HMAC Key is :%s", hmackey);
readkostatus = read_kofile(kofilename, &data);
if(readkostatus < 0)
goto readkoerr;
printk(KERN_INFO "File data size is :%ld", strlen(data));
printk(KERN_INFO "File data is :%s", data);
hmac_sha512(hmackey, data, hmac, sizeof(hmac));
printk(KERN_INFO "FINAL HMAC:%s", hmac);
readhmacstatus = read_file(hmac_file, &stored_hmac);
if(readhmacstatus < 0)
goto readhmacerr;
printk(KERN_INFO "Stored HMAC:%s", stored_hmac);
if(!memcmp(stored_hmac, hmac, HMAC_SHA512_SIZE))
printk(KERN_INFO "HMACs match");
else
printk(KERN_INFO "HMACs do not match");
vfree(stored_hmac);
vfree(data);
vfree(hmackey);
return 0;
readkeyerr:
{
printk(KERN_INFO "hmac key read error:%d", readkeystatus);
return readkeystatus;
}
readkoerr:
{
printk(KERN_INFO "ko read error:%d", readkostatus);
return readkostatus;
}
readhmacerr:
{
printk(KERN_INFO "hmac read error:%d", readhmacstatus);
return readhmacstatus;
}
Code for reading the files is given below:
int read_file(const char *filename, unsigned char **data)
{
struct file* filp = NULL;
long filesize;
int ret = 0;
mm_segment_t old_fs = get_fs();
set_fs(get_ds());
filp = filp_open(filename, O_RDONLY, 0);
if(IS_ERR(filp)) {
ret = PTR_ERR(filp);
printk(KERN_INFO "kofile is not opened");
}
else
{
loff_t offset;
offset = 0;
filesize = filp->f_dentry->d_inode->i_size;
if (filesize <= 0 || filesize > 131072)
{
goto fileoperror;
}
*data = (unsigned char *)vmalloc(filesize);
if (*data == NULL)
goto datanull;
ret = vfs_read(filp, *data, filesize, &offset);
if(ret != filesize)
goto read_error;
filp_close(filp, NULL);
}
set_fs(old_fs);
return ret;
fileoperror:
{
filp_close(filp, NULL);
set_fs(old_fs);
printk(KERN_INFO "Invalid file operation '%s'\n", filename);
return (-EPERM);
}
datanull:
{
filp_close(filp, NULL);
set_fs(old_fs);
printk(KERN_INFO "Data Buffer is not allocated");
return (-EFAULT);
}
read_error:
{
filp_close(filp, NULL);
set_fs(old_fs);
printk(KERN_INFO "Failed to read '%s'.\n", filename);
return (-EFBIG);
}
}
int read_kofile(const char* filename, unsigned char **data)
{
return read_file(filename, data);
}
HMAC-SHA-512 is calculated on the file by calling Crypto APIs:
#define HMAC_SHA512_SIZE 64
struct hmac_sha512_result {
struct completion completion;
int err;
};
static void hmac_sha512_complete(struct crypto_async_request *req, int err) {
struct hmac_sha512_result *r=req->data;
if(err==-EINPROGRESS)
return;
r->err=err;
complete(&r->completion);
}
int hmac_sha512(const unsigned char *key, // key
const unsigned char *data_in, // data in
unsigned char *hash_out, size_t outlen) { // hash buffer and length
int rc=0;
struct crypto_ahash *tfm;
struct scatterlist sg;
struct ahash_request *req;
struct hmac_sha512_result tresult;
void *hash_buf;
size_t klen = strlen(key);
size_t dlen = strlen(data_in);
int len = HMAC_SHA512_SIZE;
char hash_tmp[HMAC_SHA512_SIZE];
char *hash_res = hash_tmp;
printk(KERN_INFO "hmac_sha512: HMAC key is %s ", key);
/* Set hash output to 0 initially */
memset(hash_out, 0, outlen);
init_completion(&tresult.completion);
tfm=crypto_alloc_ahash("hmac(sha512)",0,0);
if(IS_ERR(tfm)) {
printk(KERN_ERR "hmac_sha512: crypto_alloc_ahash failed.\n");
rc=PTR_ERR(tfm);
goto err_tfm;
}
if(!(req=ahash_request_alloc(tfm,GFP_KERNEL))) {
printk(KERN_ERR "hmac_sha512: failed to allocate request for hmac(sha512)\n");
rc=-ENOMEM;
goto err_req;
}
if(crypto_ahash_digestsize(tfm)>len) {
printk(KERN_ERR "hmac_sha512: tfm size > result buffer.\n");
rc=-EINVAL;
goto err_req;
}
ahash_request_set_callback(req,CRYPTO_TFM_REQ_MAY_BACKLOG,
hmac_sha512_complete,&tresult);
if(!(hash_buf=kzalloc(dlen,GFP_KERNEL))) {
printk(KERN_ERR "hmac_sha512: failed to kzalloc hash_buf");
rc=-ENOMEM;
goto err_hash_buf;
}
memcpy(hash_buf,data_in,dlen);
sg_init_one(&sg,hash_buf,dlen);
crypto_ahash_clear_flags(tfm,-0);
if((rc=crypto_ahash_setkey(tfm,key,klen))){
printk(KERN_ERR "hmac_sha512: crypto_ahash_setkey failed\n");
goto err_setkey;
}
ahash_request_set_crypt(req,&sg,hash_res,dlen);
rc=crypto_ahash_digest(req);
switch(rc) {
case 0:
while (len--) {
snprintf(hash_out, outlen, "%02x", (*hash_res++ & 0x0FF));
hash_out += 2;
}
break;
case -EINPROGRESS:
case -EBUSY:
rc=wait_for_completion_interruptible(&tresult.completion);
if(!rc && !(rc=tresult.err)) {
INIT_COMPLETION(tresult.completion);
break;
} else {
printk(KERN_ERR "hmac_sha512: wait_for_completion_interruptible failed\n");
goto out;
}
default:
goto out;
}
out:
err_setkey:
kfree(hash_buf);
err_hash_buf:
ahash_request_free(req);
err_req:
crypto_free_ahash(tfm);
err_tfm:
return rc;
}
Please let me know, if something is missing.
crypto_req_done defined in hash.h
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/blkdev.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/device.h>
#include <linux/dmi.h>
#include <linux/gfp.h>
#include <linux/msi.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_cmnd.h>
#include <linux/libata.h>
#include <linux/ahci-remap.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <net/sock.h>
#include <linux/fs.h>
#include <asm/segment.h>
#include <linux/buffer_head.h>
#include <linux/libata.h>
#include <asm/uaccess.h>
#include <asm/traps.h>
#include <linux/cdev.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/sched/signal.h>
#include <linux/slab.h>
#include <linux/kthread.h>
#include <linux/hashtable.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/signal_types.h>
#include <linux/fcntl.h>
#include <linux/fs.h>
#include <linux/wait.h>
#include <linux/slab.h>
#include <linux/crypto.h>
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
#define TESTMGR_POISON_BYTE 0xfe
static int do_ahash_op(int (*op)(struct ahash_request *req), struct ahash_request *req, struct crypto_wait *wait)
{
int err;
err = op(req);
return crypto_wait_req(err, wait);
}
static int check_nonfinal_ahash_op(const char *op, int err){
if(err < 0){
pr_err("alg: ahash: %s() failed with err %d on test vector %s\n", op, err, get_error(err));
return 1;
}
return 0;
}
now hmac_sha256:
int hmac_sha256(unsigned char *key, size_t key_size, unsigned char *ikm, size_t ikm_len, unsigned char *okm, size_t okm_len){
int rc, key_is_null;
struct crypto_ahash *tfm;
struct ahash_request *req;
struct scatterlist pending_sgl;
DECLARE_CRYPTO_WAIT(wait);
if(key == NULL){
key = kzalloc(32, GFP_KERNEL);
key_size = 32;
key_is_null = 1;
}else{key_is_null = 0;}
tfm = crypto_alloc_ahash("hmac(sha256)", 0, 0);
if(IS_ERR(tfm)) {
rc=PTR_ERR(tfm);
printk(KERN_ERR "hmac_sha256: crypto_alloc_ahash failed(%s).\n", get_error(rc));
goto err_tfm;
}
if(!(req = ahash_request_alloc(tfm, GFP_KERNEL))) {
rc =- ENOMEM;
printk(KERN_ERR "hmac_sha256: failed to allocate request for hmac(sha512)(%s).\n", get_error(rc));
}
if(key_size){
if((rc = crypto_ahash_setkey(tfm, key, key_size))){
printk(KERN_ERR "hmac_sha256: crypto_ahash_setkey failed.(%s)\n", get_error(rc));
goto out;
}
}
sg_init_table(&pending_sgl, 1);
memset(req->__ctx, TESTMGR_POISON_BYTE, crypto_ahash_reqsize(tfm));
ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, crypto_req_done, &wait);
ahash_request_set_crypt(req, NULL, okm, 0);
rc = do_ahash_op(crypto_ahash_init, req, &wait);
rc = check_nonfinal_ahash_op("crypto_ahash_init", rc);
if (rc)
goto out;
sg_set_buf(&pending_sgl, ikm, ikm_len);
ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, crypto_req_done, &wait);
ahash_request_set_crypt(req, &pending_sgl, okm, okm_len);
rc = do_ahash_op(crypto_ahash_update, req, &wait);
rc = check_nonfinal_ahash_op("crypto_ahash_update", rc);
if(rc)
goto out;
rc = do_ahash_op(crypto_ahash_final, req, &wait);
rc = check_nonfinal_ahash_op("crypto_ahash_final", rc);
if (rc)
goto out;
dump_memory(okm, "hmac_sha256", okm_len);
out:
ahash_request_free(req);
crypto_free_ahash(tfm);
err_tfm:
if(key_is_null)
kfree(key);
return rc;
}
Break A leg!
int hkdf_extract(unsigned char *salt, size_t salt_len, unsigned char *ikm, size_t ikm_len, unsigned char *okm, size_t okm_len){
return hmac_sha256(salt, salt_len, ikm, ikm_len, okm, okm_len);
}