Kernel to userspace application communication - android

I am trying to make Kernel (Android, kernel 4.9.59) communicate with userspace applications. I found a solution using Netlink sockets: https://stackoverflow.com/a/25071310/4190159
The first issue with the solution is that struct netlink_skb_parms used in the solution does not have a member named 'pid', instead has a member named 'portid', which I believe is not the same as pid. Anyway, to compile my kernel side code/solution I used 'portid' member of struct netlink_skb_parms instead for initialization. However, now I am getting a different error.
My Kernel side Netlink socket code as follows:
#include <linux/sched.h>
//For netlink socket -->
#include <net/sock.h>
//#include <net/netlink.h>
#include <linux/netlink.h>
#include <linux/skbuff.h>
#include <linux/string.h>
#define MY_GROUP 1 //For netlink socket
struct sock* socket; //For netlink socket
struct sk_buff* socket_buff; //For netlink socket
static void nl_receive_callback (struct sk_buff *skb)
{
nlmsg_free(skb);
}
static void kernel_send_nl_msg(void)
{
struct nlmsghdr *nlsk_mh;
char* msg = "hello from kernel";
socket = netlink_kernel_create(&init_net, NETLINK_USERSOCK, 1, nl_receive_callback, NULL, THIS_MODULE);
socket_buff = nlmsg_new(256, GFP_KERNEL);
nlsk_mh = nlmsg_put(socket_buff, 0, 0, NLMSG_DONE, strlen(msg), 0);
//NETLINK_CB(socket_buff).pid = 0; // kernel pid is deprecated
NETLINK_CB(socket_buff).portid = 0;
NETLINK_CB(socket_buff).dst_group = MY_GROUP;
strcpy(nlmsg_data(nlsk_mh), msg);
nlmsg_multicast(socket, socket_buff, 0, MY_GROUP, GFP_KERNEL);
pr_info("%s", msg);//Print out the message to kernel
return;
}
My userspace application side code to intercept the message from the kernel as follows:
#include <sys/socket.h>
#include <linux/netlink.h>
#define MY_GROUP 1
void user_recieve_nl_msg(void)
{
int sock_fd;
struct sockaddr_nl user_sockaddr;
struct nlmsghdr *nl_msghdr;
struct msghdr msghdr;
struct iovec iov;
char* kernel_msg;
sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USERSOCK);
memset(&user_sockaddr, 0, sizeof(user_sockaddr));
user_sockaddr.nl_family = AF_NETLINK;
user_sockaddr.nl_pid = getpid();
user_sockaddr.nl_groups = MY_GROUP;
bind(sock_fd, (struct sockaddr*)&user_sockaddr, sizeof(user_sockaddr));
while (1) {
nl_msghdr = (struct nlmsghdr*) malloc(NLMSG_SPACE(256));
memset(nl_msghdr, 0, NLMSG_SPACE(256));
iov.iov_base = (void*) nl_msghdr;
iov.iov_len = NLMSG_SPACE(256);
msghdr.msg_name = (void*) &user_sockaddr;
msghdr.msg_namelen = sizeof(user_sockaddr);
msghdr.msg_iov = &iov;
msghdr.msg_iovlen = 1;
recvmsg(sock_fd, &msghdr, 0);
kernel_msg = (char*)NLMSG_DATA(nl_msghdr);
print("Kernel message: %s\n", kernel_msg); // print to android logs
}
close(sock_fd);
}
When I am trying to build the android kernel I am receiving the fllowing error:
kernel/sched/custom_code.h:34:65: error: passing argument 3 of
'netlink_kernel_create' makes pointer from integer without a cast
[-Werror]
kernel/sched/custom_code.h:34:14: error: too many arguments to
function 'netlink_kernel_create'
Note the code for the Kernel side is written in custom_code.h.
My questions are as follows:
I have checked the function 'netlink_kernel_create' and I am sending the right number of arguments then why is the aformentioned error coming up? How this error could be resolved?
What should I do to establish a valid communication between the kernel and the userspace application so that messages could be passed (to and fro) between them?

