I am trying to create a kernel module that will be able to send out modified packets from ones it receives through netfilter hooking. I'm using a code skeleton provided here. I am creating a raw socket inside the kernel simply using this code:
struct socket *sockptr;
sock_create(PF_INET, SOCK_RAW, IPPROTO_TCP, &sockptr);
The sendpacket function is called by this:
len = sendpacket(sockptr, dev, IPPROTO_TCP, duplicate, ntohs(dupiph->tot_len));
socketptr being the raw socket I created, dev being the net_device in passed to me by the hooking function, and duplicate being a modified copy of the original packet.
The return from the call to dev_queue_xmit indicates that the packet was transmitted successfully but I cannot see the packet on the wire. I have two questions: first, I would like to be able to better debug what is happening so any advice concerning that is much appreciated. Also, I am wondering if I am handling the socket creation properly or if there is some type of configuration I am missing. This is all very new to me so it very well could be that I am missing something silly.
It is unlikely that you need to modify the kernel to accomplish your task. Have you considered using tun or tap interface so you can do all of your work in user space? Here's a tutorial: http://backreference.org/2010/03/26/tuntap-interface-tutorial/
Related
The Situation
I am currently writing a kernel module, that should handle a custom network protocol based on UDP.
What I do (in rather pseudocode) is, I create a UDP socket with
sock_create(AF_INET, SOCK_DGRAM, IPPROTO_UDP, &sk);
where sk is the socket pointer.
So, instead of actively polling the kernel for new UDP data, I registered the data ready callback with
sk->sk_data_ready = myudp_data_ready;
And here is the full code of the myudp_data_ready function:
void myudp_data_ready(struct sock *sk) {
struct sk_buff *skb;
int err;
if ((skb = skb_recv_datagram(sk, 0, 1, &err)) == NULL) {
goto Bail;
}
// ...
// HERE, MY CUSTOM UDP-BASED PROTOCOL WILL BE IMPLEMENTED
// ...
skb_free_datagram(sk, skb);
return;
Bail:
return;
}
The Problem
The problem now is, that I get all UDP packets perfectly fine.
The skb_recv_datagram function is returning a socket buffer that I can handle.
However, after some time, it stops working.
What I already tried
In /proc/net/udp I can see, that the rx_queue is growing until it's full and than packets are dropped.
This is, where I do not get any packets anymore in my code (obviously).
This seems odd.
If I understood correctly, the Kernel uses a reference count in socket buffers.
If this count drops to 1, the buffer is free'd and unlinked from the receive queue.
I had a look in to the skb->users field, which is supposed to be the reference count.
It is set to 1, which means, that my code is the only place holding a reference to the skb.
But neither skb_free_datagram nor kfree_skb seems to free the buffer, as the rx_queue keeps growing.
And I have no clue why.
Do you have any advice?
Am I missing something?
Some more information
I am using Ubuntu 20.04 with Kernel version 5.4.0-52.
I have a simple user-land application sending UDP packets to a specific port where the kernel module is listening on.
Thank you for your help.
I need to create two sockets listening on the same IP:port but on different interfaces:
socket0 receives UDP traffic sent to 224.2.2.2:5000 on interface eth0
socket1 receives UDP traffic sent to 224.2.2.2:5000 on interface eth1
It seemed pretty straight forward until I realized that Linux merges all of that into the same traffic. For example, say there's only traffic on eth1 and there's no activity on eth0. When I first create socket0 it won't be receiving any data but as soon as I create socket1 (and join the multicast group) then socket0 will also start receiving the same data. I found this link that explains this.
Now this actually makes sense to me because the only moment when I specify the network interface is when joining the multicast group setsockopt(socket,IPPROTO_IP,IP_ADD_MEMBERSHIP,...) with ip_mreq.imr_interface.s_addr. I believe this specifies which interface joins the group but has nothing to do with from which interface your socket will receive from.
What I tried so far is binding the sockets to the multicast address and port, which behaves like mentioned above. I've tried binding to the interface address but that doesn't work on Linux (it seems to do so on Windows though), you don't receive any traffic on the socket. And finally, I've tried binding to INADDR_ANY but this isn't what I want since I will receive any other data sent to the port regardless of the destination IP, say unicast for example, and it will still not stop multicast data from other interfaces.
I cannot use SO_BINDTODEVICE since it requires root privileges.
So what I want to know is if this is possible at all. If it can't be done then that's fine, I'll take that as an answer and move on, I just haven't been able to find any way around it. Oh, and I've tagged the question as C because that's what we're using, but I'm thinking it really might not be specific to the language.
I haven't included the code for this because I believe it's more of a theoretical question rather than a problem with the source code. We've been working with sockets (multicast or otherwise) for a while now without any problems, it's just this is the first time we've had to deal with multiple interfaces. But if you think it might help I can write some minimal working example.
Edit about the possible duplicate:
I think the usecase I'm trying to achieve here is different. The socket is supposed to receive data from the same multicast group and port (224.2.2.2:5000 in the example above) but only from one specific interface. To put it another way, both interfaces are receiving data from the same multicast group (but different networks, so data is different) and I need each socket to only listen on one interface.
I think that question is about multiple groups on same port, rather than same group from different interfaces. Unless there's something I'm not seeing there that might actually help me with this.
Yes, you can do what you want on Linux, without root privileges:
Bind to INADDR_ANY and set the IP_PKTINFO socket option. You then have to use recvmsg() to receive your multicast UDP packets and to scan for the IP_PKTINFO control message. This gives you some side band information of the received UDP packet:
struct in_pktinfo {
unsigned int ipi_ifindex; /* Interface index */
struct in_addr ipi_spec_dst; /* Local address */
struct in_addr ipi_addr; /* Header Destination address */
};
The ipi_ifindex is the interface index the packet was received on. (You can turn this into an interface name using if_indextoname() or the other way round with if_nametoindex().
As you said on Windows the same network functions have different semantics, especially for UDP and even more for multicast.
The Linux bind() semantics for the IP address for UDP sockets are mostly useless. It is essentially just a destination address filter. You will almost always want to bind to INADDR_ANY for UDP sockets since you either do not care to which address a packet was sent or you want to receive packets for multiple addresses (e.g. receiving unicast and multicast).
Is it possible for a Linux kernel module to transparently detour the packet coming from upper layer (i.e. L2,L3) and NIC? For example, 1) a packet arrives from a NIC, the module gets the packet (do some processing on it) and delivers back to tcp/ip stack or 2) an app sends data, the module gets the packet (do some processing) and then, delivers the packet to an output NIC.
It is not like a sniffer, in which a copy of the packet is captured while the actual packet flow continues.
I thought on some possibilities to achieve my goal. I thought in registering a rx_handler in the kernel to get access to the incoming packets (coming from a NIC), but how to delivers back to the kernel stack? I mean, to allow the packet to follow the path that it should have taken without the module in the middle.
Moreover, let's say an app is sending a packet through TCP protocol. How the module could detour the packet (to literally get the packet)? Is it possible? In order to send it out through the NIC, I think dev_queue_xmit() does the job, but I'm not sure.
Does anyone know a possible solution? or any tips?
Basically, I'd like to know if there is a possibility to put a kernel module between the NIC and the MAC layer.. or in the MAC layer to do what I want. In positive case, does anyone has any hint like main kernel functions to use for those purposes?
Thanks in advance.
Yes. You can hook into kernel networking stack by providing customized callback in place of default sk_data_ready function.
static void my_sk_data_ready(struct sock *sk, int len) {
call_customized_logic(sk, len);
sock_def_readable(sk, len); /* call default callback or not call it */
}
Usage:
sk->sk_data_ready = my_sk_data_ready;
Im working in a FreeBSD environment. I need to listen to a raw socket (it's actually an AF_MAP but it's said to be based on a raw socket) and extract the information that passes through it. This socket allows a protocol/software (written in c) to communicate from user-space to kernel-space. I then need to write this information on a json file.
1) How do i listen to this socket?
2) Which utilities/programms do i need to use to monitor this socket and to retrieve the data
3) What really passes in a socket? im thinking bytes. If so, how do i extract higher level information from the socket? Do i need to count the bytes and then convert them to higher level logic?
Im very new to sockets, so please forgive me for whole's im my logic.
Thanks!
You can use listen()
Suricata is pretty good for monitoring and retrieving data
This is a pretty good read for an intro to sockets: http://www.linuxhowtos.org/C_C++/socket.htm
References:
listen(): 'man listen' in command line, or http://linux.die.net/man/2/listen
Suricata: https://redmine.openinfosecfoundation.org/projects/suricata/wiki/Interacting_via_Unix_Socket
Hope this helps.
I want to pass message between two devices, for an example devices are PCs. Currently I am using UDPServer.c and UDPClient.c for reference. By using this reference, I can pass messages, now I want to implement both side listen and send, also I want to create an API for sending message, that API can be used by other functions. May I need to use two different port for sending message and receiving message?? what is the best way to set up UDP socket programming for message passing??
From your description, it doesn't look like you need any more than what sendto()/recvfrom() already do. You might as well treat them as your "API" for message passing. Once you set up/open the socket, just send/recv as needed. You don't need to worry about different ports for sending/receiving; your example is fine. FYI, you can sendto/recvfrom on the same socket.