Changing mac-address of packets with winpcap - struct

I am an absolute beginner with winpcap and c++. I want to read dump files, change the MAC-address(es) of some packets and delete the other packets. So in the end, my file is only supposed to contain the changed packets (same file as before, only without the 'unimportant' packtes).
I use Qt Creator, mingw, c++ and winpcap (of course).
What works so far: Reading from the file, displaying the mac/ip/whatever I want.
What does not work: Editing the packets.
I use some structs to read the data out of my packet:
struct pcap_hdr_s {
public:
uint32_t magic_number; /* magic number */
uint16_t version_major; /* major version number */
uint16_t version_minor; /* minor version number */
int32_t thiszone; /* GMT to local correction */
uint32_t sigfigs; /* accuracy of timestamps */
uint32_t snaplen; /* max length of captured packets, in octets */
uint32_t network; /* data link type */
};
struct pcaprec_hdr_s {
public:
uint32_t ts_sec; /* timestamp seconds */
uint32_t ts_usec; /* timestamp microseconds */
uint32_t incl_len; /* number of octets of packet saved in file */
uint32_t orig_len; /* actual length of packet */
} ;
struct ip_address{
public:
u_char byte1;
u_char byte2;
u_char byte3;
u_char byte4;
};
struct mac_adress{
public:
u_char mac1;
u_char mac2;
u_char mac3;
u_char mac4;
u_char mac5;
u_char mac6;
};
/*Ethernet header*/
struct eth_header{
public:
mac_adress dst_mac;
mac_adress src_mac;
uint16_t type;
};
/* IPv4 header */
struct ip_header{
public:
u_char ver_ihl; // Version (4 bits) + Internet header length (4 bits)
u_char tos; // Type of service
u_short tlen; // Total length
u_short identification; // Identification
u_short flags_fo; // Flags (3 bits) + Fragment offset (13 bits)
u_char ttl; // Time to live
u_char proto; // Protocol
u_short crc; // Header checksum
ip_address* saddr; // Source address
ip_address* daddr; // Destination address
//u_int op_pad; // Option + Padding --> optional
};
/* UDP header*/
struct udp_header{
public:
u_short sport; // Source port
u_short dport; // Destination port
u_short len; // Datagram length
u_short crc; // Checksum
};
This is my code so far for editing the packets:
void changeMAC(QString wantedMac, QString file){
const u_char *pkt_data;
struct pcap_pkthdr *header;
pcap_t *pd;
pcap_dumper_t *pdumper;
eth_header *ethHeader;
ip_header *ipHeader;
int res = 0;
QString check;
pd = pcap_open_dead(DLT_EN10MB, 65535);
/*output file = input file*/
pdumper = pcap_dump_open(pd, qPrintable(file));
while((res = pcap_next_ex(pd, &header, &pkt_data)) >= 0){//iterate through the packets in the file
ipHeader = (ip_header *)(pkt_data + 14);
if(ipHeader->proto == 17){ //I only have a look at udp
ethHeader = (eth_header *)(pkt_data); //check if this is the MAC I want to cahnge
check.clear();
check.append(ethHeader->dst_mac.mac1);
check.append(ethHeader->dst_mac.mac2);
check.append(ethHeader->dst_mac.mac3);
check.append(ethHeader->dst_mac.mac4);
check.append(ethHeader->dst_mac.mac5);
check.append(ethHeader->dst_mac.mac6);//'check' contains the current MAC of the packet
if(wantedMac.contains(check.toAscii().toHex())){ //if 'check' contains the MAC I was looking for
/*
* Create fake IP header and put UDP header
* and payload in place
* --> how to do this for all wanted packets?
*/
pcap_dump((u_char *)pdumper, header, pkt_data); //write changed packet to file
}
else{
//delete the packet --> how?
}
}
}
pcap_close(pd);
pcap_dump_close(pdumper);
}
I have had a look at this, and it helped, but just up to this point. My code is all I have so far.
Edit: As a first step, I would also be happy if I were able to save the edited packets in a second file, not in the file the packets come from. But how to edit them?

Ok, I figured out something by myself. It might not be the most elegant solution, but it works.
QString string;
QStringList mac;
int result=0;
bool ok;
QChar d
string=mac[0];//first part of mac address to string
for(int i=0; i<string.length();i++){//string to ascii hex and int
d=string.toUInt(&ok,16);
result=d.unicode();
}
ethHeader->dst_mac.macPart1=result;//allocate, go on for next part of macaddress

Related

msg not getting printed in struct msghdr in linux kernel

