dev_add_pack hook and memory leak - networking

I have to write lkm, which would resend all incoming packets. Yep, I know about xt_TEE, but have to write it on my own. I've looked through some examples: http://www.phrack.org/archives/55/p55_0x0c_Building%20Into%20The%20Linux%20Network%20Layer_by_lifeline%20&%20kossak.txt (it's rather old) and http://www.xakep.ru/post/20794/default.asp?print=true (packet sniffer).
Then I've wrote my code:
//INCLUDES//////////////////////////////////////////////////////////////
#include <linux/ip.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/module.h>
#include <linux/kernel.h>
//ABOUT/////////////////////////////////////////////////////////////////
MODULE_AUTHOR("");
MODULE_DESCRIPTION("");
MODULE_LICENSE("GPL");
MODULE_VERSION("0.0.1");
//DEFINES///////////////////////////////////////////////////////////////
//SYSCALLS//////////////////////////////////////////////////////////////
//PROTOTYPES/////////////////////////////////////////////////////////////
int new_hook_func(struct sk_buff *skb, struct device *dv, struct packet_type *pt);
void test();
//GLOBALS///////////////////////////////////////////////////////////////
static struct packet_type my_packet_type;
static char *dev = "eth0";
struct net_dev *d;
//INIT//////////////////////////////////////////////////////////////////
static int __init init(void)
{
printk(KERN_ALERT "module init\n");
d = dev_get_by_name(&init_net, dev);
my_packet_type.type = htons(ETH_P_ALL);
my_packet_type.func = new_hook_func;
my_packet_type.dev = d;
dev_add_pack(&my_packet_type);
return 0;
}
//EXIT//////////////////////////////////////////////////////////////////
static void __exit exit(void)
{
dev_remove_pack(&my_packet_type);
printk(KERN_ALERT "module exit");
}
////////////////////////////////////////////////////////////////////////
module_init(init);
module_exit(exit);
////////////////////////////////////////////////////////////////////////
//CORE//////////////////////////////////////////////////////////////////
int new_hook_func(struct sk_buff *skb, struct device *dv, struct packet_type *pt)
{
struct iphdr *ip;
ip = (struct iphdr*)skb_network_header(skb);
if(skb->pkt_type != PACKET_OUTGOING)
{
if(ip->version == 4 && ip->protocol == IPPROTO_ICMP)
{
struct sk_buff *my_skb = 0;
//copy incoming skb
my_skb = skb_copy_expand(skb, 16, 16, GFP_ATOMIC);
//get eth header
struct ethhdr *eth = eth_hdr(my_skb);
//push ethernet layer to skb
skb_push(my_skb, ETH_HLEN);
//set packet type to outgoing
skb->pkt_type = PACKET_OUTGOING;
//send skb struct
dev_queue_xmit(my_skb);
//drop all incoming packets
// kfree_skb(my_skb);
// kfree_skb(skb);
}
}
return NET_RX_DROP;
}
This code is supposed to resend every icmp packet recieved.
So, I've faced three problems:
1) Memory leak. Some how it leaks. I tried to comment whole hook func and there was only return, but memory was still leaking.
2) Return codes don't work. It's no matter what I return(NET_RX_DROP/NET_RX_ACCEPT/NF_DROP/NF_ACCEPT/1/0) is still recieves packets and answers to it.
3) Problems with mac-layer. As you may see in my code, I copy skb struct with expansion and have to push 14 bytes of mac-layer there. Otherwise packet will be sent without any mac bytes.
I apologize for my poor english and kindly ask for help.

Related

How to use read() to get the adress of a pointer?