1.
Let's check netlink_kernel_create function in linux kernel:
static inline struct sock *
netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg)
{
return __netlink_kernel_create(net, unit, THIS_MODULE, cfg);
}
from here https://elixir.bootlin.com/linux/v4.9.59/source/include/linux/netlink.h#L60
Notice, that this function takes only 3 arguments ( instead of 6 in your code )
This function have been changed in kernel 3.6 ( from 6 parameters to 4 )
https://elixir.bootlin.com/linux/v3.6/source/include/linux/netlink.h#L185
And then renamed to __netlink_kernel_create in kernel 3.7
netlink_kernel_create function from 3.7 accepts 3 arguments
https://elixir.bootlin.com/linux/v3.7/source/include/linux/netlink.h#L48
Try change this
socket = netlink_kernel_create(&init_net, NETLINK_USERSOCK, 1, nl_receive_callback, NULL, THIS_MODULE);
to this
struct netlink_kernel_cfg cfg = {
.input = nl_receive_callback,
.groups = 1,
};
socket = netlink_kernel_create(&init_net, NETLINK_USERSOCK, &cfg);
Now you can send data in direction "kernel => application".
When you start your application, it will bind socket with user_sockaddr.nl_groups = MY_GROUP; bind(sock_fd, (struct sockaddr*)&user_sockaddr, sizeof(user_sockaddr));.
After this you can send data from kernel to application with NETLINK_CB(socket_buff).dst_group = MY_GROUP; nlmsg_multicast(socket, socket_buff, 0, MY_GROUP, GFP_KERNEL); and it will be received by application with recvmsg(sock_fd, &msghdr, 0);
How can I call kernel_send_nl_msg() function to actually communicate with the userspace?
You can call it from kernel module, which you write, compile and insmod into kernel. Or you can call it directly from kernel, but for this you will need to rebuild whole kernel.
If you want to send data in direction "application = > kernel", then you need to do things in reverse: bind new socket with another MY_GROUP2 in kernel and send data from application with nlmsg_multicast

Related

Create UDP server using C++ to embed in cross platform iOS and Android app

