How to use kernel libcrc32c (or same functions) in userspace programmes? - c

I want to do some CRC check in my own userspace programme. And I find that the kernel crypto lib is already in the system, and come with SSE4.2 support.
I tried to directly #include <linux/crc32c.h> and run gcc with -I/usr/src/linux/include/. However, it doesnot work.
Any way to use some kind of libcrc32c ?

You can use kernel crypto CRC32c (and other hash/cipher functions) from user-space via socket family AF_ALG on Linux:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/socket.h>
#include <linux/if_alg.h>
#include <sys/param.h>
#include <string.h>
#include <strings.h>
int
main (int argc, char **argv) {
int sds[2] = { -1, -1 };
struct sockaddr_alg sa = {
.salg_family = AF_ALG,
.salg_type = "hash",
.salg_name = "crc32c"
};
if ((sds[0] = socket(AF_ALG, SOCK_SEQPACKET, 0)) == -1 )
return -1;
if( bind(sds[0], (struct sockaddr *) &sa, sizeof(sa)) != 0 )
return -1;
if( (sds[1] = accept(sds[0], NULL, 0)) == -1 )
return -1;
char *s = "hello";
size_t n = strlen(s);
if (send(sds[1], s, n, MSG_MORE) != n)
return -1;
int crc32c = 0x00000000;
if(read(sds[1], &crc32c, 4) != 4)
return -1;
printf("%08X\n", crc32c);
return 0;
}
If you're hashing files or socket data you can speed it up using zero-copy approach to avoid kernel -> user-space buffer copy with sendfile and/or splice.
Happy coding.

Related

Different results when running inside a docker container

I am trying to run some code based on this libaio sample:
https://oxnz.github.io/2016/10/13/linux-aio/#example-1
I added the O_DIRECT flag according to libaio's documentation.
It seems to work inside my ubuntu 16.04 desktop machine (hello is written to /tmp/test).
However, when I compile and run the same sample inside a docker container nothing is written to the file. when running inside gdb I can see that an event is read by io_getevents and the result is set to -22 (EINVAL).
Any ideas?
This is my modified code
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <libaio.h>
int main() {
io_context_t ctx;
struct iocb iocb;
struct iocb * iocbs[1];
struct io_event events[1];
struct timespec timeout;
int fd;
fd = open("/tmp/test", O_WRONLY | O_CREAT | O_DIRECT) ;
if (fd < 0) err(1, "open");
memset(&ctx, 0, sizeof(ctx));
if (io_setup(10, &ctx) != 0) err(1, "io_setup");
const char *msg = "hello";
io_prep_pwrite(&iocb, fd, (void *)msg, strlen(msg), 0);
iocb.data = (void *)msg;
iocbs[0] = &iocb;
if (io_submit(ctx, 1, iocbs) != 1) {
io_destroy(ctx);
err(1, "io_submit");
}
while (1) {
timeout.tv_sec = 0;
timeout.tv_nsec = 500000000;
int ret = io_getevents(ctx, 0, 1, events, &timeout);
printf("ret=%d\n", ret);
if (ret == 1) {
close(fd);
break;
}
printf("not done yet\n");
sleep(1);
}
io_destroy(ctx);
return 0;
}
The filesystem inside the container is likely to be different to that of the host's filesystem (on modern setups is likely to be overlayfs but on older systems it could be aufs). For O_DIRECT on an open to work a device/filesystem has to at least "support" it (note the scare quotes) and it's likely your container's filesystem does not.

System call poll() does not work on Ubuntu Kylin 16.04 32 bit version, but works on Fedora 21 64 bit

