Change destination ip - c

I'm trying to create a kernel module that forward packets in certain conditions. Now I'm trying to do just a hard code test to forward a packet received in an interface and forward it to another interface. In this test I'm receiving a packet from 192.168.56.101 on eth0 and I want to forward this packet on eht1 for 192.168.57.103. In eth0 my ip is 192.168.56.102 and in eth1 my ip is 192.168.57.102. The transport protocol I'm using is a experimental protocol (253). The following code is just a simplified part of my code:
#define XOR_PROTOCOL 253
static unsigned int xor_pre_routing_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *))
{
struct iphdr *iph;
struct xorhdr *ptr;
char sip[15];
char sip2[15];
iph = ip_hdr(skb);
sprintf(sip, "%pI4", &iph->saddr);
sprintf(sip2, "%pI4", &iph->daddr);
// Check if is XOR protocol
if (iph->protocol == XOR_PROTOCOL) {
DEBUG("(Ogirinal) From %pI4 to %pI4.\n", &iph->saddr, &iph->daddr);
if (strcmp(sip, "192.168.56.101") == 0 && strcmp(sip2, "192.168.56.255") == 0) {
//iph->saddr = inet_addr("192.168.57.102");
iph->daddr = inet_addr("192.168.57.103");
DEBUG("(Modified) From %pI4 to %pI4.\n", &iph->saddr, &iph->daddr);
iph = ip_hdr(skb);
iph->check = 0;
ip_send_check (iph);
return NF_ACCEPT;
}
}
accept:
return NF_ACCEPT;
}
This hook in NF_INET_PRE_ROUTING. I also have a hook to just print source and destination ip in NF_INET_FORWARD, but there is no packet passing through this hook.
I'm testing with 3 linux virtual machine on virtual box, and I enabled the forward option in each vm. Is possible to forward packets in this scenario? What I'm doing wrong and what can I do to solve this problem?

The problem is the broadcast ip 192.168.56.255, with the ip 192.168.56.102 the packets were forwarded.

Related

Retrieve TCP MSS value from Socket Buffer

I am filtering packets on kernel level through LKM/netfilter using attributes from headers (TCP/IP) [linux tcp.h and ip.h].
Follows an example of the code:
static unsigned int Filter(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) {
struct ethhdr *ethh;
struct iphdr *iph; // ip header struct
struct tcphdr *tcph; // tcp header struct
ethh = eth_hdr(skb);
iph = ip_hdr(skb);
if (!(iph)){
return NF_ACCEPT;
}
if (iph->protocol == IPPROTO_TCP){ // TCP Protocol
tcph = tcp_hdr(skb);
if (iph->tot_len > htons(64)) return NF_ACCEPT;
if ((iph->ttl > htons(254))
|| (iph->ttl == htons(64) && tcph->window > htons(1024))
|| (iph->ttl <= htons(65) && tcph->window <= htons(1024) && ((iph->frag_off & IP_DF)==0)))
return NF_DROP;
return NF_ACCEPT;
}
return NF_ACCEPT;
}
But now I have to include a condition based on MSS Value, this criteria was raised through Wireshark analysis and according to Wireshark Reference:
Field Name: tcp.options.mss_val
Description: MSS Value
Type: Unsigned integer, 2 bytes
I looked for it here on Stack Overflow and through Google but did not find how to retrieve this tcp.options.mss_val from socket buffer (skb).
Please, any idea on how to retrieve it ?

kernel module for netfilter

I have started writing a kernel module for netfilter.
I am able to hook the netfilter and able to access the sk_buff.
Now I am trying to modify the packet(sk_buff) and trying to reinject the packet in tcp stack but it is not working.
My requirement is to drop SYN-ACK packet from a server and sending back a cooked up ack packet back to server without sending the SYN-ACK to client.
I have tried dev_queue_xmit after swapping source/dest IP, source/dest port
and source/dest MAC and recalculating the checksum and then send the packet using dev_queue_xmit. though dev_queue_xmit return success, the packet is not injecting to stack. Any help will be appreciated.
My code:
code is part of NF_INET_POST_ROUTING hook.
void do_exp(struct sk_buff *skb)
{
printk(KERN_INFO "Enering do_exp\n");
struct iphdr *ip_header = (struct iphdr *)skb_network_header(skb);
struct tcphdr *tcp_header = (struct tcphdr *)skb_transport_header(skb);
struct sk_buff *newskb;
struct iphdr *newiph;
struct tcphdr *newtcph;
newskb = skb_copy(skb, GFP_ATOMIC);
//newskb->pkt_type = PACKET_OUTGOING;
// newskb->pkt_type = PACKET_HOST;
/*changing Mac address */
unsigned char srcaddr[6];
struct ethhdr *eth = eth_hdr(skb);
struct ethhdr *neweth = eth_hdr(newskb);
memcpy(srcaddr, eth->h_source, ETH_ALEN);
memcpy(neweth->h_source, eth->h_dest, ETH_ALEN);
memcpy(neweth->h_dest, srcaddr, ETH_ALEN);
newiph = (struct iphdr *) skb_network_header(newskb);
newtcph = (struct tcphdr *) skb_transport_header(newskb);
newiph->saddr = ip_header->daddr;
newiph->daddr = ip_header->saddr;
newtcph->source = tcp_header->dest;
newtcph->dest = tcp_header->source;
newtcph->syn = 0;
newtcph->ack_seq = tcp_header->seq + 1;
newtcph->seq = tcp_header->ack_seq;
newiph->check = ip_fast_csum((unsigned char *)newiph, newiph->ihl);
int ret = dev_queue_xmit(newskb);
}
TCP dump is showing correctly.

