Linux Kernel Module unix domain sockets - android

I'm trying to share data between kernel and user space. The ultimate goal is to port it to Android, so I'm using unix domain sockets. (I don't know if this is the best option).
I have a kernel module acting as socket client and a c program acting as socket server. And vice-versa, a kernel module acting as socket server and a c program acting as socket client.
Programs are very simple. Servers just send a string to clients and they print it.
I run server_user.c and then I try to insert client_module.ko using insmod. I get the following error:
Kernel panic - not syncing: stack - protector: Kernel stack is corrupted in: ffffffffa0005157
drm-kms-helper: panic occurred, switching back to text console
What's wrong?
Module Server
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/socket.h>
#include <linux/net.h>
#include <linux/un.h>
#include <net/sock.h>
#define SOCK_PATH "/tmp/usocket"
#define LISTEN 10
struct socket *sock = NULL;
struct socket *newsock = NULL;
static int __init server_module_init( void ) {
int retval;
char* string = "hello_world";
struct sockaddr_un addr;
struct msghdr msg;
struct iovec iov;
mm_segment_t oldfs;
// create
retval = sock_create(AF_UNIX, SOCK_STREAM, 0, &sock);
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, SOCK_PATH);
// bind
retval = sock->ops->bind(sock,(struct sockaddr *)&addr, sizeof(addr));
// listen
retval = sock->ops->listen(sock, LISTEN);
//accept
retval = sock->ops->accept(sock, newsock, 0);
//sendmsg
memset(&msg, 0, sizeof(msg));
memset(&iov, 0, sizeof(iov));
msg.msg_name = 0;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iov->iov_base = string;
msg.msg_iov->iov_len = strlen(string)+1;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
oldfs = get_fs();
set_fs(KERNEL_DS);
retval = sock_sendmsg(newsock, &msg, strlen(string)+1);
set_fs(oldfs);
return 0;
}
static void __exit server_module_exit( void ) {
printk(KERN_INFO "Exit usocket.");
}
module_init( server_module_init );
module_exit( server_module_exit );
MODULE_LICENSE("GPL");
Module Client
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/un.h>
#include <linux/net.h>
#include <net/sock.h>
#include <linux/socket.h>
#define SOCK_PATH "/tmp/usocket"
#define MAX 100
struct socket *sock = NULL;
static int __init client_module_init( void ) {
int retval;
char str[MAX];
struct sockaddr_un addr;
struct msghdr msg;
struct iovec iov;
mm_segment_t oldfs;
printk(KERN_INFO "Start client module.\n");
// create
retval = sock_create(AF_UNIX, SOCK_STREAM, 0, &sock);
// connect
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, SOCK_PATH);
retval = sock->ops->connect(sock, (struct sockaddr *)&addr, sizeof(addr), 0);
// recvmsg
memset(&msg, 0, sizeof(msg));
memset(&iov, 0, sizeof(iov));
msg.msg_name = 0;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_iov->iov_base= str;
msg.msg_iov->iov_len= strlen(str)+1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
oldfs = get_fs();
set_fs(KERNEL_DS);
retval = sock_recvmsg(sock, &msg, strlen(str)+1, 0);
set_fs(oldfs);
// print str
printk(KERN_INFO "client module: %s.\n",str);
// release socket
sock_release(sock);
return 0;
}
static void __exit client_module_exit( void )
{
printk(KERN_INFO "Exit client module.\n");
}
module_init( client_module_init );
module_exit( client_module_exit );
MODULE_LICENSE("GPL");
User Server
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#define SOCK_PATH "/tmp/usocket"
int send_msg_to_client(int socketfd, char* data) {
struct msghdr msg;
struct iovec iov;
int s;
memset(&msg, 0, sizeof(msg));
memset(&iov, 0, sizeof(iov));
msg.msg_name = NULL;
msg.msg_namelen = 0;
iov.iov_base = data;
iov.iov_len = strlen(data)+1;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
s = sendmsg(socketfd, &msg, 0);
printf("after send - iov_base: %s, length: %d\n", (char *) msg.msg_iov->iov_base, (int) strlen(msg.msg_iov->iov_base));
if(s < 0){
perror("sendmsg");
return 0;
}
return s;
}
int main(int argc, char* argv[])
{
if (argc != 2) {
printf("Usage: $ %s <string>\n",argv[0]);
return 0;
}
int s, s2, len, slen;
socklen_t t;
struct sockaddr_un local, remote;
char* data = argv[1];
printf("print data: %s\n",data);
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
memset(&local, 0, sizeof(local));
local.sun_family = AF_UNIX;
strcpy(local.sun_path, SOCK_PATH);
unlink(local.sun_path);
len = strlen(local.sun_path) + sizeof(local.sun_family);
if (bind(s, (struct sockaddr *)&local, len) == -1) {
perror("bind");
exit(1);
}
if (listen(s, 5) == -1) {
perror("listen");
exit(1);
}
printf("Waiting for a connection...\n");
t = sizeof(remote);
if ((s2 = accept(s, (struct sockaddr *)&remote, &t)) == -1) {
perror("accept");
exit(1);
}
printf("Connected.\n");
slen = send_msg_to_client(s2, data);
if(slen < 0)
perror("send");
close(s2);
return 0;
}
User Client
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#define SOCK_PATH "/tmp/usocket"
#define MAX 100
int recv_msg_from_server(int socketfd, char data[MAX]) {
struct msghdr msg;
struct iovec iov;
int s;
memset(&msg, 0, sizeof(msg));
memset(&iov, 0, sizeof(iov));
msg.msg_name = NULL;
msg.msg_namelen = 0;
iov.iov_base = data;
iov.iov_len = MAX;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
s = recvmsg(socketfd, &msg, 0);
printf("after recv - iov_base: %s, length: %d\n", (char *) msg.msg_iov->iov_base, (int) strlen(msg.msg_iov->iov_base));
if(s < 0){
perror("recvmsg");
}
return s;
}
int main(void)
{
int s, len, slen;
struct sockaddr_un remote;
char data[MAX];
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
printf("Trying to connect...\n");
memset(&remote, 0, sizeof(remote));
remote.sun_family = AF_UNIX;
strcpy(remote.sun_path, SOCK_PATH);
len = strlen(remote.sun_path) + sizeof(remote.sun_family);
if (connect(s, (struct sockaddr *)&remote, len) == -1) {
perror("connect");
exit(1);
}
printf("Connected.\n");
slen = recv_msg_from_server(s, data);
if (slen < 0) {
perror("recvmsg");
}
//printf("print data received > %s\n", data);
close(s);
return 0;
}