I am using kernel 4.5.3 , and i am working on to implement new Layer 4 transport protocol (essentially a new socket). From user space app, when i send data through my new socket, i am receiving it in kernel. In kernel i am trying to unwrap the buffer and trying to verify if all is correct. The user data send from user space is wrapped into struct msghdr structure by socket layer of linux kernel (so i this portion has to be bug free). It is this structure i am trying to print in my kernel handler of the new socket specific sendmsg fn. Though i am able to print all the fields, except when i print the actual msg send (char *) , it is printing blank.
Below is the dump fn i have written:
static
void dump_msg_hdr(struct msghdr *msg){
struct sockaddr_in *dest_addr = NULL;
struct in_addr dest_ip;
char ipv4[32], *usermsg = NULL;
struct iovec *iov = NULL;
memset(&dest_addr, 0, sizeof(struct sockaddr_in));
memset(&dest_ip, 0, sizeof(struct in_addr));
dest_addr = (struct sockaddr_in *)msg->msg_name;
dest_ip = dest_addr->sin_addr;
getDotDecimalIpV4(htonl(dest_ip.s_addr), ipv4);
printk(KERN_INFO "Dest ip address = %s", ipv4);
iov = msg->msg_iter.iov;
usermsg = (char *)kmalloc(iov->iov_len, GFP_USER);
memcpy(usermsg, iov->iov_base, iov->iov_len);
printk(KERN_INFO "user msg = %s\n", usermsg);
printk(KERN_INFO "msg->msg_iter.count (no of data bytes) = %d", msg->msg_iter.count);
printk(KERN_INFO "iov_base = 0x%x, iov_len = %d\n", iov->iov_base, iov->iov_len);
printk(KERN_INFO "msg->msg_iter.nr_segs (no of iovec segments) = %d", msg->msg_iter.nr_segs);
kfree(usermsg);
}
output
Dest ip address = 192.168.6.6
user msg = <blank>
sg->msg_iter.count (no of data bytes) = 27
iov_base = 0xbf95c153, iov_len = 27
msg->msg_iter.nr_segs (no of iovec segments) = 1
structures
struct iov_iter {
int type;
size_t iov_offset;
size_t count;
const struct iovec *iov; /* SIMPLIFIED - see below */
unsigned long nr_segs;
};
47 struct msghdr {
48 void *msg_name; /* ptr to socket address structure */
49 int msg_namelen; /* size of socket address structure */
50 struct iov_iter msg_iter; /* data */
51 void *msg_control; /* ancillary data */
52 __kernel_size_t msg_controllen; /* ancillary data buffer length */
53 unsigned int msg_flags; /* flags on received message */
54 struct kiocb *msg_iocb; /* ptr to iocb for async requests */
55 };

how to implement splice_read for a character device file with uncached DMA buffer