I have 2 programs communicating with each other through a fifo, one's the writer the other's the reader.
The writer sends a pointer to a struct containing information.
The reader should receive the pointer and be able to see the information inside the struct.
Header file:
typedef struct req{
int _code;
char _client_pipe[PIPENAME];
char _box_name[BOXNAME];
} request;
/*writes to pipe tx a pointer with information*/
void send_request(int tx, request *r1) {
ssize_t ret = write(tx, &r1, sizeof(r1));
if (ret < 0) {
fprintf(stdout, "ERROR: %s\n", ERROR_WRITING_PIPE);
exit(EXIT_FAILURE);
}
}
/*Returns a pointer to a struct containing the request*/
request *serialize(int code, char* client_pipe, char* box_name){
request *r1 = (request*) malloc(sizeof(request));
r1->_code = code;
strcpy(r1->_client_pipe, client_pipe);
strcpy(r1->_box_name, box_name);
return r1;
}
Program writer:
int main(int argc, char **argv){
(void *) argc; // in my program i used argc, but for this problem it's not important hence why the //typecast to void
char register_pipe[PIPENAME];
char personal_pipe[PIPENAME];
char box_name[BOXNAME];
strcpy(register_pipe, argv[1]);
strcpy(personal_pipe, argv[2]);
strcpy(box_name, argv[3]);
int reg_pipe = open(register_pipe, O_WRONLY);
if (reg_pipe == -1) {
fprintf(stdout, "ERROR: %s\n", UNEXISTENT_PIPE);
return -1;
}
send_request(reg_pipe, serialize(1, personal_pipe, box_name));
}
Program reader:
char register_pipe[PIPENAME];
strcpy(register_pipe, argv[1]);
if(mkfifo(register_pipe, 0644) < 0)
exit(EXIT_FAILURE);
if ((reg_pipe = open(register_pipe, O_RDONLY)) < 0){
exit(EXIT_FAILURE);
}
if ((reg_pipe = open(register_pipe, O_RDONLY)) < 0){
exit(EXIT_FAILURE);
}
request* buffer = (request*) malloc(sizeof(request)); //this might be the issue but not sure
ssize_t broker_read= read(reg_pipe, buffer, 256); //is not reading correctly
printf("%d, %s, %s\n", buffer->_code, buffer->_client_pipe, buffer->_box_name);
So if i start program reader and set register pipe as "reg", this will create the register pipe and wait for someone to join it.
Then if i start the program writer like ./writer reg personal box
this will open the reg pipe correctly, create a struct of type request and then sent it to the reader.
The reader should receive a pointer to a struct req set like:
_code = 1;
_client_pipe[PIPENAME] = "personal";
_box_name[BOXNAME] = "box";
The reader is in fact receiving but for some reason it's not receiving correctly.
If i try to print like in the last line, it will output some random numbers and letters.
How can i fix this?
You would need to have that structure exist inside a shared memory region that you have arranged to be mapped into both processes at the same address.
Without some such arrangement, each process has a private address space, so an address known to process A is meaningless to process B.
How to make such an arrangement is very much dependent upon you operating system, and perhaps even variant of said operating system.
You will likely find it easier to just copy the structure, as opposed to its address, via the fifo.

Implementing cloning and (de/en)capsulation of packets using eBPF

I am trying to create a TC program that will clone a packet, encapsulate it with a modified L3 header and send the clone to a different host ("Monitor host") - Can I do that using a combination of bpf_skb_adjust_room with bpf_clone_redirect?
Kernel examples do not shed too much details into this use-case (for example, here.)
My current attempt seems to be mutating the original packet:
// Represents the redirect destination.
struct destination {
__u32 destination_ip;
__u8 destination_mac[ETH_ALEN];
};
// Contains the destination to redirect traffic to.
struct bpf_map_def SEC("maps") destinations = {
.type = BPF_MAP_TYPE_HASH,
.key_size = sizeof(__u32),
.value_size = sizeof(struct destination),
.max_entries = 1,
.map_flags = BPF_F_NO_PREALLOC,
};
SEC("tc")
int tc_ingress(struct __sk_buff *skb) {
__u32 key = 0;
struct destination *dest = bpf_map_lookup_elem(&destinations, &key);
if (dest != NULL) {
void *data_end = (void *)(long)skb->data_end;
void *data = (void *)(long)skb->data;
// Necessary validation: if L3 layer does not exist, ignore and continue.
if (data + sizeof(struct ethhdr) > data_end) {
return TC_ACT_OK;
}
struct ethhdr *eth = data;
struct iphdr encapsulate_iphdr = {};
struct iphdr *original_iphdr = data + sizeof(struct ethhdr);
if ((void*) original_iphdr + sizeof(struct iphdr) > data_end) {
return TC_ACT_OK;
}
// Change the L2 destination to the provided MAC destination
// and the source to the MAC addr of the recieving host.
memcpy(&eth->h_source, &eth->h_dest, ETH_ALEN);
memcpy(&eth->h_dest, dest->destination_mac, ETH_ALEN);
// Change the L3 destination to the provided destination IP
// and the source to the ip addr of the recieving host.
memcpy(&encapsulate_iphdr.daddr, &dest->destination_ip, IPV4_ADDR_LEN);
memcpy(&encapsulate_iphdr.saddr, &original_iphdr->daddr, IPV4_ADDR_LEN);
// Adjust room for another iphdr after the L2 layer.
if (bpf_skb_adjust_room(skb, sizeof(struct iphdr), BPF_ADJ_ROOM_NET, 0)) {
return TC_ACT_OK;
}
// Store the headers at after L2 headers at the original headers offset.
unsigned long offset = (unsigned long) original_iphdr;
if (bpf_skb_store_bytes(skb, (int)offset, &encapsulate_iphdr, sizeof(struct iphdr), 0)) {
return TC_ACT_OK;
}
// route back the to egress path.
// Zero flag means that the socket buffer is
// cloned to the iface egress path.
bpf_clone_redirect(skb, skb->ifindex, 0);
}
return TC_ACT_OK;
}
I believe that's not possible within the same BPF program run today because bpf_clone_redirect will redirect the clone as soon as it's called and there is not clone helper that wouldn't redirect as well.
You could however implement this with a recirculation to the same interface. The pseudo code would look something like:
if (skb->mark == ORIGINAL_PACKET) {
skb->mark = 0;
return TC_ACT_OK;
}
skb->mark = ORIGINAL_PACKET;
bpf_clone_redirect(skb, skb->ifindex, BPF_F_INGRESS);
skb->mark = 0;
... implement changes ...
return bpf_redirect(skb, skb->ifindex, 0);

