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));
Related
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 am able to print the destination and source address from an ARP packet. But I want to print the source ip and destination ip address from the ARP request. I have searched in the and found this structure.
`struct ether_arp {
struct arphdr ea_hdr;
u_char arp_sha[6];
u_char arp_spa[4];
u_char arp_tha[6];
u_char arp_tpa[4];
};`
But when I am trying to print arp_spa I am getting something like 0:1:8:0.
Obviously this is not the source IP address.
arp_ptr = (struct ether_arp *) packet;
I feel this is wrong as some bytes need to be skipped. But I am not sure about this
Can anybody please comment.
EDIT :
arp_ptr = (struct ether_arp *) packet;
ptr = arp_ptr->arp_sha;
i = ETHER_ADDR_LEN;
printf(" source is: ");
do{
printf("%s%x",(i == ETHER_ADDR_LEN) ? " " : ":",*ptr++);
}while(--i>0);
The above is the code I am using to print either source IP or source MAC from ARP request
I am reading packets in pcap format, and have determined that some have the protocol ICMP. I think that if that's the case, the ICMP header immediately follows the IP header. However, I get the wrong ICMP type (echo reply, request) for each of my results. I.e, here is the correct output for one such packet which has an echo reply:
Packet number: 2 Packet Len: 74
Ethernet Header
Dest MAC: 0:2:2d:90:75:89
Source MAC: 0:6:25:78:c4:7d
Type: IP
IP Header
TOS: 0xff
TTL: 52
Protocol: ICMP
Checksum: Incorrect (0x5565)
Sender IP: 66.94.230.35
Dest IP: 192.168.1.102
ICMP Header
Type: Reply
I know that echo reply has a code of 8, but instead of that I get a 0, indicating "request", and this happens for all packets when I try to get the type. I feel that I may be pointing my ICMP header to the wrong location following the IP header. My IP information though is correct. Here is how I am adjusting the pointers to the IP and ICMP headers:
EtherHeader *eth = (EtherHeader *)packet;
IPHeader *iph;
TCPHeader *tcp;
ICMPHeader *icm;
---
ipLen = ntohs(iph->totLen * 4);
if(iph->protocol == ICMP) {
icm = (ICMPHeader *)(packet + ETHER_SIZE + (ntohs(iph->totLen)));
printf("%d\n", icm->type);;
}
Is there anything wrong with how I am setting my pointer to the ICMP header?
Here are my headers for IP and ICMP:
typedef struct __attribute__((__packed__)) IPHeader {
#if __BYTE_ORDER__ == __LITTLE_ENDIAN__
uint8_t hdrLen:4;
uint8_t version:4;
#else
uint8_t version:4;
uint8_t hdrLen:4;
#endif
uint8_t TOS;
uint16_t totLen;
uint16_t id;
uint16_t offset;
#define DF 0x4
#define MF 0x2
#define OFF 0
uint8_t TTL;
uint8_t protocol;
uint16_t checksum;
struct in_addr srcIP;
struct in_addr destIP;
}IPHeader;
typedef struct __attribute__((__packed__)) ICMPHeader {
uint8_t type;
}ICMPHeader;
Your understanding of the control message type numbers is backwards:
I know that echo reply has a code of 8, but instead of that I get a 0, indicating "request",
The correct values are:
0 Echo Reply
....
8 Echo Request
Internet Control Message Protocol - Control Messages
Also, although this doesn't appear to be used, you have an order of operations problem here - you can't do anything with the value until you change its byte order:
ntohs(iph->totLen * 4);
needs to be
ntohs(iph->totLen) * 4;
I know that echo reply has a code of 8, but instead of that I get a 0,
No it doesn't. ICMP echo reply has a type of 0. type 8 is Echo (request).
See e.g. this
I am reading a buffer from a socket (AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP)) with the following code. I am using an arp_frame struct to access the component parts of the contained ARP reply. The inet_ntoa() returns the correct first octet of the IP but the other octets are 0 producing 172.0.0.0.
Question 1 is why might this happen?
Question 2 is how can I print r bytes of the msg buffer as hex in host byte order to debug the packet?
unsigned char msg[65535];
struct ether_arp *arp_frame = (struct ether_arp *)msg;
while ((r = recv(sock, msg, sizeof(msg), 0))) {
// skip it's not an ARP REPLY
if (ntohs(arp_frame->arp_op) != ARPOP_REPLY)
continue;
for (i = 0; i < SONOS_PREFIX_NUM; i++) {
if (!memcmp(sonos_prefixes[i], arp_frame->arp_sha, 3)) {
struct in_addr addr;
addr.s_addr = *arp_frame->arp_spa;
printf("Blah: %lu\n", ntohl(*arp_frame->arp_spa));
printf("Sonos found at %s\n", inet_ntoa(addr));
}
}
}
struct ether_arp looks like this:
struct ether_arp {
struct arphdr ea_hdr; /* fixed-size header */
u_int8_t arp_sha[ETH_ALEN]; /* sender hardware address */
u_int8_t arp_spa[4]; /* sender protocol address */
u_int8_t arp_tha[ETH_ALEN]; /* target hardware address */
u_int8_t arp_tpa[4]; /* target protocol address */
};
With that in mind, I think that your addr.s_addr = *arp_frame->arp_spa; looks a little fishy. arp_frame->arp_spa yields a u_int8_t[4], which you then dereference as a pointer. I think memcpy() might be more appropriate there.
If you think the API is broken, print out the bytes.
"Methinks, Brutus, the fault is not in the stars."