I have written a linux networking driver.
This is my "hard_header" function:
int snull_header(struct sk_buff *skb, struct net_device *dev,
unsigned short type, void *daddr, void *saddr,
unsigned int len)
{
struct ethhdr *eth = (struct ethhdr *)skb_push(skb,ETH_HLEN);
pr_err("inside snull_header\n");
pr_err("THE DATA TO SEND BEFORE ADDITION IS:%s\n", skb->data);
pr_err("THE SOURCE IS:%s\n", (char*)saddr);
pr_err("THE DEST IS:%s\n", (char*)daddr);
eth->h_proto = htons(type);
memcpy(eth->h_source, saddr ? saddr : dev->dev_addr, dev->addr_len);
memcpy(eth->h_dest, daddr ? daddr : dev->dev_addr, dev->addr_len);
eth->h_dest[ETH_ALEN-1] ^= 0x01; /* dest is us xor 1 */
pr_err("THE DATA TO SEND AFTER ADDITION IS:%s\n", skb->data);
return (dev->hard_header_len);
}
This is the definition of pr_err (from printk.h):
#define pr_err(fmt, ...) \
printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
When I run load this driver and try to send packet, I see all the prints, but instead of the strings of skb->data, source and destination I see gibberish.
My guess is that it's related somehow to the fact I'm referring to kernel memory, but on the other hand, this is what printk is for.
How can I print correctly these strings?
For the saddr and daddr, you can not print them in this way. You can print them with:
pr_err("THE SOURCE IS:%d.%d.%d.%d\n",
(0xff & saddr),
(0xff00 & saddr) >> 8,
(0xff000000 & saddr) >> 16
(0xff000000 & saddr) >>24);
For the skb->data, it's not null('\0') terminated so you can not printed as string with the format "%s". the limit of the skb->data determined by skb->len. You can print the content of the skb->data in this way.
int i;
for (i=0; i<skb->len; i++)
printk("%c", skb->data+i );
The contents of skb->data vary depending on which networking layer you are printing it. It can contain the header of previous layers even. So get the length of the data. And then print the data bytewise for the length. Then analyse what it contains. skb->data_len gives the size of data.
Related
I want to check the Ethernet header of packet that is porececed by iptables (1.4), so i need to write a module that catch packet and apply my function. I am looking for the mac destination value in the Ethernet header (just for test purpose), so the code should be like this:
static bool match(const struct sk_buff *skb, struct xt_action_param *par)
{
struct ethhdr *hdr;
hdr = eth_hdr(skb);
printk(KERN_INFO "hdr->h_dest 0x%x\n", hdr->h_dest);
printk(KERN_INFO "MACPROTO=%04x\n", hdr->h_proto);
The problem is that i cant get the correct value, i have some thing that is not even in the real frame (i checked that with Wireshark), so is it the right function to get Ethernet header attributs?
update:
i used the solution presented in the post, but still have wrong output, it's like if the structure point to wrong place
This image show result when i use nc to send "aaa" string, the ethernet header should be the same in the to frame, but as present in the result, it's not.
struct ethhdr is defined as such:
/*
* This is an Ethernet frame header.
*/
struct ethhdr {
unsigned char h_dest[ETH_ALEN]; /* destination eth addr */
unsigned char h_source[ETH_ALEN]; /* source ether addr */
__be16 h_proto; /* packet type ID field */
} __attribute__((packed));
But your code is trying to print that byte array using %x:
printk(KERN_INFO "hdr->h_dest 0x%x\n", hdr->h_dest);
That makes no sense, and probably causes a compiler warning to be generated. This is kernel code - you are using -Werror and -Wall, right?
Good news: printk supports printing MAC addresses directly. From documentation/printk-formats.txt:
MAC/FDDI addresses:
%pM 00:01:02:03:04:05
%pMR 05:04:03:02:01:00
%pMF 00-01-02-03-04-05
%pm 000102030405
%pmR 050403020100
For printing 6-byte MAC/FDDI addresses in hex notation. The 'M' and 'm'
specifiers result in a printed address with ('M') or without ('m') byte
separators. The default byte separator is the colon (':').
Where FDDI addresses are concerned the 'F' specifier can be used after
the 'M' specifier to use dash ('-') separators instead of the default
separator.
For Bluetooth addresses the 'R' specifier shall be used after the 'M'
specifier to use reversed byte order suitable for visual interpretation
of Bluetooth addresses which are in the little endian order.
Passed by reference.
So you can just use this:
printk(KERN_INFO "hdr->h_dest 0x%pM\n", hdr->h_dest);
These format specifiers are provided anywhere vsnprintf is used. Here's an example:
switch (dev->type) {
case ARPHRD_ETHER:
nf_log_buf_add(m, "MACSRC=%pM MACDST=%pM MACPROTO=%04x ",
eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest,
ntohs(eth_hdr(skb)->h_proto));
return;
default:
break;
}
I have a list of IP addresses, stored like this:
char IP_addresses_list[] = {
"157.55.130", /* 157.55.130.0/24 */
"157.56.52", /* 157.56.52.0/24 */
"157.12.53", /* 157.12.53.0/24 */
...
};
I get the IP address from the sniffed packet (casting it to struct iphdr *iph = (struct iphdr *)(packet + sizeof(struct ether_header)); I convert it in a character string using inet_ntop; finally, I compare the IP address from the packet with the ones in the list with the following code:
/*
* input: IP address to search in the list
* output: 1 if IP address is found in the list, 0 otherwise
*/
int find_IP_addr(char *server) {
int ret = 0;
int i, string_size1, string_size2;
char *copied_server, *copied_const_char;
char *save_ptr1, *save_ptr2;
char dot[2] = ".";
/* Here I store the IP address from the packet */
char first_IPaddr_pkt[4], second_IPaddr_pkt[4], third_IPaddr_pkt[4];
/* Here I store the IP address from the list */
char first_IPaddr_list[4], second_IPaddr_list[4], third_IPaddr_list[4];
string_size1 = strlen(server)+1;
copied_server = (char *)malloc(string_size1 * sizeof(char));
strcpy(copied_server, server);
/* I store and compare the first three bits of the IP address */
strcpy(first_IPaddr_pkt, strtok_r(copied_server, dot, &save_ptr1));
strcpy(second_IPaddr_pkt, strtok_r(NULL, dot, &save_ptr1));
strcpy(third_IPaddr_pkt, strtok_r(NULL, dot, &save_ptr1));
printf("tokenized %s, %s and %s\n", first_IPaddr_pkt, second_IPaddr_pkt, third_IPaddr_pkt);
/* Now I scan the list */
for (i=0; i<LIST_LENGTH; i++) {
/* I copy an address from the list */
string_size2 = strlen(IP_addresses_list[i])+1; // +1 for null character
copied_const_char = (char *)malloc(string_size2 * sizeof(char));
strcpy(copied_const_char, IP_addresses_list[i]);
/* Let's split the address from the list */
strcpy(first_IPaddr_list, strtok_r(copied_const_char, dot, &save_ptr2));
strcpy(second_IPaddr_list, strtok_r(NULL, dot, &save_ptr2));
strcpy(third_IPaddr_list, strtok_r(NULL, dot, &save_ptr2));
printf("tokenized %s, %s and %s\n", first_IPaddr_list, second_IPaddr_list, third_IPaddr_list);
/* I compare the first byte of the address from the packet I got and
the first byte of the address from the list:
if they are different, there's no reason to continue comparing
the other bytes of the addresses */
if (strcmp(first_IPaddr_pkt, first_IPaddr_list) != 0) {
continue;
}
else {
if (strcmp(second_IPaddr_pkt, second_IPaddr_list) != 0) {
continue;
}
else {
if (strcmp(third_IPaddr_pkt, third_IPaddr_list) != 0) {
continue;
}
else
/* All the bytes are the same! */
ret = 1;
}
}
free(copied_const_char);
}
free(copied_server);
return ret;
}
I'd like to make this more fast, without using strtok, strcmp, malloc or free.
In /usr/include/netinet/ip.h I see that addresses are
u_int32_t saddr;
u_int32_t daddr;
is it possible to compare without even using inet_ntop first, maybe just comparing the two addresses while they still are u_int32_t?
EDIT: here's a solution example for whoever will read this question.
#include <stdio.h>
#include <sys/types.h>
int main() {
// In the list I have: 104.40.0.0./13
int cidr = 13;
u_int32_t ipaddr_from_pkt = 1747488105; // pkt coming from 104.40.141.105
u_int32_t ipaddr_from_list = 1747451904; // 104.40.0.0
int mask = (-1) << (32 - cidr);
if ((ipaddr_from_pkt & mask) == ipaddr_from_list)
printf("IP address belongs to the given range!!!\n");
else printf ("failure\n");
return 0;
}
Thanks to iharob too for the bsearch hint.
I would avoid converting the binary data to strings. If you keep them binary then it's quite easy to compare:
match = (ip & listed_mask) == listed_ip;
"/24" is a mask. Means inly 24 highest bits are relevant. You convert it to binary mask as follows:
listed_mask = (-1) << (32 - 24);
The performance issues have nothing to do with strcmp(), malloc() is unnecessary though.
If you are only using IPv4 addresses you only need 16 characters to store it so you can remove malloc() and declare the temporary storage as an array.
But there is an important improvement if there are going to be many ip addresses in the list.
First you need to sort the list of IP addresses, and then use bsearch() to search for the right IP. This way the code will run in O(log(2n)) time which is a lot faster than O(N), specially for large N
My approach here would be:
simply use a strncat with ".0" to build valid IPv4 addresses.
use getaddrinfo with constant values for socket type etc to build a addrinfo struct
compare the relevant fields of the addrinfo.
Basically, the example from man getaddrinfo does all this.
The fastest way would be to store addresses in a dictionary, see this link
I am implementing raw sockets in C. In the code below, I am parsing the IP header I received from sender.
a. I will send back the ack as well so storing IP address received in a buffer(ret_ip).
b. I don't have another computer so using lo (local loop back) as my interface.
//first two printf statements are printing the right address, 10.100.207.74
//daddr SENT = 32.112.247.9saddr SENT = 36.112.247.9
How can I get it correct?
I think this problem is due to memcpy whose first argument is pointing to unsigned char while second argument is pointing to _be32.
What I actually want to do in my program is : ret_ip's first 4 bytes should contain the destination address and next 4 the source address. Then I will create IP header and make dest addr=source addr and source-addr=dest-addr. and send ACK to sender.
char* ParseIPHeader(unsigned char *packet,int len)
{
struct ethhdr *ethernet_header;
struct iphdr *ip_header;
char *ret_ip;
ethernet_header=(struct ethhdr *)packet;
if(ntohs(ethernet_header->h_proto)==ETH_P_IP)
{
if(len>=(sizeof(struct ethhdr)+sizeof(struct iphdr)))
{
ip_header=(struct iphdr*)(packet+sizeof(struct ethhdr));
ret_ip=malloc(2*(sizeof(ip_header->daddr)));
printf("Dest IP address: %s\n",inet_ntoa(ip_header->daddr));
printf("Source IP address: %s\n",inet_ntoa(ip_header->saddr));
memcpy(ret_ip,&(ip_header->daddr),sizeof(ip_header->daddr));
memcpy(ret_ip+4,&(ip_header->saddr),4);
printf("daddr SENT = %s",inet_ntoa(ret_ip));
printf("saddr SENT = %s",inet_ntoa(ret_ip+4));
}
else
printf("IP packet does not have full header\n");
}
else
{
//not an IP packet
}
return ret_ip;
}
Thanks :)
first problem is your memory allocation
ret_ip=malloc(2*(ip_header->daddr));
it should be
ret_ip=malloc(2*(sizeof(ip_header->daddr)));
but why you are not using the ip_hdr struct again ? for example
struct iphdr *ret_ip = malloc(sizeof(iphdr));
ret_ip->daddr = ip_header->saddr;
ret_ip->saddr = ip_header->daddr;
i suggest this solution is much easier ;)
I am trying to modify the IP header to include more IP options with the use of the libnetfiletr_queue. So far I have managed to come to the point where I obtain the packet as shown below.
if (nfq_set_mode(qh, NFQNL_COPY_PACKET, 0xffff) < 0) {
fprintf(stderr, "Unable to set nfq_set_mode\n");
exit(1);
}
Then I managed to go far as shown below,
static int my_callBack(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg,struct nfq_data *tb)
{
int id = 0;
int packet_len;
unsigned char *data;
struct nfqnl_msg_packet_hdr *packet_hdr;
unsigned char *data;
packet_hdr = nfq_get_msg_packet_hdr(tb);
if (packet_hdr) {
id = ntohl(packet_hdr->packet_id);
}
packet_len = nfq_get_payload(tb, &data);
if (packet_len >= 0) {
//print payload length
printf("payload_length = %d ", packet_len);
//modify packet ip header
}
return nfq_set_verdict(qh, id, NF_ACCEPT, 0, NULL);
}
But from here onwards I am a bit confused on how to proceed on modifying the IP header of the captured packet at //modify packet ip header comment.Example on a modification to the IP header (such as traffic class(IPV6)/ IP options/ version/ flags/ destination address) is ok since I only need to understand how the modification works :).
I have tried many resources and could not succeed in proceeding any further. You expert advice and help on this query will be very much appreciated. :)
Thank you very much :)
To modify the values of an IP header, start by defining a structure to represent your header. You find what the structure should be by reading the RFC spec for the protocol you're trying to access.
Here's a link to the RFC for IPv6: https://www.rfc-editor.org/rfc/rfc2460#section-3
The first row of the IPv6 header is a bit tricky, because they aren't using byte-aligned fields. The Version field is 4-bits wide, the Traffic Class is 8-bits wide, and the Flow Label is 20-bits wide. The whole header is 320 bits (40 bytes) and 256 of those are src and dest address. Only 64-bits are used for the other fields, so it's probably easiest to define your struct like this:
struct ipv6_hdr {
uint32_t row1;
uint16_t payload_length;
uint8_t next_header;
uint8_t hop_limit;
uint16_t src[8];
uint16_t dest[8];
};
To extract the row one values, you can use some masking:
#define VERSION_MASK 0xF0000000
#define TRAFFIC_CLASS_MASK 0x0FF00000
#define FLOW_LABEL_MASK 0x000FFFFF
struct ipv6_hdr foo;
...
nfq_get_payload(tb, &foo); // Just an example; don't overflow your buffer!
// bit-wise AND gets masked field from row1
uint8_t version = (uint8_t) ((foo->row1 & VERSION_MASK) >> 28); // shift (32-4) bits
Once you point your struct to the data payload, assuming your byte array matches this format, modifying the header values becomes simple assignment:
version = 6;
// bit-wise OR puts our value in the right place in row1
foo->row1 &= ~(VERSION_MASK) // clear out the old value first
foo->row1 = ((uint32_t) version << 28) | foo->row1;
I chose to make the src and dest addresses in the struct an array of 16-bit values because IPv6 addresses are a series of 8, 16-bit values. This should make it easy to isolate any given pair of bytes.
You will have to determine what format your data payload is in before applying the proper struct to it.
For info on how to create an IPv4 header, check its RFC: https://www.rfc-editor.org/rfc/rfc791#section-3.1
Hope this helps (you may have to fiddle with my code samples to get the syntax right, it's been a few months).
editing with info about checksums as requested in comments
Follow this RFC for generating checksums after modifying your header: https://www.rfc-editor.org/rfc/rfc1071
The key take-away there is to zero the checksum field in the header before generating the new checksum.
I am trying to send data between a client/Server, the data looks like
typedef Struct Message
{ int id;
int message_length;
char* message_str;
}message;
I am trying to Write and Read this message between a client and server constantly updating the elements in this struct. I have heard Writev may do the trick. i want to send a
message to the server and then the server pulls out the elements and uses those elements as conditionals to execute the proper method?
Assuming you want to do the serialization yourself and not use Google Protocol Buffers or some library to handle it for you, I'd suggest writing a pair of functions like this:
// Serializes (msg) into a flat array of bytes, and returns the number of bytes written
// Note that (outBuf) must be big enough to hold any Message you might have, or there will
// be a buffer overrun! Modifying this function to check for that problem and
// error out instead is left as an exercise for the reader.
int SerializeMessage(const struct Message & msg, char * outBuf)
{
char * outPtr = outBuf;
int32_t sendID = htonl(msg.id); // htonl will make sure it gets sent in big-endian form
memcpy(outPtr, &sendID, sizeof(sendID));
outPtr += sizeof(sendID);
int32_t sendLen = htonl(msg.message_length);
memcpy(outPtr, &sendLen, sizeof(sendLen));
outPtr += sizeof(sendLen);
memcpy(outPtr, msg.message_str, msg.message_length); // I'm assuming message_length=strlen(message_str)+1 here
outPtr += msg.message_length;
return (outPtr-outBuf);
}
// Deserializes a flat array of bytes back into a Message object. Returns 0 on success, or -1 on failure.
int DeserializeMessage(const char * inBuf, int numBytes, struct Message & msg)
{
const char * inPtr = inBuf;
if (numBytes < sizeof(int32_t)) return -1; // buffer was too short!
int32_t recvID = ntohl(*((int32_t *)inPtr));
inPtr += sizeof(int32_t);
numBytes -= sizeof(int32_t);
msg.id = recvID;
if (numBytes < sizeof(int32_t)) return -1; // buffer was too short!
int32_t recvLen = ntohl(*((int32_t *)inPtr));
inPtr += sizeof(int32_t);
numBytes -= sizeof(int32_t);
msg.message_length = recvLen; if (msg.message_length > 1024) return -1; /* Sanity check, just in case something got munged we don't want to allocate a giant array */
msg.message_str = new char[msg.message_length];
memcpy(msg.message_str, inPtr, numBytes);
return 0;
}
With these functions, you are now able to convert a Message into a simple char-array and back at will. So now all you have to do is send the char-array over the TCP connection, receive it at the far end, and then Deserialize the array back into a Message struct there.
One wrinkle with this is that your char arrays will be variable-length (due to the presence of a string which can be different lengths), so your receiver will need some easy way to know how many bytes to receive before calling DeserializeMessage() on the array.
An easy way to handle that is to always send a 4-byte integer first, before sending the char-array. The 4-byte integer should always be the size of the upcoming array, in bytes. (Be sure to convert the integer to big-endian first, via htonl(), before sending it, and convert it back to native-endian on the receiver via htonl() before using it).
Okay, I'll take a stab at this. I'm going to assume that you have a "message" object on the sending side and what you want to do is somehow send it across to another machine and reconstruct the data there so you can do some computation on it. The part that you may not be clear on is how to encode the data for communications and then decode it on the receiving side to recover the information. The simplistic approach of just writing the bytes contained in a "message" object (i.e. write(fd, msg, sizeof(*msg), where "msg" is a pointer to an object of type "message") won't work because you will end up sending the value of a virtual address in the memory of one machine to different machine and there's not much you can do with that on the receiving end. So the problem is to design a way to pass an two integers and a character string bundled up in a way that you can fish them back out on the other end. There are, of course, many ways to do this. Does this describe what you are trying to do?
You can send structs over socket, but you have to serialize them before sending the struct using boost serialization.
Here is a sample code :
#include<iostream>
#include<unistd.h>
#include<cstring>
#include <sstream>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
using namespace std;
typedef struct {
public:
int id;
int message_length;
string message_str;
private:
friend class boost::serialization::access;
template <typename Archive>
void serialize(Archive &ar, const unsigned int vern)
{
ar & id;
ar & message_length;
ar & message_str;
}
} Message;
int main()
{
Message newMsg;
newMsg.id = 7;
newMsg.message_length = 14;
newMsg.message_str="Hi ya Whats up";
std::stringstream strData;
boost::archive::text_oarchive oa(strData);
oa << newMsg;
char *serObj = (char*) strData.str().c_str();
cout << "Serialized Data ::: " << serObj << "Len ::: " << strlen(serObj) << "\n";
/* Send serObj thru Sockets */
/* recv serObj from socket & deserialize it */
std::stringstream rcvdObj(serObj);
Message deserObj;
boost::archive::text_iarchive ia(rcvdObj);
ia >> deserObj;
cout<<"id ::: "<<deserObj.id<<"\n";
cout<<"len ::: "<<deserObj.message_length<<"\n";
cout<<"str ::: "<<deserObj.message_str<<"\n";
}
you can compile the program by
g++ -o serial boost.cpp /usr/local/lib/libboost_serialization.a
you must have libboost_serialization.a statically compiled in your machine.
Keeping the sockets 'blocking' will be good and you have to devise for reading these structs from recv buffer.