What is rosidl_runtime_c__double__Sequence type?

I'm trying to use a teensy 4.1 as an interface between an encoder and ROS thanks to micro-ros (arduino version).
I would like to publish position of a wheel to the /jointState topic with the teensy but there is no example on the micro-ros arduino Github repo.
I've tried to inspect the sensormsgs/msg/jointState message struct but everything is a bit fuzzy and I don't understand how to make it works. I can't understand what is rosidl_runtime_c__double__Sequence type.
I've tried several things but I always get an error about operand types
no match for 'operator=' (operand types are 'rosidl_runtime_c__String' and 'const char [18]')
msg.name.data[0] = "drivewhl_1g_joint";
Here is my arduino code
#include <micro_ros_arduino.h>
#include <stdio.h>
#include <rcl/rcl.h>
#include <rcl/error_handling.h>
#include <rclc/rclc.h>
#include <rclc/executor.h>
#include <sensor_msgs/msg/joint_state.h>
rcl_publisher_t publisher;
sensor_msgs__msg__JointState msg;
rclc_executor_t executor;
rclc_support_t support;
rcl_allocator_t allocator;
rcl_node_t node;
rcl_timer_t timer;
#define LED_PIN 13
#define RCCHECK(fn) { rcl_ret_t temp_rc = fn; if((temp_rc != RCL_RET_OK)){error_loop();}}
#define RCSOFTCHECK(fn) { rcl_ret_t temp_rc = fn; if((temp_rc != RCL_RET_OK)){}}
void error_loop(){
while(1){
digitalWrite(LED_PIN, !digitalRead(LED_PIN));
delay(100);
}
}
void timer_callback(rcl_timer_t * timer, int64_t last_call_time)
{
RCLC_UNUSED(last_call_time);
if (timer != NULL) {
RCSOFTCHECK(rcl_publish(&publisher, &msg, NULL));
//
//Do not work
//msg.name=["drivewhl_1g_joint","drivewhl_1d_joint","drivewhl_2g_joint","drivewhl_2d_joint"];
//msg.position=["1.3","0.2", "0","0"];
msg.name.size = 1;
msg.name.data[0] = "drivewhl_1g_joint";
msg.position.size = 1;
msg.position.data[0] = 1.85;
}
}
void setup() {
set_microros_transports();
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, HIGH);
delay(2000);
allocator = rcl_get_default_allocator();
//create init_options
RCCHECK(rclc_support_init(&support, 0, NULL, &allocator));
// create node
RCCHECK(rclc_node_init_default(&node, "micro_ros_arduino_node", "", &support));
// create publisher
RCCHECK(rclc_publisher_init_default(
&publisher,
&node,
ROSIDL_GET_MSG_TYPE_SUPPORT(sensor_msgs, msg, JointState),
"JointState"));
// create timer,
const unsigned int timer_timeout = 1000;
RCCHECK(rclc_timer_init_default(
&timer,
&support,
RCL_MS_TO_NS(timer_timeout),
timer_callback));
// create executor
RCCHECK(rclc_executor_init(&executor, &support.context, 1, &allocator));
RCCHECK(rclc_executor_add_timer(&executor, &timer));
}
void loop() {
delay(100);
RCSOFTCHECK(rclc_executor_spin_some(&executor, RCL_MS_TO_NS(100)));
}
I'm a beginner with Ros and C, it may be a very dumb question but I don't know how to solve it. Thanks for your help !
rosidl_runtime_c__String__Sequence is a structure used to old string data that is to be transmitted. Specifically it is a sequence of rosidl_runtime_c__String data. You're running into an error because rosidl_runtime_c__String is also a struct itself with no custom operators defined. Thus, your assignment fails since the types are not directly convertible. What you need to do instead is use the rosidl_runtime_c__String.data field. You can see slightly more info here
void timer_callback(rcl_timer_t * timer, int64_t last_call_time)
{
RCLC_UNUSED(last_call_time);
if (timer != NULL) {
//msg.name=["drivewhl_1g_joint","drivewhl_1d_joint","drivewhl_2g_joint","drivewhl_2d_joint"];
//msg.position=["1.3","0.2", "0","0"];
msg.name.size = 1;
msg.name.data[0].data = "drivewhl_1g_joint";
msg.name.data[0].size = 17; //Size in bytes excluding null terminator
msg.position.size = 1;
msg.position.data[0] = 1.85;
RCSOFTCHECK(rcl_publish(&publisher, &msg, NULL));
}
}
I also spent quite some time trying to get publishing JointState message from my esp32 running microros, and also couldn't find working example. Finally, i was successful, maybe it will help someone.
In simple words:
.capacity contains max number of elements
.size contains actual number of elements (strlen in case of string)
.data should be allocated as using malloc as .capacity * sizeof()
each string within sequence should be allocated separately
This is my code that allocates memory for 12 joints, named j0-j11. Good luck!
...
// Declarations
rcl_publisher_t pub_joint;
sensor_msgs__msg__JointState joint_state_msg;
...
// Create publisher
RCCHECK(rclc_publisher_init_default(&pub_joint, &node,
ROSIDL_GET_MSG_TYPE_SUPPORT(sensor_msgs, msg, JointState),
"/hexapod/joint_state"));
//Allocate memory
joint_state_msg.name.capacity = 12;
joint_state_msg.name.size = 12;
joint_state_msg.name.data = (std_msgs__msg__String*) malloc(joint_state_msg.name.capacity*sizeof(std_msgs__msg__String));
for(int i=0;i<12;i++) {
joint_state_msg.name.data[i].data = malloc(5);
joint_state_msg.name.data[i].capacity = 5;
sprintf(joint_state_msg.name.data[i].data,"j%d",i);
joint_state_msg.name.data[i].size = strlen(joint_state_msg.name.data[i].data);
}
joint_state_msg.position.size=12;
joint_state_msg.position.capacity=12;
joint_state_msg.position.data = malloc(joint_state_msg.position.capacity*sizeof(double));
joint_state_msg.velocity.size=12;
joint_state_msg.velocity.capacity=12;
joint_state_msg.velocity.data = malloc(joint_state_msg.velocity.capacity*sizeof(double));
joint_state_msg.effort.size=12;
joint_state_msg.effort.capacity=12;
joint_state_msg.effort.data = malloc(joint_state_msg.effort.capacity*sizeof(double));
for(int i=0;i<12;i++) {
joint_state_msg.position.data[i]=0.0;
joint_state_msg.velocity.data[i]=0.0;
joint_state_msg.effort.data[i]=0.0;
}
....
//Publish
RCSOFTCHECK(rcl_publish(&pub_joint, &joint_state_msg, NULL));