I am developing a cross-platform mobile game (iOS and Android) using cocos2d-x.
Most of my code is written in C++, with OS specific code in Objective-C / Java / Swift using a bridge.
I was wondering if anyone has used any C++ library to host a UDP server within their app ?
EDIT: So far I have found many platform specific solutions (using Java for Android, and cocoaasync etc for iOS), but nothing specifically in C++ which has been used for a cross platform app.
Edit: I would prefer a solution without boost. Preferably something simple to include like adding a couple of files to a project.
You can most probably use Valve's GameNetworkingSockets, https://github.com/ValveSoftware/GameNetworkingSockets
They have very limited external dependencies, so you should be able to compile them for both iOS and Android
You can also take a look at this list: https://github.com/MFatihMAR/Awesome-Game-Networking, where there is a list of libraries that you can alternatively try.
You can use ASIO Standalone library. It is the same library available as boost/asio, but it doesn't require any other boost libraries. I have used boost/asio in exactly this type of project (Android/iOS) and it is by far the most superior solution.
Here is what I ended up with:
h file:
#include "Queue.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <array>
#include <iostream>
#include <thread>
using namespace std;
#define MAXBUFFER_SIZE 1024
class UDPServer {
public:
/**
* Constructor
*
* #port the port on which the UDP server is listening for packets.
*/
explicit UDPServer(unsigned short port);
/**
* Destructor
*/
~UDPServer() = default;
/**
* Setup the server.
*/
void setupServer();
/**
* Get a single message.
* For demonstration purposes, our messages is expected to be a array of int
*/
bool getMessage(std::array<int, 4>& message);
bool getIPAddress(std::array<int, 4>& message);
void setFoundIP();
bool isReady();
void nextPort();
int getPort();
private:
bool _isBoundToPort = false;
/**
* The server port.
*/
unsigned short port_;
bool isFoundIP = false;
/**
* The thread-safe message queue.
*/
Queue queue_;
Queue _ipAddresses;
/**
* The UDP server function.
*/
int UDPServerFunc();
};
cpp file:
#include "UDPServer.h"
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
using namespace std;
/**
* This function parses an incoming message with the following format: 1;234;-89;-53;
*
* A valid message consists of 4 integer values separated by semicolons.
*/
inline std::array<int, 4> parseMessage(const std::string& input);
inline std::array<int,4> parseIp(const std::string& input);
UDPServer::UDPServer(unsigned short port) {
port_ = port;
}
bool UDPServer::getMessage(std::array<int, 4>& message) {
return queue_.pop(message);
}
bool UDPServer::getIPAddress(std::array<int, 4>& message) {
return _ipAddresses.pop(message);
}
void UDPServer::setFoundIP(){
isFoundIP = true;
}
bool UDPServer::isReady(){
return _isBoundToPort;
}
void UDPServer::nextPort(){
port_++;
}
int UDPServer::getPort(){
return port_;
}
void UDPServer::setupServer() {
// Launch the server thread.
std::thread t([this](){
UDPServerFunc();
});
t.detach();
}
int UDPServer::UDPServerFunc() {
// Creating socket file descriptor
int sockfd;
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
// Filling server information
struct sockaddr_in servaddr, cliaddr;
memset(&servaddr, 0, sizeof(servaddr));
memset(&cliaddr, 0, sizeof(cliaddr));
servaddr.sin_family = AF_INET; // IPv
servaddr.sin_addr.s_addr = INADDR_ANY;
servaddr.sin_port = htons(port_);
// Bind the socket with the server address
if (::bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
perror("bind failed");
exit(EXIT_FAILURE);
}
_isBoundToPort = true;
while (true) {
// Read the next message from the socket.
char message[MAXBUFFER_SIZE];
socklen_t len = sizeof(struct sockaddr);
ssize_t n = recvfrom(sockfd, (char *)&message, MAXBUFFER_SIZE, MSG_DONTWAIT,
(struct sockaddr *)&cliaddr, (socklen_t*)&len);
if (n > 0) {
message[n] = '\0';
// Parse incoming data and push the result on the queue.
// Parsed messages are represented as a std::array<int, 4>.
if(!isFoundIP){
_ipAddresses.push(parseIp(message));
}else{
queue_.push(parseMessage(message));
}
} else {
// Wait a fraction of a millisecond for the next message.
usleep(100);
}
}
return 0;
}
I removed any unnecessary code from my answer, as the meat really is just the code above. If anyone needs the extraneous functions as well, I shared the code on Github, and will add some examples later on too.
The above code is really simple and has a couple of parse functions for extracting an IP address, or a set of four numbers delimited by semicolons. The above code is simple enough to modify yourself for your own custom messages.
Queue.h is just a simple thread safe queue.

Kernel (Android) to Userspace message multicast error: netlink_broadcast_filtered+0x24/0x3d4

