G
G
grizzli7712015-10-23 17:58:04
linux
grizzli771, 2015-10-23 17:58:04

Linux kernel module: changing the tcp destination address of a packet does not work, how to fix it?

I form and send a TCP packet via RAW-socket( socket(PF_INET, SOCK_RAW, IPPROTO_TCP) ) to the specified IP (main_server_ip).
The Linux kernel module is running on the machine, which catches TCP packets to this IP address (main_server_ip) and changes the destination address to another one (other_server_ip). Packets do not arrive at their destination. I intercept packets between machines with Wireshark and observe the picture: the IP header of the packet contains the correct IP address (other_server_ip), but the Ethenet headers contain the MAC address corresponding to the original IP address (other_server_ip). That is, it turns out that the IP address has changed, but the MAC has remained from the old IP address.
I can't figure out what this is connected with and how to fix it...
If you send data via regular sockets, then everything works successfully. Kernel version: 3.13.0-65-generic.
Here is the code I use to change the destination IP address in the kernel module:
struct iphdr *ip_header = (struct iphdr *) skb_network_header(skb);
unsigned int dest_ip = (unsigned int) ip_header->daddr;
struct tcphdr *tcp_header;
if (dest_ip == main_server_ip) {
printk(KERN_INFO "Protocol: %d\n", ip_header->protocol);
if (ip_header->protocol == 6) {
tcp_header = (struct tcphdr*) ((char*) ip_header + (ip_header->ihl * 4));
tcp_header->check = 0;
}
unsigned char* pack = ((char*) ip_header + (ip_header->ihl * 4));
tcp_len = ntohs(ip_header->tot_len) - (ip_header->ihl * 4);
ip_header->daddr = other_server_ip;
if (ip_header->protocol == 6) {
tcp_header = (struct tcphdr*) ((char*) ip_header + (ip_header->ihl * 4));
len = skb->len;
tcp_header->check = 0;
tcp_header->check = tcp_v4_check(len - 4 * ip_header->ihl,
ip_header->saddr, ip_header->daddr,
csum_partial((char *) tcp_header, len - 4 * ip_header->ihl,
0));
skb->ip_summed = CHECKSUM_NONE;
ip_header->check = 0;
ip_header->check = ip_fast_csum((u8 *) ip_header, ip_header->ihl);
printk(KERN_INFO "IP addr after change: %u", ip_header->daddr);
return NF_ACCEPT;
}
}

Answer the question

In order to leave comments, you need to log in

1 answer(s)
O
Oleg Tsilyurik, 2015-10-23
@Olej

but the MAC address is registered in the Ethenet headers - the address corresponding to the original IP address

This is as it should be, because the MAC and IP must match, the match is resolved via ARP, ARP resolution will not work for your RAW packets (worse, the old match is in the ARP cache).
Achieve an exact match between MAC and IP.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question