I have just faced a similar issue (with UNIX datagrams though), and I believe there is a bug in the unix_mkname() function which is part of the kernel: this function makes sure the sun_path field of the given sockaddr_un structure is always null terminated by adding a NUL character after the end of that field. Since this is the last field, it will corrupt the area (stack or heap) where that structure has been allocated.
The best solution is to patch that function in the kernel source code, so that it only sets to NUL the last character of the sun_path field. Meanwhile, you can make your code work by decreasing by 1 byte the size you give for the sockaddr_un structure:
Module Server
// bind
retval = sock->ops->bind(sock,(struct sockaddr *)&addr, sizeof(addr) - 1);
Module Client
retval = sock->ops->connect(sock, (struct sockaddr *)&addr, sizeof(addr) - 1, 0);
As I am using UNIX datagrams, I cannot really tell if it fixes the issue for bind() and connect(), but a least, it fixes it for sock_sendmsg().
#shu-suzuki: thanks for the leads.

After 5 years I came back here...
I again stucked with this problem and found that I did the same thing 5 years ago.
The cause is as in Tey's answer and the solution is OK (sutbract 1 from the length) but here's another solution.
What you can do is to use struct sockaddr_storage as struct sockaddr_un. As far as I read the kernel code this is what they use to handle bind system call from user space.
sockeaddr_storage is larger than sockaddr_un so kernel code can write beyond the sizeof(sockaddr_un).
The code should be like
struct sockaddr_storage addrStorage;
struct sockaddr_un* addr;
memset(&addrStorage, 0, sizeof(addrStorage));
addr = (struct sockaddr_un*)&addrStorage;
addr->sun_family = AF_UNIX;
strcpy(addr->sun_path, "/my/sock/name");
kernel_bind(listenSock, (struct sockaddr*)addr, sizeof(*addr));
This looks working for me.