I am trying to develop a function which would enable the Kernel (Android kernel: 4.9.59) to send a message to an/many userspace application and I have followed this example: Kernel to userspace application communication
However, when I am trying to call the message conveying function from the scheduler I am receiving the following error:
Fatal exception
PC is at netlink_broadcast_filtered+0x24/0x3d4
LR is at netlink_broadcast+0x14/0x20
Inside the kernel scheduler (kernel/sched) I have created a header (custom_code.h) which holds the function to send message from the kernel. I am not using Kernel module to inject this because Android does not support modules. The code inside custom_code.h is as follows:
#include <linux/sched.h>
#include <net/sock.h>
#include <linux/netlink.h>
#include <linux/skbuff.h>
#include <linux/string.h>
#define MY_GROUP 1 //For netlink socket
struct sock* socket; //For netlink socket
struct sk_buff* socket_buff; //For netlink socket
static inline void nl_receive_callback (struct sk_buff *skb)
{
nlmsg_free(skb);
}
struct netlink_kernel_cfg cfg = {
.input = nl_receive_callback,
.groups = 1,
};
static inline int kernel_send_nl_msg(void)
{
struct nlmsghdr *nlsk_mh;
char* msg = "hello from kernel";
socket = netlink_kernel_create(&init_net, NETLINK_USERSOCK, &cfg);
socket_buff = nlmsg_new(256, GFP_KERNEL);
nlsk_mh = nlmsg_put(socket_buff, 0, 0, NLMSG_DONE, strlen(msg), 0);
//NETLINK_CB(socket_buff).pid = 0; // kernel pid pid is deprecated
NETLINK_CB(socket_buff).portid = 0;
NETLINK_CB(socket_buff).dst_group = MY_GROUP;
strcpy(nlmsg_data(nlsk_mh), msg);
nlmsg_multicast(socket, socket_buff, 0, MY_GROUP, GFP_KERNEL);
pr_info("%s", msg);//Print out the message to kernel
return 0;
}
I am calling the kernel_send_nl_msg from the sugov_start(struct cpufreq_policy *policy) function inside the cpufreq_schedutil.c (kernel/sched/cpufreq_schedutil.c), and I have built the whole kernel and flashed it on an Android device.
My modified code inside the kernel/sched/cpufreq_schedutil.c as follows:
static int sugov_start(struct cpufreq_policy *policy)
{
struct sugov_policy *sg_policy = policy->governor_data;
unsigned int cpu;
sg_policy->up_rate_delay_ns =
sg_policy->tunables->up_rate_limit_us * NSEC_PER_USEC;
sg_policy->down_rate_delay_ns =
sg_policy->tunables->down_rate_limit_us * NSEC_PER_USEC;
update_min_rate_limit_us(sg_policy);
sg_policy->last_freq_update_time = 0;
sg_policy->next_freq = UINT_MAX;
sg_policy->work_in_progress = false;
sg_policy->need_freq_update = false;
sg_policy->cached_raw_freq = 0;
for_each_cpu(cpu, policy->cpus) {
struct sugov_cpu *sg_cpu = &per_cpu(sugov_cpu, cpu);
memset(sg_cpu, 0, sizeof(*sg_cpu));
sg_cpu->sg_policy = sg_policy;
sg_cpu->flags = 0;
sugov_start_slack(cpu);
sg_cpu->iowait_boost_max = policy->cpuinfo.max_freq;
cpufreq_add_update_util_hook(cpu, &sg_cpu->update_util,
policy_is_shared(policy) ?
sugov_update_shared :
sugov_update_single);
}
if(kernel_send_nl_msg() != 0)
pr_info("Error sending message from Kernel using socket");
return 0;
}
After flashing the kernel image to the device and trying to boot the aformentioned error is conveyed by the device (even without booting).
My question on the error is as follows:
1) What might be the issue that is causing the error?
2) How can I send messages successfully from Android kernel to an/many userspace application? Any suggestion would be really helpful.

Receiving bytes on server in C from Android

