LKM to lookup tcp_sock from packet - sockets

My goal is to write a LKM (Linux Kernel Module) which intercepts all TCP packets, lookups tcp_sock structure and based on some conditions, logs some information from tcp_sock structure (ex: tcpsock->snd_una).
This is how I am trying to achieve this: I am intercepting using netfilter on ubunutu (code below) but is there a way to access tcp_sock structure (look for NOTE in the code)?
I am not particular about netfilter. Please suggest if there is any other way of writing LKM to achieve this. It has to be a LKM.
unsigned int ip_packets_hook_func(unsigned int hooknum,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
struct iphdr *ipp = (struct iphdr *)skb_network_header(skb);
struct tcphdr *tcp_hdr;
struct tcp_sock *tcpsock;
if (!skb) {
return NF_ACCEPT;
}
if (ipp->protocol == IPPROTO_TCP ) { // Incomming packet is TCP
tcp_hdr = (struct tcphdr *) ((__u32 *)ipp + ipp->ihl);
tcpsock = (struct tcp_sock *) skb->sk;
if(ntohs(tcp_hdr->dest) != INTERESTED_PORT) {
return NF_ACCEPT;
}
printk("TCP ports: source: %d, dest: %d \n", ntohs(tcp_hdr->source),
ntohs(tcp_hdr->dest));
if(tcp_hdr->syn || tcp_hdr->fin || tcp_hdr->rst) {
printk("Flag: %s %s %s\n", tcp_hdr->syn? "SYN" : "",
tcp_hdr->fin? "FIN" : "",
tcp_hdr->rst? "RST" : "");
return NF_ACCEPT;
}
// **NOTE**
// skb->sk is NULL at this point.
// Get tcp_sock for this connection.
} else {
printk("Its not TCP\n");
}
return NF_ACCEPT;
}

I was able to achieve it by looking up inet hashtables (code snippet below). I am the server in this example. Make sure you lookup after 3 way handshake is complete.
const struct iphdr *iph;
const struct tcphdr *th;
struct sock *sk = NULL;
struct tcp_sock *tp;
.
.
.
.
sk = __inet_lookup_established(dev_net(skb->dev), &tcp_hashinfo,
iph->saddr, th->source,
iph->daddr, ntohs(th->dest),
skb->skb_iif);
// Sanity checks here.
tp = tcp_sk(sk);

Related

Error with sendto() function: Invalid Argument Error

I am working on writing a ping CLI program for linux and I have been getting errno 22: invalid argument in the sendto() function. I don't understand why, all the arguments seem to be correct.
Here is where I call the function:
// send echo request
bytesSent = sendto(socketFD, // socket file descriptor
(char*)&packet, PacketSize, // packet and size
0, // flags
(sockaddr*)DestinationAddr, (socklen_t)sizeof(DestinationAddr)); // destination address and size
'packet' looks like this:
(I call initializePacket() in the function where I call sendto())
struct PacketData {
icmphdr header;
char message[PacketSize - sizeof(header)]; // want total size to be 64 bytes
};
PacketData initializePacket(int &transmitted) {
PacketData packet = {};
packet.header.type = ICMP_ECHO; // set ICMP type to Echo
packet.header.un.echo.id = getpid() & 0xFFFF; // set id (ICMP field is 16 bits)
packet.header.checksum = 0; // fixed checksum because data is unchanging
packet.header.un.echo.sequence = transmitted++;
// fill up message
memset(&packet.message, '0', sizeof(packet.message));
packet.message[PacketSize - sizeof(packet.header) - 1] = '\0';
return packet;
}
'DestinationAddr' is this:
// variables needed to store IP Address
addrinfo* result;
sockaddr_in* DestinationAddr;
char ipString[INET_ADDRSTRLEN];
// get IP Address and store in result (passed by reference)
if (getIPAddress(argv[1], result) != 0) {
std::cout << "Invalid IP Address. Terminating ...\n";
exit(EXIT_FAILURE);
}
else {
DestinationAddr = (sockaddr_in*)result->ai_addr; // get struct from resulting linked list
void* address;
address = &DestinationAddr->sin_addr; // store IP Address
inet_ntop(result->ai_family, address, ipString, sizeof(ipString)); // convert binary IP to string
std::cout << "IP: " << ipString << std::endl;
}
And the getIPAddress() function is:
int getIPAddress(char* hostName, addrinfo* &result) {
addrinfo tempStruct = {0};
tempStruct.ai_family = AF_INET; // want IPv4
tempStruct.ai_socktype = SOCK_DGRAM; // set socket type to datagram
tempStruct.ai_flags = AI_PASSIVE; // fill in IP automatically
// get and validate IP address
return (getaddrinfo(hostName, &PortNo, &tempStruct, &result));
}
PortNo is defined as: const char PortNo = '0';
According to documentation icmp:
A user protocol may receive ICMP packets for all local sockets by opening a raw socket with the protocol IPPROTO_ICMP.
So, try creating your socket like that:
socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)
And, if you encounter EPERM error, then run your program as root.