Related

Android NDK: How to find load relocation for a PIE binary / how to get _r_debug.r_map->l_addr

This is sister-question of How to find load relocation for a PIE binary? which was solved by using _r_debug.r_map->l_addr. Now I need to do this on Android NDK 19.
I need to print backtrace without symbols. I can do this with following code:
#include <iostream>
#include <iomanip>
#include <unwind.h>
#include <dlfcn.h>
#include <sstream>
#include <android/log.h>
namespace {
struct BacktraceState
{
void** current;
void** end;
};
static _Unwind_Reason_Code unwindCallback(struct _Unwind_Context* context, void* arg)
{
BacktraceState* state = static_cast<BacktraceState*>(arg);
uintptr_t pc = _Unwind_GetIP(context);
if (pc) {
if (state->current == state->end) {
return _URC_END_OF_STACK;
} else {
*state->current++ = reinterpret_cast<void*>(pc);
}
}
return _URC_NO_REASON;
}
}
size_t captureBacktrace(void** buffer, size_t max)
{
BacktraceState state = {buffer, buffer + max};
_Unwind_Backtrace(unwindCallback, &state);
return state.current - buffer;
}
namespace clog {
std::string bt(int from, int max_stack) {
const size_t MAX_STACK = 30;
if (from >= max_stack)
from = 0;
if (max_stack > MAX_STACK)
max_stack = MAX_STACK;
void* array[MAX_STACK];
std::ostringstream msg;
size_t size = captureBacktrace(array, MAX_STACK);
if (size >= 2) {
msg << "[bt]" << std::hex;
for (int i = from; i < size; ++i) {
msg << "0x" << reinterpret_cast<uintptr_t>(array[i]) << " ";
}
msg << "[/bt]" << std::dec;
}
return msg.str();
}
}
It prints addresses that include offset from ASLR. On Linux I fixed this by changing
msg << "0x" << reinterpret_cast<uintptr_t>(array[i]) << " ";
to
#include <execinfo.h>
#include <link.h>
...
static const uintptr_t relocation = _r_debug.r_map->l_addr;
msg << "0x" << reinterpret_cast<uintptr_t>(array[i]) - reinterpret_cast<uintptr_t>(relocation) << " ";
_r_debug.r_map->l_addr is not available on Android as far as I can see. Is there some equivalent?

How to get mac address in Android native code

I can get mac address with this C code:
struct ifreq s;
char buffer[12 + 1];
int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
int i, j;
memset(buffer, 0 , sizeof(buffer));
strcpy(s.ifr_name, "wlan0");
if (0 == ioctl(fd, SIOCGIFHWADDR, &s)) {
memcpy(mac, s.ifr_addr.sa_data, 6);
return 1;
}
I want to get mac address in Android. but this code does not give MAC address. if (0 == ioctl(fd, SIOCGIFHWADDR, &s) is always false.
I found a code in https://groups.google.com/d/msg/android-ndk/6Y-0Eid3mGw/6bFvtap8xJIJ
But this code gives error:
ioctl(SIOCGIFHWADDR) error
and it gives wrong mac address like 00:00:00:7C::00:01
How can I get mac address in Android native code ?
This code works good on Nexus 5:
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <linux/sockios.h>
#include <linux/if.h>
#include <linux/in.h>
int get_mac_addr(char *mac_addr)
{
int sockfd;
struct ifreq ifr;
if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) >= 0) {
//strncpy(ifr.ifr_name, "eth0", IFNAMESIZE);
strncpy(ifr.ifr_name, "wlan0", IFNAMSIZ);
ifr.ifr_addr.sa_family = AF_INET;
if (ioctl(sockfd, SIOCGIFHWADDR, (char*) &ifr) == 0) {
sprintf(mac_addr, "%02X:%02X:%02X:%02X:%02X:%02X",
(unsigned char) ifr.ifr_ifru.ifru_hwaddr.sa_data[0], (unsigned char) ifr.ifr_ifru.ifru_hwaddr.sa_data[1],
(unsigned char) ifr.ifr_ifru.ifru_hwaddr.sa_data[2], (unsigned char) ifr.ifr_ifru.ifru_hwaddr.sa_data[3],
(unsigned char) ifr.ifr_ifru.ifru_hwaddr.sa_data[4], (unsigned char) ifr.ifr_ifru.ifru_hwaddr.sa_data[5]);
return 0;
}
}
/* error */
return -1;
}
int main(int argc, char *argv[], char *envp[])
{
char mac_addr[80] = {0};
printf( "MAC = %d, %s\n", get_mac_addr(mac_addr), mac_addr );
return 0;
}
You must connect to a WiFi network, then try to get mac address.