I have successfully connected my android phone as client to a raspberry pi as server.
The next step is sending data to the raspberry pi using bytes. Am I doing it correctly for the Android in Java?
socket.getOutputStream().write(String.valueOf(progress).getBytes());
Thereafter, on my raspberry pi side, I will have to receive the bytes in C. I am facing problems in receiving data on server. Here is my code.
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
int main(){
int welcomeSocket,clientSocket;
char buffer[1024];
struct sockaddr_in serverAddr;
struct sockaddr_storage serverStorage;
socklen_t addr_size;
ssize_t nread;
/*---- Create the socket. The three arguments are: ----*/
/* 1) Internet domain 2) Stream socket 3) Default protocol (TCP in this case) */
welcomeSocket = socket(PF_INET, SOCK_STREAM, 0);
/*---- Configure settings of the server address struct ----*/
/* Address family = Internet */
serverAddr.sin_family = AF_INET;
/* Set port number, using htons function to use proper byte order */
serverAddr.sin_port = htons(9999);
/* Set IP address to localhost */
serverAddr.sin_addr.s_addr = INADDR_ANY;
/* Set all bits of the padding field to 0 */
memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);
/*---- Bind the address struct to the socket ----*/
bind(welcomeSocket, (struct sockaddr *) &serverAddr, sizeof(serverAddr));
/*---- Listen on the socket, with 5 max connection requests queued ----*/
if(listen(welcomeSocket,5)==0){
printf("Listening\n");
}
else
printf("Error\n");
/*---- Accept call creates a new socket for the incoming connection ----*/
addr_size = sizeof serverStorage;
clientSocket = accept(welcomeSocket, (struct sockaddr *) &serverStorage, &addr_size);
printf("Successfully Connected!!!\n\r");
//Receive data from android
//recv(welcomeSocket,buffer,1024,0);
nread = recvfrom(clientSocket, buffer,1024, 0, (struct sockaddr *) &serverStorage, &addr_size);
if(nread==-1)
perror("Error!");
else
printf("Data received: %s",buffer);
return 0;
}
As from Pravin's comment, I tried using the Recvfrom and perror, it showed an error of -1.
Okay! I realised the problem. I realised I didnt use the client socket created previously. I kept on using the Server's socket lol. Didn't notice it at first. I used an example to see what was different between my server and the example server, which really helped!
http://www.binarytides.com/server-client-example-c-sockets-linux/

Cannot connect to Bluetooth/BlueZ server: Could not create RFCOMM socket