Sending ZeroMQ data using TCP

I am trying to make a simple server.
A language I am restricted to use is c++.
I am using ZeroMQ.
I have creatred a simple server and a client, as in documentation.
ZeroMQ uses TCP instead of HTTP.
I know that HTTP's underlying layer is TCP, so I want to know will it have any performance issues by using TCP instead of HTTP.
And for HTTP I can use curl to test the application.
What should I use for TCP ( curl command to send request to a socket with a string parameter ).
Server:
#include <zmq.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
int main (void)
{ // Socket to talk to clients
void *context = zmq_ctx_new ();
void *responder = zmq_socket (context, ZMQ_REP);
int rc = zmq_bind (responder, "tcp://*:5555");
assert (rc == 0);
while (1)
{
char buffer [10];
zmq_recv (responder, buffer, 10, 0);
printf ("Received Hello\n");
// trying to send json object
zmq_send (responder, "World", 5, 0);
// zmq_send (responder, "World", 5, 0);
sleep (1); // Do some 'work'
}
return 0;
}
client:
// Hello World client
// Connects REQ socket to tcp://localhost:5555
// Sends "Hello" to server, expects "World" back
#include <zmq.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
int main (void)
{
void *context = zmq_ctx_new ();
// Socket to talk to server
printf ("Connecting to hello world server...\n");
void *requester = zmq_socket (context, ZMQ_REQ);
zmq_connect (requester, "tcp://localhost:5555");
int request_nbr;
for (request_nbr = 0; request_nbr != 10; request_nbr++)
{
zmq_msg_t request;
zmq_msg_init_size (&request, 5);
memcpy (zmq_msg_data (&request), "Hello", 5);
printf ("Sending Hello %d...\n", request_nbr);
zmq_msg_send (&request, requester, 0);
zmq_msg_close (&request);
zmq_msg_t reply;
zmq_msg_init (&reply);
zmq_msg_recv (&reply, requester, 0);
printf ("Received World %d\n", request_nbr);
zmq_msg_close (&reply);
}
zmq_close (requester);
zmq_ctx_destroy (context);
return 0;
}
Q1: will it have any performance issues by using TCP instead of HTTP?
A1: yes, it will. Both performance and latency will benefit from avoiding HTTP-rich-re-wrapping of data
Q2: What should I use for TCP to send a request to a socket with a string parameter?
A2: No command ( curl command ) will help you. ZeroMQ uses certain line-code ( assume it as a trivial protocol between communicationg peers ), so a standalone command-line tool will not be able to match the line-code requirement off-the-shelf. Solution? Create a simple c-programme, that will consume a cmd-line arguments ( the string, as an example ) and assemble a ZeroMQ-layer compatible data-framing so as to communicate with the remote peer. Also you shall notice, that for ZeroMQ REQ/REP Formal Communication Pattern to work, this proxy-tool will have to become the sole respective REQ, resp. REP entity in the step-forward-locking diadic-communication relation, thus also providing an awaited response, the REQ-side is expecting to receive after the REP-side has received a message.