This is a short version of my code: I have a 64 bit fedora 21 on which the code works fine. However on my other 32bit machine on which I have installed ubuntu Kylin 16.04 32 bit version, poll does not return when text is entered on stdin, it just stays blocked. When I hit Ctrl-C in gdb after the text is entered, and then try executing next command it works... Is this a bug of ubuntu 32 bit version or I have to use poll differently on 32 bit version?
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int
main(int argc, char **argv)
{
int sockfd;
int const myOPEN_MAX=100,MAXLINE=4096;
struct pollfd client[myOPEN_MAX];
int maxi,nready,n;
char buf[4096];
client[0].fd = fileno(stdin);
client[0].events = POLLRDNORM;
for (int i = 1; i < myOPEN_MAX; i++){
client[i].fd = -1; /* -1 indicates available entry */
}
maxi = 1; /* max index into client[] array */
printf("enter something\n");
for ( ; ; ) {
nready = poll(client, maxi+1, -1);
for (int i = 0; i <= maxi; i++) { /* check all clients for data */
if ( (sockfd = client[i].fd) < 0)
continue;
if (client[i].revents & (POLLRDNORM | POLLERR)) {
if ( (n = read(sockfd, buf, MAXLINE)) > 0) {
if (i == 0) { //stdin
printf("works\n");
return 0;
}
if (--nready <= 0)
break; /* no more readable descriptors */
}
}
}
}
return 0;
}

Why does mmap defined in a Linux kernel module return MAP_FAILED?

I am trying to map a kernel buffer in user space using mmap method in linux 3.10.10. But it is returning MAP_FAILED. Why it is failed to map the buffer.
Kernel module
#include <linux/module.h> /* Needed by all modules */
#include <linux/kernel.h> /* Needed for KERN_ALERT */
#include <linux/init.h> /* Needed for the macros */
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#include <linux/interrupt.h>
#include <linux/io.h>
//#include <linux/malloc.h>
#include <linux/mm.h> /* mmap related stuff */
long long *buf1;
long long* buf;
static int driver_mmap(struct file *file, struct vm_area_struct *vma)
{
vma->vm_flags |= VM_LOCKED|VM_SHARED;
int i = remap_pfn_range(vma, vma->vm_start,
virt_to_phys(buf) >> PAGE_SHIFT,
vma->vm_end-vma->vm_start, vma->vm_page_prot);
SetPageReserved(virt_to_page(buf));
printk("MMAP \n");
return 0;
}
struct file_operations proc_fops =
{
mmap:driver_mmap,
};
int init_module_test(void)
{
int i;
buf1 = kmalloc(4096, __GFP_COLD|GFP_DMA);
buf = ((int)buf1 + PAGE_SIZE -1) & PAGE_MASK;
printk("<1>Hello world1\n");
for (i = 0; i < 512; i++)
{
buf[i] = (long long) i + 1;
}
proc_create ("mmap_example",0,NULL, &proc_fops);
printk("<1>Hello world3\n");
printk("<1>BUF1 = 0x%08x\n BUF = 0x%08x\n", buf1,buf);
return 0;
}
void cleanup_module_test(void)
{
remove_proc_entry ("mmap_example", NULL);
kfree(buf1);
printk("Goodbye world\n");
}
module_init(init_module_test);
module_exit(cleanup_module_test);
Application code
#include<stdio.h>
#include<stdlib.h>
#include<sys/mman.h>
int main(void)
{
int fd, i;
long long *msg = NULL;
if ((fd = fopen("/proc/mmap_example", "r")) < 0)
{
printf("File not opened");
}
msg = mmap(NULL, 4096, PROT_READ, MAP_SHARED, fd, 0);
if (msg == MAP_FAILED)
{
printf("MAP failed");
return 0;
}
for (i = 0; i < 512; i++)
printf("0x%llx,", msg[i]);
fclose(fd);
return 0;
}
I always end up seing "MAP failed".
Is there something wrong with my code?
The first problem is that you are attempting to use fopen to open a file and placing the return value in an integer, but fopen doesn't return an integer. It returns FILE *. This tells me you are ignoring compiler warnings and errors. That's a bad idea: they're produced for a reason.
The second problem is that you actually do need an integer file handle in order to provide it as an argument to mmap(2). For that, you should be calling open(2) (not fopen(3)).
There may well be additional problems with this code, but that's a start.
well, i can't comment so i post an answer that is not one, but i hope it will be still useful:
i'm not sure about the driver, but you can use the errno method (http://man7.org/linux/man-pages/man3/errno.3.html) on mmap to have a better answer on why it's failing:
add in your application code at the right place:
#include <errno.h>
printf("%i",errno);
or you could maybe use the following if you don't want to print the errno :
cpp -dM /usr/include/errno.h | grep 'define E' | sort -n -k 3
from How to know what the 'errno' means?
debugfs can't handle mmap
I know this is not your exact case, but it also makes mmap fail with MAP_FAILED, and it may help future Googlers: https://patchwork.kernel.org/patch/9252557/
And here is a fully working procfs example with an userland test.