I am trying to create a simple and custom Bluetooth connection between my Linux laptop and Android phone. On the Linux side of things, I use the BlueZ libbluetooth library to set up a server. The code for it is very similar to others I've seen floating around on github:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
#include <bluetooth/sdp.h>
#include <bluetooth/sdp_lib.h>
#include <bluetooth/rfcomm.h>
sdp_session_t *register_service();
int main(int argc, char **argv)
{
struct sockaddr_rc loc_addr = { 0 }, rem_addr = { 0 };
char buf[1024] = { 0 };
char str[1024] = { 0 };
int s, client, bytes_read;
sdp_session_t *session;
socklen_t opt = sizeof(rem_addr);
session = register_service();
s = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
loc_addr.rc_family = AF_BLUETOOTH;
loc_addr.rc_bdaddr = *BDADDR_ANY;
loc_addr.rc_channel = (uint8_t) 11;
printf("Trying to bind...");
fflush(stdout);
bind(s, (struct sockaddr *)&loc_addr, sizeof(loc_addr));
printf("bound\n");
fflush(stdout);
printf("Waiting for connection...");
fflush(stdout);
listen(s, 1);
client = accept(s, (struct sockaddr *)&rem_addr, &opt);
ba2str( &rem_addr.rc_bdaddr, buf );
fprintf(stderr, "accepted connection from %s\n", buf);
memset(buf, 0, sizeof(buf));
bytes_read = read(client, buf, sizeof(buf));
if( bytes_read > 0 ) {
printf("received [%s]\n", buf);
}
sprintf(str,"to Android.");
printf("sent [%s]\n",str);
write(client, str, sizeof(str));
close(client);
close(s);
sdp_close( session );
return 0;
}
sdp_session_t *register_service()
{
uint32_t svc_uuid_int[] = { 0x00000000,0x00000000,0x00000000,0x3233 };
uint8_t rfcomm_channel = 11;
const char *service_name = "Remote Host";
const char *service_dsc = "What the remote should be connecting to.";
const char *service_prov = "Your mother";
uuid_t root_uuid, l2cap_uuid, rfcomm_uuid, svc_uuid;
sdp_list_t *l2cap_list = 0,
*rfcomm_list = 0,
*root_list = 0,
*proto_list = 0,
*access_proto_list = 0;
sdp_data_t *channel = 0, *psm = 0;
sdp_record_t *record = sdp_record_alloc();
// set the general service ID
sdp_uuid128_create( &svc_uuid, &svc_uuid_int );
sdp_set_service_id( record, svc_uuid );
sdp_list_t service_class = {NULL, &svc_uuid};
sdp_set_service_classes( record, &service_class);
// make the service record publicly browsable
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
root_list = sdp_list_append(0, &root_uuid);
sdp_set_browse_groups( record, root_list );
// set l2cap information
sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
l2cap_list = sdp_list_append( 0, &l2cap_uuid );
proto_list = sdp_list_append( 0, l2cap_list );
// set rfcomm information
sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
channel = sdp_data_alloc(SDP_UINT8, &rfcomm_channel);
rfcomm_list = sdp_list_append( 0, &rfcomm_uuid );
sdp_list_append( rfcomm_list, channel );
sdp_list_append( proto_list, rfcomm_list );
// attach protocol information to service record
access_proto_list = sdp_list_append( 0, proto_list );
sdp_set_access_protos( record, access_proto_list );
// set the name, provider, and description
sdp_set_info_attr(record, service_name, service_prov, service_dsc);
int err = 0;
sdp_session_t *session = 0;
// connect to the local SDP server, register the service record, and
// disconnect
session = sdp_connect( BDADDR_ANY, BDADDR_LOCAL, SDP_RETRY_IF_BUSY );
err = sdp_record_register(session, record, 0);
// cleanup
//sdp_data_free( channel );
sdp_list_free( l2cap_list, 0 );
sdp_list_free( rfcomm_list, 0 );
sdp_list_free( root_list, 0 );
sdp_list_free( access_proto_list, 0 );
return session;
}
I am able to run the server side code in Linux without error:
$ ./bluetooth-server
Trying to bind...bound
Waiting for connection...
I am then able to use sdptool to see my RFCOMM socket and service (description, channel number, and all other information look correct):
Service Name: Remote Host
Service Description: What the remote should be connecting to.
Service Provider: Your mother
Service RecHandle: 0x10009
Service Class ID List:
UUID 128: 00000000-0000-0000-0000-000033320000
Protocol Descriptor List:
"L2CAP" (0x0100)
"RFCOMM" (0x0003)
Channel: 11
Now, on the Android side of things I first pair the phone to the Linux server via the standard settings screen. Once paired, I then use code on the Android side of things to scan for Bluetooth devices, and if it is my Linux device then I try to connect to it. I have tried two different methods that people have suggested on stack overflow to make the connection:
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if(device.getName().equals("ubuntu-0")) {
try {
mBluetoothAdapter.cancelDiscovery();
// This returns the value 00001103-0000-1000-8000-00805f9b34fb
UUID uuid = device.getUuids()[0].getUuid();
// This does not work
//BluetoothSocket tmp = device.createRfcommSocketToServiceRecord(uuid);
// And neither does this
Method m = device.getClass().getMethod("createRfcommSocket", new Class[] {int.class});
BluetoothSocket tmp = (BluetoothSocket) m.invoke(device, 11); // Match channel 11
tmp.connect(); // Exception is thrown here
}
catch(Exception e) {
Log.e("BT", "Could not create RFCOMM socket " + e.toString());
}
}
}
I keep getting the following Exception being thrown when I actually try the tmp.connect() call:
03-20 14:20:13.089: E/BT(16915): Could not create RFCOMM socket
java.io.IOException: read failed, socket might closed or timeout, read ret: -1
Does anyone see what I am doing wrong here in trying to create the connection? Note that I do get a UUID returned when I call UUID uuid = device.getUuids()[0].getUuid(); ... this leads me to believe that the pairing is OK, which returns the value 00001103-0000-1000-8000-00805f9b34fb.
Maybe this could be reason if you pair your device before running ./bluetooth-server then rfcomm service record is not added and remote device will not information about this and if remote device does not do the service discovery then it will not be able to connect to your device.