Linux Kernel Module - nfilter hook incorrect data on Slackware

I have a linux kernel module that needs to process the data received via netfilter hook as a socket buffer.
The data received on Debian 8 (kernel: 3.16.0) is good but with SlackWare 14.0 (kernel: 3.2.29) the data is incorrect. I do not understand what's wrong. I searched everywhere on the forum and on Google but I never found a solution.
This is my nfilter hook function:
static unsigned int magic_packet_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) {
struct iphdr *iph;
struct tcphdr *tcph;
//Return if empty
if(!skb)
return NF_ACCEPT;
//Get ip header
iph = ip_hdr(skb);
//Check protocol
if(iph->protocol != IPPROTO_TCP)
return NF_ACCEPT;
//Get tcp header
tcph = tcp_hdr(skb);
//Get & check data
char *data;
data = (char *)((unsigned char *)tcph+(tcph->doff*4));
if(!data)
return NF_ACCEPT;
//Print in dmesg
#ifdef DEBUG
printk("anhackit data: %s\n", data);
#endif
//Convert source ip to string
char ip[16];
snprintf(ip, 16, "%pI4", &iph->saddr);
//Convert destination port to string
char port[6];
sprintf(port, "%u", ntohs(tcph->dest));
#ifdef DEBUG
printk("anhackit - magic packet received from %s on port %s !\n", ip, port);
#endif
return NF_ACCEPT;}
I hope someone can help me. Thanks in advance.

Calculating TCP Checksum in a netfilter module

I am trying to change some fields in the IP and TCP header in a netfilter postrouting hook, however I can't seem to get the kernels TCP checksum function to work properly to amend it afterwards.
The checksum is fine during the TCP handshake, but as soon as the packet has any payload the checksum is wrong.
I have pulled this checksum code together from digging around the TCP source. I am fairly sure tcplen is correct, matching the expected TCP header + payload size.
static unsigned int posthook_fn(
unsigned int hooknum,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
struct iphdr *iph;
struct tcphdr *tcph;
iph = ip_hdr(skb);
tcph = (struct tcphdr *)(skb->data + iph->ihl * 4);
tcph->source = port;
iph->saddr = addr;
tcplen = (skb->len - (ip_header->ihl << 2));
tcph->check = 0;
tcph->check = tcp_v4_check(tcph, tcplen,
iph->saddr,
iph->daddr,
csum_partial((char *)tcph, tcplen, 0));
skb->ip_summed = CHECKSUM_NONE; //stop offloading
ip_header->check = ip_fast_csum((u8 *)iph, iph->ihl);
return NF_ACCEPT;
}
Am I correct in thinking that tcp_v4_check calculates the psuedo header and csum_partial calculates the unfolded checksum for the payload and tcp_header?
I really want to avoid writing the function myself as the kernel will be much faster as the underlying functions use assembly for the calculation.
Is there an alternative method that might work? Or is there something I am missing out?
There is no need for extra call to skb_is_nonlinear(), since include/linux/skbuff.h:
static inline int skb_linearize(struct sk_buff *skb)
{
return skb_is_nonlinear(skb) ? __skb_linearize(skb) : 0;
}
Also, you have to:
ip_header->check = 0
before:
ip_header->check = ip_fast_csum((u8 *)iph, iph->ihl);
It's taken a while to get here but the problem seems to be that the socket buffer isn't always linear, the following code ensures that it is before calculating the checksum.
if (skb_is_nonlinear(skb)) {
if (skb_linearize(skb) != 0) {
return NF_DROP;
}
iph = ip_hdr(skb);
tcph = (void *)iph + (iph->ihl << 2);
}

Can I use pcap library for receiving ipv6 packets?

I am trying to convert hping3 to hping6. hping3 uses Pcap library to receive IPv4 packets. But I need to receive IPv6 packets.
That is possible. libpcap is able to catch anything on the wire.
Example using ETHERTYPE_IPV6:
static u_int16_t ether_packet(u_char *args, const struct pcap_pkthdr *pkthdr, co
nst u_char *p) {
struct ether_header *eptr = (struct ether_header*)p;
assert(pkthdr->caplen <= pkthdr->len);
assert(pkthdr->caplen >= sizeof(struct ether_header));
return eptr->ether_type;
}
// This is the callback. assumes ethernet frame.
static void pcap_callback(u_char *args,const struct pcap_pkthdr* pkthdr,const u_
char* p)
{
const u_int16_t type = ether_packet(args, pkthdr, p);
switch (ntohs(type)) {
case ETHERTYPE_IP:
// handle IPv4
break;
case ETHERTYPE_IPV6:
// handle v6
break;
}
}

Resources