I'm trying to create linux kernel module, that will inspect incoming packets. At the moment, I'm in process of extracting TCP header of packet and reading source and destination port -> However I'm getting incorrect values. I have hook function:
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 *))
{
struct iphdr *ipp = (struct iphdr *)skb_network_header(skb);
struct tcphdr *hdr;
/* Using this to filter data from another machine */
unsigned long ok_ip = 2396891328;
/* Some problem, empty network packet. Stop it now. */
if (!skb)
return NF_ACCEPT;
/* Just to track only packets coming from 1 IP */
if (ipp->saddr != ok_ip)
return NF_ACCEPT;
/* Incomming packet is TCP */
if (ipp->protocol == IPPROTO_TCP) {
hdr = (struct tcphdr *) skb_transport_header(skb);
printk(" TCP ports: source: %d, dest: %d .\n", ntohs(hdr->source),
ntohs(hdr->dest));
}
}
Now, when I try to telnet port 21(not listening there I get):
[ 4252.961912] TCP ports: source: 17664, dest: 52 .
[ 4253.453978] TCP ports: source: 17664, dest: 52 .
[ 4253.953204] TCP ports: source: 17664, dest: 48 .
And when I telnet port 22 - SSH deamon listening there:
[ 4299.239940] TCP ports: source: 17664, dest: 52 .
[ 4299.240527] TCP ports: source: 17664, dest: 40 .
[ 4299.552566] TCP ports: source: 17664, dest: 40 .
As visible from output I'm getting very weird results, anyone has idea where problem is coming from? when I compile module I have no errors / warnings. Version of kernel(headers): 3.7.10 . Not using SELinux or similar.
I had the same problem writing a small firewall for a networking class I just found out the problem I was having. I was casting the tcp header wrong. Try casting to tcp then accessing the port.
Here is a code snippet of it working
struct iphdr *ip_header; // ip header struct
struct tcphdr *tcp_header; // tcp header struct
struct udphdr *udp_header; // udp header struct
struct sk_buff *sock_buff;
unsigned int sport ,
dport;
sock_buff = skb;
if (!sock_buff)
return NF_ACCEPT;
ip_header = (struct iphdr *)skb_network_header(sock_buff);
if (!ip_header)
return NF_ACCEPT;
//if TCP PACKET
if(ip_header->protocol==IPPROTO_TCP)
{
//tcp_header = (struct tcphdr *)skb_transport_header(sock_buff); //doing the cast this way gave me the same problem
tcp_header= (struct tcphdr *)((__u32 *)ip_header+ ip_header->ihl); //this fixed the problem
sport = htons((unsigned short int) tcp_header->source); //sport now has the source port
dport = htons((unsigned short int) tcp_header->dest); //dport now has the dest port
}
To get the IP header or TCP header from a socket buffer (skb), just apply the functions ip_hdr(skb) and tcp_hdr(skb).
Related
I'm trying to write my own network packet filter module to replace iptables on my custom linux system.
I want the filter to drop all packets arriving from port 80 except the ones coming from the interface wlan0.
I hoped there would be a interface member in the struct iphdr but it seems to not be there (which makes sense since I think the interface is part of the 2nd layer). does anyone know how I can acheive this?
this is my code and I'm wandering what to put in the if statement that will complete the check:
static unsigned int pkg_filter_handler(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
{
if (!skb) {
return NF_ACCEPT;
}
/* check if loopback */
u32 srcAddr;
struct iphdr *ipHeader = ip_hdr(skb);
/* check if TCP protocol */
else if (iphdr->protocol == IPPROTO_TCP) {
/* check interface */
if (/*TODO: accept wlan0 rcp packets ad drop the rest*/) {
return NF_ACCEPT;
}
return NF_DROP;
}
return NF_ACCEPT;
}
There are functions in Linux for getting Ethernet header, IP header,and UDP headers likes these
udp_hdr(skb)
ip_hdr
skb_push(skb, ETH_HLEN)
But I could not find any function for getting payload like body of a packet like i.e. which contains body so I can write HTTP or other protocol data. in Linux Device Driver book or after searching couldn't find it. so question is how to compose UDP packet with Ethernet, IP, UDP headers and payload in kernel?
any function for getting payload like body of a packet
You can access payload different ways depending on what you want to do the next time. E.g.:
struct iphdr *iph = ip_hdr(skb);
if (iph->protocol == IPPROTO_UDP) {
struct udphdr *udph = udp_hdr(skb);
// E.g. check for UDP port
struct myl7_header *l7h = (struct myl7_header *)(udph + sizeof(struct udphdr));
// ...
}
Or you can pull the network and transport headers if you want to reconstruct encapsulation further or they are no longer needed (rough example, not with all possible sanity checks):
struct iphdr *iph = ip_hdr(skb);
if (iph->protocol == IPPROTO_UDP) {
struct udphdr *udph = udp_hdr(skb);
struct myl7_header *l7h;
// E.g. check for UDP port
skb_pull(skb, sizeof(struct iphdr));
skb_pull(skb, sizeof(struct udphdr));
l7h = (struct myl7_header *)skb->data;
// tansport protocol payload's length:
// skb->len or skb_tail_pointer(skb) - skb->data
}
I don't really know what you mean by payloadhtml, L7-protocol it not so kernel specific thing, so in general we are talking about transport protocol's payload.
N.B.: ip_hdr(), udp_hdr() functions imply that non-paged (linear) skb is used.
Related: What's the correct way to process all the payload of a sk_buff packet in Linux
I'm writing kernel module using netfilter. I just want to handle ACK for SYN/ACK (TCP three-way handshake). I use skb_is_tcp_pure_ack function, but ACK for data is also processed.
How can I do? My kernel version is 3.10.0-514.16.1.el7.x86_64.
Current code looks like this:
struct iphdr *iph;
struct tcphdr *tcph;
struct net *net;
unsigned int hdr_len;
unsigned int tcphoff;
if (!skb_is_tcp_pure_ack(skb)) {
return NF_ACCEPT;
}
/* add tcp option */
/* A netfilter instance to use */
static struct nf_hook_ops nfho __read_mostly = {
.hook = ato_hookfn,
.pf = PF_INET,
.hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP_PRI_LAST,
.owner = THIS_MODULE,
};
From the TCP state machine, you want to only match ACK packets when the state is TCP_SYN_SENT. Try adding another condition to check that, something like:
if (skb_is_tcp_pure_ack(skb)
&& skb->sk->sk_state == TCP_SYN_SENT) {
/*
* ACK(-only) packet during three-way handshake
*/
}
Also, note that skb_is_tcp_pure_ack was introduced in kernel version 4.* and not below.
I'm writing a few C line of code using libnet and pcap.
The purpose is to do a 3-way handshake manually sniffing filtered traffic on my NIC, looking for SYN packet and creating a SYN-ACK response using raw ipv4 socket.
I succesfully receive raw SYN packet using pcap_loop().
In my custom packet_handler() I do the following:
void packet_handler(u_char *user_args, const struct pcap_pkthdr *cap_header, const u_char *packet) {
struct libnet_ipv4_hdr *ip = (struct libnet_ipv4_hdr *)(packet + LIBNET_ETH_H);
struct libnet_tcp_hdr *tcp = (struct libnet_tcp_hdr *)(ip + (ip->ip_hl << 2));
printf(
"%s:%"PRIu16" > %s:%"PRIu16"\t[seq: %"PRIu32"\tack: %"PRIu32"]\n",
libnet_addr2name4(ip->ip_src.s_addr, LIBNET_DONT_RESOLVE),
ntohs(tcp->th_sport),
libnet_addr2name4(ip->ip_dst.s_addr, LIBNET_DONT_RESOLVE),
ntohs(tcp->th_dport),
ntohl(tcp->th_seq), ntohl(tcp->th_ack)
);
}
As results of opening a TCP connection I got the following result:
192.168.1.64:0 > 192.168.1.64:0 [seq: 0 ack: 0]
As you can see the IP header is correctly read, but the TCP header is not.
In particular, the problem is the libnet_tcp_hdr header fields which result to be all zero.
Am I doing something wrong in the pointer assignment?
Firstly, you need to verify that your packet handler only receives IP packets carrying TCP, not e.g. UDP.
However, your pointer arithmetic is wrong. Pointer aritmetic is done based on the type of the pointer, it is not based on bytes. This means this code:
struct libnet_tcp_hdr *tcp = (struct libnet_tcp_hdr *)(ip + (ip->ip_hl << 2));
adds ip->ip_hl << 2 * sizeof(struct struct libnet_ipv4_hdr bytes to your ip pointer.
Or if you look at it another way, the above code is exactly the same as:
struct libnet_tcp_hdr *tcp = (struct libnet_tcp_hdr *)(&ip[ip->ip_hl << 2]);
That might show more clearly what is going on.
You need to change that code to something like:
struct libnet_tcp_hdr *tcp = (struct libnet_tcp_hdr *)((unsigned char*)ip + (ip->ip_hl << 2));
I'm working with the pcap library and I'm trying to capture the source and destination IP addresses but it seems to be giving me totally random results:
Here is my struct:
struct sniff_ip {
u_char ip_vhl;
u_char ip_tos;
u_short ip_len;
u_short ip_id;
u_short ip_off;
u_char ip_ttl;
u_char ip_p;
u_short ip_sum;
struct in_addr ip_src, ip_dst;
};
Here is the relevant code that uses the struct:
void print_payload(u_char *arg, const struct pcap_pkthdr *pkthdr, const u_char *packet)
{
const struct sniff_ip *ip;
int i=0;
static int count=0;
ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);
printf("Source [%s] - Destination [%s]\n", inet_ntoa(ip->ip_src), inet_ntoa(ip->ip_dst));
printf("Payload:\n");
for(i=0; i<pkthdr->len; i++)
{
if(isprint(packet[i]))
printf("%c", packet[i]);
else
printf(".", packet[i]);
if((i%16 == 0 && i != 0) || ( i== pkthdr->len-1))
printf("\n");
}
}
The output is:
Source [207.117.127.0] - Destination [207.117.127.0]
Payload:
................E
..<m4#.#..u.....
.......Q#1......
....0....#......
1\.........
Source [60.190.127.0] - Destination [60.190.127.0]
Payload:
................E
..(..#.#.<......
...........Q#1.P
...wN..
The IP addresses seem to be totally random and aren't mine. The expected output would be to show my own IP for both source and destination since I'm testing it by connecting to myself. I'm running it on port 23 to avoid anybody other data from interfering.
Edit: I got it working, for some reason I had to telnet to "eth0" instead of "localhost" for it to work. However once I changed the port to something more useful, like port 80 it worked fine. I'm not sure why port 23 was different, but oh well.
inet_ntoa returns pointer to its internal buffer, so it's not likely that you can use two inet_ntoas in one call to printf and hope for different results.