Go code to list files in a Linux directory using getdents()

As an exercise, I wanted to translate some C code that used many syscalls into Golang. I found this nice code example on Unix & Linux StackExchange:
/*
* List directories using getdents() because ls, find and Python libraries
* use readdir() which is slower (but uses getdents() underneath.
*
* Compile with
* ]$ gcc getdents.c -o getdents
*/
#define _GNU_SOURCE
#include <dirent.h> /* Defines DT_* constants */
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
struct linux_dirent {
long d_ino;
off_t d_off;
unsigned short d_reclen;
char d_name[];
};
#define BUF_SIZE 1024*1024*5
int
main(int argc, char *argv[])
{
int fd, nread;
char buf[BUF_SIZE];
struct linux_dirent *d;
int bpos;
char d_type;
fd = open(argc > 1 ? argv[1] : ".", O_RDONLY | O_DIRECTORY);
if (fd == -1)
handle_error("open");
for ( ; ; ) {
nread = syscall(SYS_getdents, fd, buf, BUF_SIZE);
if (nread == -1)
handle_error("getdents");
if (nread == 0)
break;
for (bpos = 0; bpos < nread;) {
d = (struct linux_dirent *) (buf + bpos);
d_type = *(buf + bpos + d->d_reclen - 1);
if( d->d_ino != 0 && d_type == DT_REG ) {
printf("%s\n", (char *)d->d_name );
}
bpos += d->d_reclen;
}
}
exit(EXIT_SUCCESS);
}
I have only been able to code this using ioutil.ReadDir which defeats the purpose. Does anyone have an idea on how to translate this?
I would avoid using this code. As written, it's wrong: on 32-bit systems and maybe even some 64-bit ones, SYS_getdents is the legacy syscall that doesn't provide d_type and lacks support for 64-bit inode numbers, which means you get gratuitous errors on modern filesystems. Even if you fix it, it's no better than inlining readdir, which does basically exactly the same thing internally.

MAC address from interface on OS X (C)

This might be a stupid question and I apologize if it's already been addressed here, but I've searched quite a bit without much luck. I'm trying to get my interface's hardware address in C and I'm using OS X (x86-64). I know how to get it with ifconfig, but I want my program to get it automatically for any computer, well, at least OS X computers. I found another thread that posted this link which pretty much does what I want (with some modifications), but I can't make the iokit functions link in ld (my compiler is gcc). I tried adding the flags -lIOKit and -framework IOKit to the gcc command line, but I still get the same link errors. Here's a link to my code: header and source.
This little program will work without changes on OSX.
Code : (credits to Alecs King from freebsd list)
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int mib[6], len;
char *buf;
unsigned char *ptr;
struct if_msghdr *ifm;
struct sockaddr_dl *sdl;
if (argc != 2) {
fprintf(stderr, "Usage: getmac <interface>\n");
return 1;
}
mib[0] = CTL_NET;
mib[1] = AF_ROUTE;
mib[2] = 0;
mib[3] = AF_LINK;
mib[4] = NET_RT_IFLIST;
if ((mib[5] = if_nametoindex(argv[1])) == 0) {
perror("if_nametoindex error");
exit(2);
}
if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
perror("sysctl 1 error");
exit(3);
}
if ((buf = malloc(len)) == NULL) {
perror("malloc error");
exit(4);
}
if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
perror("sysctl 2 error");
exit(5);
}
ifm = (struct if_msghdr *)buf;
sdl = (struct sockaddr_dl *)(ifm + 1);
ptr = (unsigned char *)LLADDR(sdl);
printf("%02x:%02x:%02x:%02x:%02x:%02x\n", *ptr, *(ptr+1), *(ptr+2),
*(ptr+3), *(ptr+4), *(ptr+5));
return 0;
}
You should, however, change int len; to size_t len;

Resources