is there a working vmci example?

I need a working VMCI socket example that does what UDP does, but without networking. There are many good code fragments in the vmci_sockets.h code, but not a full working template to expand on.
I believe that the server should look as follows:
#include "vmci_sockets.h"
#define BUFSIZE 2048
int main() {
int afVMCI = VMCISock_GetAFValue();
if ((sockfd_dgram = socket(afVMCI, SOCK_DGRAM, 0)) == -1) {
perror("socket");
goto exit;
}
struct sockaddr_vm my_addr = {0};
my_addr.svm_family = afVMCI;
my_addr.svm_cid = VMADDR_CID_ANY;
my_addr.svm_port = VMADDR_PORT_ANY;
if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof my_addr) == -1) {
perror("bind");
goto close;
}
if (getsockname(sockfd, (struct sockaddr *) &my_addr, &svm_size) == -1) {
perror("getsockname");
goto close;
}
if ((numbytes = recvfrom(sockfd, buf, sizeof buf, 0,
(struct sockaddr *) &their_addr, &svm_size)) == -1) {
perror("recvfrom");
goto close;
}
close:
return close(sockfd);
}
and for the client
#include <stdio.h>
#include <string.h>
#include "vmci_sockets.h"
#define BUFSIZE 128
int main() {
int afVMCI = VMCISock_GetAFValue();
int fd;
struct sockaddr_vm addr;
if ((fd = socket(afVMCI, SOCK_DGRAM, 0)) == -1) {
perror("socket");
return 1;
}
addr.svm_family = afVMCI;
addr.svm_cid = VMADDR_CID_ANY;
addr.svm_port = VMADDR_PORT_ANY;
bind(fd, (struct sockaddr *) &addr, sizeof addr);
struct sockaddr_vm serveraddr;
socklen_t svm_size = sizeof serveraddr;
{
int numbytes; char buf[BUFSIZE]; bzero(buf, BUFSIZE);
strcpy(buf, "hello there\n");
if ((numbytes = sendto(fd, buf, BUFSIZE, 0,
(const struct sockaddr *) &serveraddr, svm_size)) == -1) {
perror("sendto error");
goto close;
}
}
close:
close(fd);
VMCISock_ReleaseAFValueFd(fd);
return 0;
}
however, it's not working. there is not much documentation, e.g., how to troubleshoot. there is not information whether one can try both server and client within the same virtual machine for debugging purposes.
I tried to post to the vmware board, sent an email to their support, but no one seems to have a working example. because this is not standard socketry, though it is similar socketry, it is and is not followable.
anyone have a working example?
vmci is apparently not supported for vmplayer or vmware fusion. this is what the vmware support people told me:
I have been checking internally with our development team regarding
your request and incidentally could only generate interest if this was
a situation that is failing with vSphere. The final comment I have is
that we never meant to officially support this for VMware Fusion and
certain dependencies are on internal references only.
Unfortunately, we do not have any such vmci example which can be
shared publicly when it comes to VMware Fusion.

sendto() returning -1 in server

int main()
{
int servsocket,clientsocket;
struct sockaddr_in server,client;
FILE *file;
char filename[100];
char buf[1024];
servsocket=socket(AF_INET,SOCK_DGRAM,0);
server.sin_addr.s_addr=htonl(INADDR_ANY);
server.sin_port=htons(6003);
server.sin_family=AF_INET;
bind(servsocket,(struct sockaddr *) &server,sizeof(server) );
while(1){
int clientsize=0;
printf("Waiting for file requests \n");
recvfrom(servsocket,filename,sizeof(filename),0,(struct sockaddr *)&client,&clientsize);
file=fopen(filename,"r");
int size=0;
do
{
size=fread(buf,1,sizeof(buf),file);
printf("%d bytes read \n",size);
int sentbytes= sendto(servsocket,(const char *)buf,size,0, (struct sockaddr *) &client,sizeof(client));
printf("%d bytes sent ",sentbytes);
}while(size==sizeof(buf));
}
}
I am trying to make a simple program for file transfer using UDP. The problem is that sendto() always returns -1. This is the code for server.
There are quite a few issues with your code. The one you're seeing is that you're not filling in the variable client properly: the clientsize parameter is used for both input and output by the recvfrom system call, so you need to initialise it to the size of the client structure:
int clientsize = sizeof(struct sockaddr_in);
Another issue is that you're not 0-terminating the filename string:
n = recvfrom(...);
filename[n] = '\0';
Finally, you're not testing for errors (bind, recvfrom, sendto, etc.). This will get you into trouble, I promise.