android linux kernel communicate with user space about NETLINK_USER

I want to implements kernel communicate with user space,Here is my code:
#include <sys/socket.h>
#include <linux/netlink.h>
#include <stddef.h>
#define NETLINK_USER 16
#define MAX_PAYLOAD 1024 /* maximum payload size*/
struct sockaddr_nl src_addr, dest_addr;
struct nlmsghdr *nlh = NULL;
struct iovec iov;
int sock_fd;
struct msghdr msg;
void main()
{
sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USER);
printf("%d\n",sock_fd);
if (sock_fd < 0)
return -1;
memset(&src_addr, 0, sizeof(src_addr));
src_addr.nl_family = AF_NETLINK;
src_addr.nl_pid = getpid(); /* self pid */
printf("%d\n",getpid());
.....
When I set NETLINK_USER = 17,18 or something else, then I exec this code :
sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USER);
it show sock_fd return -1, so it must set NETLINK_USER = 16, I want know why?
And I have another question: Here is my kernel code:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/unistd.h>
#include <linux/semaphore.h>
#include <asm/cacheflush.h>
#include <linux/string.h>
#include <net/sock.h>
#include <linux/netlink.h>
#include <linux/skbuff.h>
#include <stddef.h>
#define NETLINK_USER 16
struct sock *nl_sk = NULL;
void **sys_call_table;
asmlinkage int (*original_call_open) (const char*, int, int);
asmlinkage int (*original_call_read) (unsigned int, char*, int);
asmlinkage long (*sys_openat) (int, const char*, int, int);
asmlinkage long our_openat(int dfd, const char *filename, int flags, int mode){
printk("%s\n",filename);
return sys_openat(dfd,filename,flags,mode);
}
void hello_nl_recv_msg(struct sk_buff *skb)
{
struct nlmsghdr *nlh;
int pid;
struct sk_buff *skb_out;
int msg_size;
char *msg = "Hello from kernel";
int res;
printk(KERN_INFO "Entering: %s\n", __FUNCTION__);
msg_size = strlen(msg);
nlh = (struct nlmsghdr *)skb->data;
printk(KERN_INFO "Netlink received msg payload:%s\n", (char *)nlmsg_data(nlh));
pid = nlh->nlmsg_pid; //pid of sending process
skb_out = nlmsg_new(msg_size, 0);
if (!skb_out)
{
printk(KERN_ERR "Failed to allocate new skb\n");
return;
}
nlh = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, msg_size, 0);
NETLINK_CB(skb_out).dst_group = 0; //not in mcast group
strncpy(nlmsg_data(nlh), msg, msg_size);
res = nlmsg_unicast(nl_sk, skb_out, pid);
if (res < 0)
printk(KERN_INFO "Error while sending bak to user\n");
}
int init_module()
{
printk("Entering: %s\n", __FUNCTION__);
nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, 0, hello_nl_recv_msg, NULL, THIS_MODULE);
// printk("%s",nl_sk);
// nl_sk = netlink_kernel_create(NETLINK_USER, input);
if (!nl_sk)
{
printk(KERN_ALERT "Error creating socket.\n");
return -10;
}
return 0;
}
void cleanup_module()
{
// Restore the original call
sys_call_table[__NR_open] = original_call_open;
sys_call_table[__NR_read] = original_call_read;
sys_call_table[__NR_openat] = sys_openat;
printk(KERN_INFO "exiting hello module\n");
netlink_kernel_release(nl_sk);
}
//MODULE_LICENSE("GPL");
//module_init(init_module);
//module_exit(cleanup_module);
I found if I set NETLINK_USER = 16, when I insert kernel, the avd will stop running, but if I set NETLINK_USER = 31,28 or something else the avd will run normally,I want to know why it will like this?
At last, I think the function hello_nl_recv_msg haven't exec, I don't know why.

