innercoder.com

A blog for Linux programming enthusiasts

Learn TCP/IP by Coding Your Own Network Sniffer

| Comments

This a small project that came to mind after reading some reference material while stuyding HTTP. It basically consists of using raw sockets to make the kernel handle L2 data/frames to the user application. From there anything is possible, a ping server, a traceroute appication, IP spoofing, man-in-the-middle attacks, the sky is the limits.

This is another project that has good potential to apply high performance server features. It currently reads frames sequentially which is not very for high bandwidth connections with lots of traffics. A multi-threaded version is in plan for this. Apart from that, the function to specify the type of packets to listen to.

The complete code is in its github repository.

main.c

1
2
3
4
5
6
/* usage function */
void usage(char *cmd)
{
  printf("Usage: %s [INTERFACE]\n", cmd);
  exit(EXIT_SUCCESS);
}

Usage function. Currently, it only supports to choose on which interface to listen to.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
int main(int argc, char *argv[])
{
  u_int rcv_bytes;
  u_int trx_fd;
  u_char buffer[BUFF];
  struct sockaddr_in cl_addr;
  socklen_t cl_size;

  if (argc != 2)
      usage(argv[0]);
  
  
  if((trx_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) == -1)
      err_msg("Error on socket()");
  if((setsockopt(trx_fd, SOL_SOCKET, SO_BINDTODEVICE, argv[1],
          strlen(argv[1]) + 1)) == -1)
      err_msg("Error on setsockopt()");
  
  for(;;) {
      if((rcv_bytes = recvfrom(trx_fd, buffer, BUFF, 0,
                   (struct sockaddr *) &cl_addr,
                   &cl_size)) == -1)
          err_msg("Error on recvfrom()");
      proc_packet(buffer, rcv_bytes);
  }

  return 0;
}

The main procedure is the infite loop as you can see, it reads packet sequentially using one process. Line 13: creates the socket to be listen to every type of frame, and in/out direction. Line 15: set socket options to tell the program on which interface to listen to.

procpkt.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/* main processing function */
void proc_packet(const u_char *data, u_int size)
{  
  u_int prot;
  u_int dat_offs;
  u_int tcp_hdr_sz;
  u_int udp_hdr_sz;
  
  /* print captured bytes */
  printf("\n\nBytes Captured = [ %d ]\n", size);
  
  /* print L2 information */
  decode_ethernet(data);

  /* obtain IP protocol number for further decoding */
  prot = decode_ip(data + ETHER_HDR_LEN);
  
  switch(prot) {
  case 1:
      decode_icmp(data + ETHER_HDR_LEN + IP_HDR_LEN);
      break;
  case 6:
      tcp_hdr_sz = decode_tcp(data + ETHER_HDR_LEN + IP_HDR_LEN);
      break;
  case 17:
      udp_hdr_sz = decode_udp(data + ETHER_HDR_LEN + IP_HDR_LEN);
      break;
  default:
      break;
  }


  /* print raw encapsulated data */
  praw(data + ETHER_HDR_LEN, size);
              

}

The main function of the program that handles identification of the IP protocol to which we then de-encapsulate and then do a print out of the data.

psniff.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
struct ip_hdr {
  unsigned char ver_ihl;
  unsigned char tos;
  unsigned short len;
  unsigned short id;
  unsigned short frag_off;
  unsigned char ttl;
  unsigned char protocol;
  unsigned short check;
  unsigned int src_addr;
  unsigned int dst_addr;
};


/*
 * TCP
 * Note: Supports Little-Endian only
 */

struct tcp_hdr {
  unsigned short src_port;
  unsigned short dst_port;
  unsigned int seq;
  unsigned int ack;
  unsigned char reserved:4;
  unsigned char offset:4;
  unsigned char flags;
#define TCP_FIN 0x01
#define TCP_SYN 0x02
#define TCP_RST 0x04
#define TCP_PUSH 0x08
#define TCP_ACK 0x10
#define TCP_URG 0x20
  unsigned short window;
  unsigned short checksum;
  unsigned short urgent;
};


/*
 * UDP Header
 */

struct udp_hdr {
  unsigned short src_port;
  unsigned short dst_port;
  unsigned short len;
  unsigned short checksum;
};


/*
 * ICMP Header
 */

struct icmp_hdr
{
  unsigned char type;
  unsigned char code;
  unsigned short checksum;
  unsigned int gateway;
};

To decode the layers the de-encapsulated data needs to be type casted to the appropiate data structure and from there, retrieve the information. In this program I decided to the create my own structures for the different protocol headers. These user libraries are located in /usr/include/linux/if_ethernet.h and /usr/include/netinet/.

Note: This program is still a work in progress.

Comments