I have a character device driver. It includes a 4MB coherent DMA buffer. The buffer is implemented as a ring buffer. I also implemente the splice_read call for the driver to improve the performance. But this implementation does not work well. Below is the using example:
(1)splice the 16 pages of device buffer data to a pipefd[1]. (the DMA buffer is managed as in page unit).
(2)splice the pipefd[0] to the socket.
(3)the receiving side (tcp client) receives the data, and then check the correctness.
I found that the tcp client got errors. The splice_read implementation is show below (I steal it from the vmsplice implementation):
/* splice related functions */
static void rdma_ring_pipe_buf_release(struct pipe_inode_info *pipe,
struct pipe_buffer *buf)
{
put_page(buf->page);
buf->flags &= ~PIPE_BUF_FLAG_LRU;
}
void rdma_ring_spd_release_page(struct splice_pipe_desc *spd, unsigned int i)
{
put_page(spd->pages[i]);
}
static const struct pipe_buf_operations rdma_ring_page_pipe_buf_ops = {
.can_merge = 0,
.map = generic_pipe_buf_map,
.unmap = generic_pipe_buf_unmap,
.confirm = generic_pipe_buf_confirm,
.release = rdma_ring_pipe_buf_release,
.steal = generic_pipe_buf_steal,
.get = generic_pipe_buf_get,
};
/* in order to simplify the caller work, the parameter meanings of ppos, len
* has been changed to adapt the internal ring buffer of the driver. The ppos
* indicate wich page is refferred(shoud start from 1, as the csr page are
* not allowed to do the splice), The len indicate how many pages are needed.
* Also, we constrain that maximum page number for each splice shoud not
* exceed 16 pages, if else, a EINVAL will return. If a high speed device
* need a more big page number, it can rework this routing. The off is also
* used to return the total bytes shoud be transferred, use can compare it
* with the return value to determint whether all bytes has been transfered.
*/
static ssize_t do_rdma_ring_splice_read(struct file *in, loff_t *ppos,
struct pipe_inode_info *pipe, size_t len,
unsigned int flags)
{
struct rdma_ring *priv = to_rdma_ring(in->private_data);
struct rdma_ring_buf *data_buf;
struct rdma_ring_dstatus *dsta_buf;
struct page *pages[PIPE_DEF_BUFFERS];
struct partial_page partial[PIPE_DEF_BUFFERS];
ssize_t total_sz = 0, error;
int i;
unsigned offset;
struct splice_pipe_desc spd = {
.pages = pages,
.partial = partial,
.nr_pages_max = PIPE_DEF_BUFFERS,
.flags = flags,
.ops = &rdma_ring_page_pipe_buf_ops,
.spd_release = rdma_ring_spd_release_page,
};
/* init the spd, currently we omit the packet header, if a control
* is needed, it may be implemented by define a control variable in
* the device struct */
spd.nr_pages = len;
for (i = 0; i < len; i++) {
offset = (unsigned)(*ppos) + i;
data_buf = get_buf(priv, offset);
dsta_buf = get_dsta_buf(priv, offset);
pages[i] = virt_to_page(data_buf);
get_page(pages[i]);
partial[i].offset = 0;
partial[i].len = dsta_buf->bytes_xferred;
total_sz += partial[i].len;
}
error = _splice_to_pipe(pipe, &spd);
/* use the ppos to return the theory total bytes shoud transfer */
*ppos = total_sz;
return error;
}
/* splice read */
static ssize_t rdma_ring_splice_read(struct file *in, loff_t *ppos,
struct pipe_inode_info *pipe, size_t len, unsigned int flags)
{
ssize_t ret;
MY_PRINT("%s: *ppos = %lld, len = %ld\n", __func__, *ppos, (long)len);
if (unlikely(len > PIPE_DEF_BUFFERS))
return -EINVAL;
ret = do_rdma_ring_splice_read(in, ppos, pipe, len, flags);
return ret;
}
The _splice_to_pipe is just the same one as the splice_to_pipe in kernel. As this function is not an exported symbol, so I re-implemented it.
I think the main cause is that the some kind of lock of pages are omitted, but
I don't know where and how.
My kernel version is 3.10.

How to make the copy of the packet?