How to use netlink sockets for kernel - userspace communication Android

I'm trying to write kernel module for android to communicate with userspace Service so my app can display some information about the kernel. So far I've tried several examples I found on the web and none of them works. The idea is for kernel to send updates and service would then pick them up. ioctl is not a solution because the communication needs to be event driven, and initiated from kernel. C code for both jni and kernel module would be great if anyone knows how to do this.
Here's a code snippet that I've used (excluding error checking of return values for conciseness) where the netlink socket communication is driven by the kernel (i.e. the kernel is the sender, user program is the receiver).
Kernel module
#include <net/sock.h>
#include <net/netlink.h>
#include <linux/skbuff.h>
#include <string.h>
#define MY_GROUP 1
struct sock* socket;
struct sk_buff* socket_buff;
static void nl_receive_callback (struct sk_buff *skb)
{
nlmsg_free(skb);
}
static void kernel_send_nl_msg(void)
{
struct nlmsghdr *nlsk_mh;
char* msg = "hello kernel";
socket = netlink_kernel_create(&init_net, NETLINK_USERSOCK, 1, nl_receive_callback, NULL, THIS_MODULE);
socket_buff = nlmsg_new(256, GFP_KERNEL);
nlsk_mh = nlmsg_put(socket_buff, 0, 0, NLMSG_DONE, strlen(msg), 0);
NETLINK_CB(socket_buff).pid = 0; // kernel pid
NETLINK_CB(socket_buff).dst_group = MY_GROUP;
strcpy(nlmsg_data(nlsk_mh), msg);
nlmsg_multicast(socket, socket_buff, 0, MY_GROUP, GFP_KERNEL);
return;
}
You'd fire this up as a result of your kernel event, e.g. in response to an ioctl or within your interrupt handling.
User Program
#include <sys/socket.h>
#include <linux/netlink.h>
#define MY_GROUP 1
void user_recieve_nl_msg(void)
{
int sock_fd;
struct sockaddr_nl user_sockaddr;
struct nlmsghdr *nl_msghdr;
struct msghdr msghdr;
struct iovec iov;
char* kernel_msg;
sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USERSOCK);
memset(&user_sockaddr, 0, sizeof(user_sockaddr));
user_sockaddr.nl_family = AF_NETLINK;
user_sockaddr.nl_pid = getpid();
user_sockaddr.nl_groups = MY_GROUP;
bind(sock_fd, (struct sockaddr*)&user_sockaddr, sizeof(user_sockaddr));
while (1) {
nl_msghdr = (struct nlmsghdr*) malloc(NLMSG_SPACE(256));
memset(nl_msghdr, 0, NLMSG_SPACE(256));
iov.iov_base = (void*) nl_msghdr;
iov.iov_len = NLMSG_SPACE(256);
msghdr.msg_name = (void*) &user_sockaddr;
msghdr.msg_namelen = sizeof(user_sockaddr);
msghdr.msg_iov = &iov;
msghdr.msg_iovlen = 1;
recvmsg(sock_fd, &msghdr, 0);
kernel_msg = (char*)NLMSG_DATA(nl_msghdr);
print("Kernel message: %s\n", kernel_msg); // print to android logs
}
close(sock_fd);
}
I use nlmsg_multicast() in the kernel rather than nlmsg_unicast() because to do a unicast, the kernel module would need to know the process id of your user process (so you'd need to add some extra implementation there).
Hope that helps!
I have successfully used netlink socket to communicate between kernel and user-space in Android. To use socket in non-blocking mode use the fcntl and select call.

Categories

Resources