Write Error: bad address on writing encrypted data to wrapper filesystem

I am implementing a simple caeser cipher on WrapFS to store encrypted data and decrypt while reading. For that purpose I made minor changes to wrapfs_read() and wrapfs_write() functions provided in the source code to encrypt and decypt the data. My decryption is working fine, but due to my encryption code I am getting an error on write as follows bash: echo: write error: bad address. Any help regarding handling it would be really appreciated.
void caeser_encrypt(char __user *encrypted, size_t count)
{
int i;
for(i=0;i<(unsigned int)count;i++)
encrypted[i]=encrypted[i]+3;
printk(KERN_INFO "%s",encrypted);
return;
}
static ssize_t wrapfs_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
int err = 0;
struct file *lower_file;
struct dentry *dentry = file->f_path.dentry;
char *encrypted = NULL;
lower_file = wrapfs_lower_file(file);
/*Added by me*/
encrypted=kmalloc(sizeof(buf),GFP_USER);
memcpy(encrypted,buf,count);
printk(KERN_INFO "%d %d",(int)sizeof(buf), (int)count);
caeser_encrypt(encrypted,count);
err = vfs_write(lower_file, encrypted, count, ppos);
/*Added by me*/
//err = vfs_write(lower_file, buf, count, ppos);
/* update our inode times+sizes upon a successful lower write */
if (err >= 0) {
fsstack_copy_inode_size(dentry->d_inode,
lower_file->f_path.dentry->d_inode);
fsstack_copy_attr_times(dentry->d_inode,
lower_file->f_path.dentry->d_inode);
}
/*Added by me*/
kfree(encrypted);
return err;
}
I would say that "const char __user *buf" is user space address and vfs_write expects user space address and you are passing kernel space address to it. Also you cannot directly copy user space address to kernel space address. You can try using copy_from_user and copy_to_user functions. I hope you are aware that caeser cipher is insecure.

Resources