How to use getaddrinfo()?

Im trying to make a simple program that takes in a string like www.google.com and returns the ip address...
What i have so far:
char* hostname = new char[www.size()+1];
std::copy(www.begin(), www.end(), hostname);
hostname[www.size()] = '\0';
struct addrinfo new_addr, *res;
getaddrinfo(www.c_str(), SERVICE.c_str(), &new_addr, &res);
cout << new_addr.ai_addr;
What are the 3rd of 4th parameters supposed to do? Does the getaddrinfo function modify the new_addr structure or what? I dont really understand the msdn documentation. After the hostname is resolved I want to connect a socket to it.
What if i leave the third parameter nullified?
Heres the code i developed so far.
char* hostname = new char[www.size()+1];
copy(www.begin(), www.end(), hostname);
hostname[www.size()] = '\0';
struct addrinfo *res;
struct in_addr addr;
getaddrinfo(hostname, NULL, 0, &res);
addr.S_un = ((struct sockaddr_in *)(res->ai_addr))->sin_addr.S_un;
server.sin_addr.s_addr = inet_addr(inet_ntoa(addr));
server.sin_port = htons(portno);
freeaddrinfo(res);
delete []hostname;
server.sin is declared elsewhere that i use to fill a socket in another method of my sockets class.
The MSDN documentation is very detailed and explains exactly what the various parameters are for. The third parameter lets you specify the type of socket that will be used with the results of the lookup. This allies the results to be optimized as needed. The fourth parameter returns the actual results. The documentation also contains a full example of how to use the function. So what example is unclear about what the documentation says?
Try this:
struct addrinfo hints = {0};
hints.ai_flags = 0;
hints.ai_family = AF_UNSPEC; // IPv4 and IPv6 allowed
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
struct addrinfo *res = NULL;
if (getaddrinfo(www.c_str(), SERVICE.c_str(), &hints, &res) == 0)
{
TCHAR szIPAddr[64];
DWORD szIPAddrLen;
SOCKET skt;
struct addrinfo *addr = res;
do
{
skt = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (skt == INVALID_SOCKET)
cout << "Unable to create socket, error " << WSAGetLastError() << endl;
else
{
szIPAddrLen = 64;
WSAAddressToString(addr->ai_addr, addr->ai_addrlen, NULL, szIPAddr, &szIPAddrLen);
cout << "Connecting to " << szIPAddr << " ..." << endl;
if (connect(skt, addr->ai_addr, addr->ai_addrlen) == 0)
{
cout << "Connected!" << endl;
break;
}
cout << "Unable to connect, error " << WSAGetLastError() << endl;
closesocket(skt);
skt = INVALID_SOCKET;
}
addr = addr->ai_next;
}
while (addr);
freeaddrinfo(res);
if (skt != INVALID_SOCKET)
{
// use skt as needed...
closesocket(skt);
}
}

why does my connect fail?

I am trying to do basic socket calls, and trying to connect to google.com but the connect call always fails and returns -1. Any reason why it must be failing
int main()
{
int sockfd;
struct addrinfo *ai;
char port[4];
if(sockfd = socket(AF_INET, SOCK_STREAM, 0) < 0) {
printf("socket return -1");
}
sprintf(port, "%d", 80);
if(getaddrinfo("www.google.com", port, NULL, &ai) < 0)
printf("-2\n");
if(connect(sockfd, ai->ai_addr, sizeof(*ai->ai_addr)) < 0)
printf("connect failed -1");
}
I believe the problem is with the parameter sizeof(*ai->ai_addr). ai->ai_addr returns a pointer to a sockaddr struct, and dereferencing brings you to the struct itself.
struct sockaddr {
unsigned short sa_family; // address family, AF_xxx
char sa_data[14]; // 14 bytes of protocol address
};
sizeof is returning the size of the entire struct, not the length of the address.
Try making the argument ai->ai_addrlen instead.