I want to make a copy of the packet (and send it to queue that is made by me) at the Net Filter hook.
Will skb_copy work for me? i also have to add the seq no before the packet,skb_reserve will do that?
I have written the following code to capture packet
unsigned int hook_func(unsigned int hooknum,
struct sk_buff **skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
if (strcmp(in->name, drop_if) == 0) {
printk("Dropped packet on %s...\n", drop_if);
return NF_DROP;
} else {
return NF_ACCEPT;
}
}
/* Initialisation routine */
int init_module()
{
/* Fill in our hook structure */
nfho.hook = hook_func; /* Handler function */
nfho.hooknum = NF_IP_PRE_ROUTING; /* First hook for IPv4 */
nfho.pf = PF_INET;
nfho.priority = NF_IP_PRI_FIRST; /* Make our function first */
nf_register_hook(&nfho);
return 0;
}
I do agree with Rachit Jain, unless you have a valid reason to do this in Kernel space, I do suggest you use libpcap to do it in user-space.
Anyhow, if you just wanna copy the packet and then amend some data, I suggest you allocate a new skb with enough space to copy the data you already have in the skb you received + enough space to add a header.
Here's a code that I once used, it doesn't do any copying from an already existing skb but it can be useful to you. I am crafting a special kind of ICMP echo message here
int sendICMPEcho(unsigned char *msg, unsigned int length,
__be32 source, __be32 dest)
{
struct ethhdr *eth;
struct iphdr *iph;
struct icmphdr *icmph;
struct sk_buff *newPacket;
unsigned char *data;
unsigned int skbSize = length + sizeof(struct icmphdr)
+ sizeof(struct iphdr)
+ sizeof(struct ethhdr);
/* Allocate the skb */
newPacket = alloc_skb(skbSize, GFP_ATOMIC);
if(newPacket == NULL)
return SEND_FAIL_MEMORY;
/* Reserve the headers area */
skb_reserve(newPacket, sizeof(struct icmphdr)
+ sizeof(struct iphdr)
+ sizeof(struct ethhdr));
/* Extend the data area from 0 to the message length */
data = skb_put(newPacket, length);
/* Copy the data from the message buffer to the newPacket */
memcpy(data, msg, length);
/************** ICMP HEADER***************/
/* skb_push - pushing the icmp header in the packet data */
icmph = (struct icmphdr *) skb_push(newPacket,
sizeof(struct icmphdr));
/*set ICMP header here */
icmph->type = ICMP_ECHO;
icmph->code = 100; /* Our magic number */
icmph->un.echo.id = 0;
icmph->un.echo.sequence = htons(sendCounter);
icmph->checksum= 0;
icmph->checksum = in_cksum((unsigned short *)icmph,
sizeof(struct icmphdr) + length);
/************** END ICMP HEADER**************/
/************** IP HEADER ***************/
iph = (struct iphdr *) skb_push(newPacket,
sizeof(struct iphdr));
/* set IP header here */
iph->ihl = 5;/* 5 * 32(bits) */
iph->version = 4;
iph->tos = 255; /* Just a magic number - remove it */
iph->tot_len = htons( sizeof(struct iphdr)
+ sizeof(struct icmphdr)
+ length);
iph->id = 0;
iph->frag_off = 0; /* No fragementation */
iph->ttl = 65;
iph->protocol = IPPROTO_ICMP;
iph->saddr = source;
iph->daddr = dest;
iph->check = 0;
iph->check = in_cksum((unsigned short *)iph, sizeof(struct iphdr));
/************** END IP HEADER ***************/
/*WARNING: THE CODE BELOW SHOULD BE REPLACED BY SOMETHING MORE ROBUST
THAT USES THE KERNEL ROUTING!
AND USES IP_LOCAL_OUT INSTEAD OF WHAT WE ARE DOING */
/* Set up the net-device for the new packet */
/* In my code, there was a function findDeviceByIp that does the routing and return which net_device to use for transmission*/
newPacket->dev = findDeviceByIP(source);
if(newPacket->dev == NULL)
{
kfree_skb(newPacket);
return SEND_DEV_FAIL;
}
/************** ETH HEADER ***************/
eth = (struct ethhdr *) skb_push(newPacket, sizeof(struct ethhdr));
if(strcmp(newPacket->dev->name, "wlan0") == 0)
memcpy(eth->h_dest, wifiMAC, 6);
else if(strcmp(newPacket->dev->name, "eth0") == 0)
memcpy(eth->h_dest, etherMAC, 6);
else
{
kfree_skb(newPacket);
return SEND_FAIL_SEND;
}
memcpy(eth->h_source, newPacket->dev->dev_addr, 6);
eth->h_proto = htons(ETH_P_IP);
/************** END ETH HEADER ***************/
dev_queue_xmit(newPacket);/* Transmite the packet */
/* END OF THE WARNING AREA */
++sendCounter;
return SEND_SUCCESS;
}
There are many helper functions provided by linux kernel to work on skb's. it depends on the usecase which one you want to use.
skb_clone ==>copies the skbuff header and increments the reference counter for data buffer. If you are only interested in modifying the skbuff header then you can use skb_clone
skb_copy ==> copies skbuff header, data buffer as well as fragments. Use when you are interested in modifying the data in main buffer as well as in fragments buffer
pskb_copy ==> copies skbuff header + only the data buffer but not the fragments, thus if you want to modify skb except fragment buffer then you can use this one. which takes headroom also as arguement.
Better read the helper function provided by linux kernel(net/core/skbuff.c) to do the skb operations efficiently and to avoid any pitfalls.

how to intercept IP packets on linux