Android Linux kernel communicate with user space

I'm following this article to implement android linux kernel communicate with user space.
I use insmod kernel to insert my kernel to Android avd goldfish kernel, then I use cat /proc/kmsg to observe the kernel message, but I find the program doesn't execute as intended. Here is my code:
void hello_nl_recv_msg(struct sk_buff *skb)
{
struct nlmsghdr *nlh;
int pid;
struct sk_buff *skb_out;
int msg_size;
char *msg = "Hello from kernel";
int res;
printk(KERN_INFO "Entering: %sn", __FUNCTION__);
msg_size = strlen(msg);
nlh = (struct nlmsghdr *)skb->data;
printk(KERN_INFO "Netlink received msg payload:%sn", (char *)nlmsg_data(nlh));
pid = nlh->nlmsg_pid; //pid of sending process
skb_out = nlmsg_new(msg_size, 0);
if (!skb_out)
{
printk(KERN_ERR "Failed to allocate new skbn");
return;
}
nlh = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, msg_size, 0);
NETLINK_CB(skb_out).dst_group = 0; //not in mcast group
strncpy(nlmsg_data(nlh), msg, msg_size);
res = nlmsg_unicast(nl_sk, skb_out, pid);
if (res < 0)
printk(KERN_INFO "Error while sending bak to usern");
}
int init_module()
{
printk("Entering: %sn", __FUNCTION__);
nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, 0, hello_nl_recv_msg, NULL, THIS_MODULE);
printk("%s",nl_sk);
// nl_sk = netlink_kernel_create(NETLINK_USER, input);
//if (!nl_sk)
//{
// printk(KERN_ALERT "Error creating socket.n");
// return -10;
//}
return 0;
}
I find when the program exec
nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, 0, hello_nl_recv_msg, NULL, THIS_MODULE);
The kernel returns -1 or other integer, and it can't execute the function "hello_ne_recv_msg". I use android avd, the kernel is goldfish 2.6. Please help me, thanks.
Here is my code (tested on sony z2 kernel).
Note: your code may still fail if seandroid (selinux) is enforced.
Beware of code copy pasting. Check it!
Don't ignore compilation warnings.
Kernel module:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <net/sock.h>
#include <linux/socket.h>
#include <linux/net.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/skbuff.h>
static struct sock* nl_sk;
#define NETLINK_USER 31
void hello_nl_recv_msg(struct sk_buff* skb)
{
struct nlmsghdr* nlh;
int pid;
struct sk_buff* skb_out;
int msg_size;
char* msg = "Hello from kernel";
int res;
printk(KERN_INFO "Entering: %s\n", __FUNCTION__);
msg_size = strlen(msg);
nlh = (struct nlmsghdr*)skb->data;
printk(KERN_INFO "Netlink received msg payload:%s\n", (char*)nlmsg_data(nlh));
pid = nlh->nlmsg_pid; //pid of sending process
skb_out = nlmsg_new(msg_size, 0);
if (!skb_out) {
printk(KERN_ERR "Failed to allocate new skbn\n");
return;
}
nlh = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, msg_size, 0);
NETLINK_CB(skb_out).dst_group = 0; //not in mcast group
strncpy(nlmsg_data(nlh), msg, msg_size);
res = nlmsg_unicast(nl_sk, skb_out, pid);
if (res < 0) {
printk(KERN_INFO "Error while sending back to user\n");
}
}
int __init init_netlink_test(void)
{
printk(KERN_INFO "Entering: %s\n", __FUNCTION__);
nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, 0, hello_nl_recv_msg, NULL, THIS_MODULE);
if (!nl_sk) {
printk(KERN_ALERT "Error creating socket.\n");
return -10;
}
return 0;
}
void __exit exit_netlink_test(void)
{
printk(KERN_INFO "exiting hello module\n");
netlink_kernel_release(nl_sk);
}
module_init(init_netlink_test);
module_exit(exit_netlink_test);
userspace test app:
#include <sys/socket.h>
#include <linux/netlink.h>
#include <string.h>
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#define NETLINK_USER 31
#define MAX_PAYLOAD 1024 /* maximum payload size*/
struct sockaddr_nl src_addr, dest_addr;
struct nlmsghdr* nlh = NULL;
struct iovec iov;
int sock_fd;
struct msghdr msg;
int main()
{
sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USER);
if (sock_fd < 0) {
return -1;
}
memset(&src_addr, 0, sizeof(src_addr));
src_addr.nl_family = AF_NETLINK;
src_addr.nl_pid = getpid(); /* self pid */
/* interested in group 1<<0 */
bind(sock_fd, (struct sockaddr*)&src_addr,
sizeof(src_addr));
memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.nl_family = AF_NETLINK;
dest_addr.nl_pid = 0; /* For Linux Kernel */
dest_addr.nl_groups = 0; /* unicast */
nlh = (struct nlmsghdr*)malloc(
NLMSG_SPACE(MAX_PAYLOAD));
memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
nlh->nlmsg_pid = getpid();
nlh->nlmsg_flags = 0;
strcpy(NLMSG_DATA(nlh), "Hello");
iov.iov_base = (void*)nlh;
iov.iov_len = nlh->nlmsg_len;
msg.msg_name = (void*)&dest_addr;
msg.msg_namelen = sizeof(dest_addr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
printf("Sending message to kernel\n");
sendmsg(sock_fd, &msg, 0);
printf("Waiting for message from kernel\n");
/* Read message from kernel */
recvmsg(sock_fd, &msg, 0);
printf(" Received message payload: %s\n",
NLMSG_DATA(nlh));
close(sock_fd);
}

Android - IOCTL usage returns ENOTTY

I am trying to run a simple IOCTL example on Android. I am using kernel 2.6 and ICS. The module is properly registered/unregistered (insmod/rmmod). However, every time a try to execute ./user_app on the emulator, I always get
error: first ioctl: Not a typewriter
error: second ioctl: Not a typewriter
message: `�
This is clearly a ENOTTY. I debugged the application, and no fops procedure (device_ioctl, read_ioctl and write_ioctl) is being executed.
I would like to know if there is any restriction with the usage/implementation of IOCTL on Android. Thank you very much in advance.
--Raul
Here is the code:
module.c
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#define MY_MACIG 'G'
#define READ_IOCTL _IOR(MY_MACIG, 0, int)
#define WRITE_IOCTL _IOW(MY_MACIG, 1, int)
int main(){
char buf[200];
int fd = -1;
if ((fd = open("/data/local/afile.txt", O_RDWR)) < 0) {
perror("open");
return -1;
}
if(ioctl(fd, WRITE_IOCTL, "hello world") < 0)
perror("first ioctl");
if(ioctl(fd, READ_IOCTL, buf) < 0)
perror("second ioctl");
printf("message: %s\n", buf);
return 0;
}
user_app.c
#include <stdio.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#define MY_MACIG 'G'
#define READ_IOCTL _IOR(MY_MACIG, 0, int)
#define WRITE_IOCTL _IOW(MY_MACIG, 1, int)
static char msg[200];
static ssize_t device_read(struct file *filp, char __user *buffer, size_t length, loff_t *offset)
{
...
}
static ssize_t device_write(struct file *filp, const char __user *buff, size_t len, loff_t *off)
{
...
}
char buf[200];
int device_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) {
int len = 200;
switch(cmd) {
case READ_IOCTL:
...
break;
case WRITE_IOCTL:
...
break;
default:
return -ENOTTY;
}
return len;
}
static struct file_operations fops = {
.read = device_read,
.write = device_write,
.unlocked_ioctl = device_ioctl,
};
static int __init example_module_init(void)
{
printk("registering module");
return 0;
}
static void __exit example_module_exit(void)
{
printk("unregistering module");
}
module_init(example_module_init);
module_exit(example_module_exit);
MODULE_LICENSE("GPL");
It this the whole code that you've posted? You don't register a char device when initializing a module, so this can't work.
Also, be carefull when assigning IOCTLS numbers. When using reserved IOCTL on a wrong file, you will get ENOTTY. Consult this to make sure you don't have conflicts.
Read more about char drivers here.

Categories

Resources