I am trying to achieve this goal on Linux (not sure if it is possible and how if so):
I need to write a program to intercept all IP packets on an interface, and deliver this packet to a specific set of user-space programs. By intercept I mean, an IP packet is captured by my program (maybe a kernel module or a special user-space program), then this packet does not go through IP stack anymore. For example, let's say the OS is running a lot of processes (either in kernel-space or user-space), A, B, C, D..., etc. If an IP packet is received at an interface, say eth2, I only want A,B sees this packet, and all other processes do even know the existence of this packet.
Can someone put me into the right direction? Thanks a lot!
What I suggest is that you re-examine if it's really necessary to intercept packets in the way you describe. This sounds like you have not completely understood, as pointed above, how networking works.
First of all unless your programs somehow magically manage to read raw network packets instead of using standard sockets, they're not receiving traffic destined to each other anyway. Each socket has a port associated with it and only one process can bind onto same port in the same host (the socket actually being nothing more than a pair of a port and a host address).
In case you're actually reading raw network packets in your programs and this is necessary, you most likely should not be running them on the same host. Instead use virtualization to put programs which aren't allowed to see packets destined to each other in different virtual hosts and thus separate them completely instead of using a rather complex programmatic solution.
If everything else fails, you might want to take a good look at libpcap which allows you to capture network packets, even from several programs simultaneously. Albeit you'd have to run those as root but that's necessary anyway for being able to put a network interface into promiscuous mode and so on and so fort. Here is a simple example for reading little something from the network, you can find plenty more from libpcap homepage and associated
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pcap.h>
/* IP header (from tcpdump examples) */
struct sniff_ip {
u_char ip_vhl; /* version << 4 | header length >> 2 */
u_char ip_tos; /* type of service */
u_short ip_len; /* total length */
u_short ip_id; /* identification */
u_short ip_off; /* fragment offset field */
u_char ip_ttl; /* time to live */
u_char ip_p; /* protocol */
u_short ip_sum; /* checksum */
struct in_addr ip_src, ip_dst; /* source and dest address */
};
/* callback function for pcap_loop */
void cllbck(u_char * args,
const struct pcap_pkthdr *hdr, const u_char * pkt)
{
const struct sniff_ip *ip = (struct sniff_ip *) (pkt + 14);
fprintf(stderr, "Sniffed a packet with length %d.\n", hdr->len);
fprintf(stderr, "IP version %d.\n", ip->ip_vhl >> 4);
}
int main(int argc, char *argv[])
{
char *dev; /* device name */
char errbuf[PCAP_ERRBUF_SIZE]; /* buffer for libpcap errmsgs */
pcap_t *cap; /* libpcap capture session */
char *filt = "host 127.0.0.1"; /* capture filter */
struct bpf_program fp; /* compiled filter */
struct pcap_pkthdr hdr; /* packet header from libpcap */
const u_char *pkt; /* packet from libpcap */
dev = strdup(argv[1]);
if (dev == NULL) {
fprintf(stderr, "Invalid device.\n");
return 2;
}
/* open the device for live capture */
cap = pcap_open_live(dev, BUFSIZ, 1, 1000, errbuf);
if (cap == NULL) {
fprintf(stderr, "Opening device `%s´ failed: %s\n", dev, errbuf);
return 2;
}
/* compile the capture filter */
if (pcap_compile(cap, &fp, filt, 0, PCAP_NETMASK_UNKNOWN) < 0) {
fprintf(stderr, "Failed to parse filter `%s´: %s\n",
filt, pcap_geterr(cap));
return 2;
}
/* set the filter active for this session */
if (pcap_setfilter(cap, &fp) == -1) {
fprintf(stderr, "Couldn't install filter %s: %s\n",
filt, pcap_geterr(cap));
return 2;
}
/* pcap close will loop until an error if 2nd arg is < 0 */
pcap_loop(cap, -1, cllbck, NULL);
/* end session, pcap_loop has exited ie. an error has occurred */
pcap_close(cap);
return 0;
}
/* end of file */

Determining size of IP header. Why is it different from others?

Look at this code segment:
if(ip_header->protocol == IPPROTO_TCP)
{
tcp_header = (struct tcphdr*)(packet + sizeof(struct ethhdr) + ip_header->ihl*4);
/* Print the Dest and Src ports */
printf("Source Port: %d\n", ntohs(tcp_header->source));
printf("Dest Port: %d\n", ntohs(tcp_header->dest));
}
What i am confused is, in case of determining the size of other headers we normally do, sizeof(struct tcphdr) or sizeof(struct ethhdr) but for IP header size, we don't do sizeof instead we do ip_header->ihl*4. Why is it so? Also what is this 4 for?
Here is how the struct declaration of IP heder:
00116 struct iphdr {
00117 #if defined(__LITTLE_ENDIAN_BITFIELD)
00118 __u8 ihl:4,
00119 version:4;
00120 #elif defined (__BIG_ENDIAN_BITFIELD)
00121 __u8 version:4,
00122 ihl:4;
00123 #else
00124 #error "Please fix <asm/byteorder.h>"
00125 #endif
00126 __u8 tos;
00127 __u16 tot_len;
00128 __u16 id;
00129 __u16 frag_off;
00130 __u8 ttl;
00131 __u8 protocol;
00132 __u16 check;
00133 __u32 saddr;
00134 __u32 daddr;
00135 /*The options start here. */
00136 };
It's a problem of different units of measurement. The IHL field in the IP header is defined like this:
Internet Header Length is the length of the internet header in 32 bit
words.
And this size isn't fixed (because of valid but discouraged IP options). So one packet could have IHL=5, the next could have IHL=6 and so on.

Resources