Packet Sniffer

Packet Sniffer
A packet sniffer is a program that can see all of the information passing over the network it is connected to. As data streams back and forth on the network, the program looks at, or “sniffs,” each packet. A packet is a part of a message that has been broken up.
Normally, a computer only looks at packets addressed to it and ignores the rest of the traffic on the network. But when a packet sniffer is set up on a computer, the sniffer’s network interface is set to promiscuous mode. This means that it is looking at everything that comes through. The amount of traffic largely depends on the location of the computer in the network. A client system out on an isolated branch of the network sees only a small segment of the network traffic, while the main domain server sees almost all of it.
A packet sniffer can usually be set up in one of two ways:
1. Unfiltered – captures all of the packets
2. Filtered – captures only those packets containing specific data elements
Packets that contain targeted data are copied onto the hard disk as they pass through. These copies can then be analyzed carefully for specific information or patterns.

Flow chart of the program
Below is the flow chart of the sniffer program and i am just showing a flow of sequence of parsing a single packet.


Sniffer Program Code
#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<features.h>
#include<linux/if_packet.h>
#include<linux/if_ether.h>
#include<errno.h>
#include<sys/ioctl.h>
#include<net/if.h>
#include<linux/ip.h>
#include<linux/tcp.h>
#include<netinet/in.h>
int CreateRawSocket(int protocol_to_sniff)
{
int rawsock;
if((rawsock = socket(PF_PACKET, SOCK_RAW, htons(protocol_to_sniff)))== -1)
{
perror(“Error creating raw socket: “);
exit(-1);
}
return rawsock;
}
int BindRawSocketToInterface(char *device, int rawsock, int protocol)
{
struct sockaddr_ll sll;
struct ifreq ifr;
bzero(&sll, sizeof(sll));
bzero(&ifr, sizeof(ifr));
/* First Get the Interface Index */
strncpy((char *)ifr.ifr_name, device, IFNAMSIZ);
if((ioctl(rawsock, SIOCGIFINDEX, &ifr)) == -1)
{
printf(“Error getting Interface index !\n”);
exit(-1);
}

/* Bind our raw socket to this interface */
sll.sll_family = AF_PACKET;
sll.sll_ifindex = ifr.ifr_ifindex;
sll.sll_protocol = htons(protocol);

if((bind(rawsock, (struct sockaddr *)&sll, sizeof(sll)))== -1)
{
perror(“Error binding raw socket to interface\n”);
exit(-1);
}
return 1;
}

void PrintPacketInHex(unsigned char *packet, int len)
{
unsigned char *p = packet;
printf(“\n\n———Packet—Starts—-\n\n”);

while(len–)
{
printf(“%.2x “, *p);
p++;
}

printf(“\n\n——–Packet—Ends—–\n\n”);
}

PrintInHex(char *mesg, unsigned char *p, int len)
{
printf(mesg);
while(len–)
{
printf(“%.2X “, *p);
p++;
}
}

ParseEthernetHeader(unsigned char *packet, int len)
{
struct ethhdr *ethernet_header;
if(len > sizeof(struct ethhdr))
{
ethernet_header = (struct ethhdr *)packet;
/* First set of 6 bytes are Destination MAC */
PrintInHex(“Destination MAC: “, ethernet_header->h_dest, 6);
printf(“\n”);
/* Second set of 6 bytes are Source MAC */
PrintInHex(“Source MAC: “, ethernet_header->h_source, 6);
printf(“\n”);
/* Last 2 bytes in the Ethernet header are the protocol it carries */
PrintInHex(“Protocol: “,(void *)&ethernet_header->h_proto, 2);
printf(“\n”);
}
else
{
printf(“Packet size too small !\n”);
}
}

ParseIpHeader(unsigned char *packet, int len)
{
struct ethhdr *ethernet_header;
struct iphdr *ip_header;
/* First Check if the packet contains an IP header using
the Ethernet header */

ethernet_header = (struct ethhdr *)packet;
if(ntohs(ethernet_header->h_proto) == ETH_P_IP)
{
/* The IP header is after the Ethernet header */
if(len >= (sizeof(struct ethhdr) + sizeof(struct iphdr)))
{
ip_header = (struct iphdr*)(packet + sizeof(struct ethhdr));
/* print the Source and Destination IP address */
printf(“Dest IP address: %s\n”, inet_ntoa(ip_header->daddr));
printf(“Source IP address: %s\n”, inet_ntoa(ip_header->saddr));
}
else
{
printf(“IP packet does not have full header\n”);
}
}
else
{
/* Not an IP packet */
}
}

ParseTcpHeader(unsigned char *packet , int len)
{
struct ethhdr *ethernet_header;
struct iphdr *ip_header;
struct tcphdr *tcp_header;

/* Check if enough bytes are there for TCP Header */
if(len >= (sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct tcphdr)))
{
/* Do all the checks: 1. Is it an IP pkt ? 2. is it TCP ? */
ethernet_header = (struct ethhdr *)packet;
if(ntohs(ethernet_header->h_proto) == ETH_P_IP)
{
ip_header = (struct iphdr *)(packet + sizeof(struct ethhdr));
if(ip_header->protocol == IPPROTO_TCP)
{
tcp_header = (struct tcphdr*)(packet + sizeof(struct ethhdr) + ip_header->ihl*4 );
/* Print the Dest and Src ports */
printf(“Source Port: %d\n”, ntohs(tcp_header->source));
printf(“Dest Port: %d\n”, ntohs(tcp_header->dest));
}
else
{
printf(“Not a TCP packet\n”);
}
}
else
{
printf(“Not an IP packet\n”);
}
}
else
{
printf(“TCP Header not present \n”);
}
}

int ParseData(unsigned char *packet, int len)
{
struct ethhdr *ethernet_header;
struct iphdr *ip_header;
struct tcphdr *tcp_header;
unsigned char *data;
int data_len;
/* Check if any data is there */
if(len > (sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct tcphdr)))
{
ip_header = (struct iphdr*)(packet + sizeof(struct ethhdr));
data = (packet + sizeof(struct ethhdr) + ip_header->ihl*4 +sizeof(struct tcphdr));
data_len = ntohs(ip_header->tot_len) – ip_header->ihl*4 – sizeof(struct tcphdr);
if(data_len)
{
printf(“Data Len : %d\n”, data_len);
PrintInHex(“Data : “, data, data_len);
printf(“\n\n”);
return 1;
}
else
{
printf(“No Data in packet\n”);
return 0;
}
}
else
{
printf(“No Data in packet\n”);
return 0;
}
}

int IsIpAndTcpPacket(unsigned char *packet, int len)
{
struct ethhdr *ethernet_header;
struct iphdr *ip_header;
ethernet_header = (struct ethhdr *)packet;
if(ntohs(ethernet_header->h_proto) == ETH_P_IP)
{
ip_header = (struct iphdr *)(packet + sizeof(struct ethhdr));
if(ip_header->protocol == IPPROTO_TCP)
return 1;
else
return -1;
}
else
{
return -1;
}
}

main(int argc, char **argv)
{
int raw;
unsigned char packet_buffer[2048];
int len;
int packets_to_sniff;
struct sockaddr_ll packet_info;
int packet_info_size = sizeof(packet_info);
/* create the raw socket */
raw = CreateRawSocket(ETH_P_IP);
/* Bind socket to interface */
BindRawSocketToInterface(argv[1], raw, ETH_P_IP);
/* Get number of packets to sniff from user */
packets_to_sniff = atoi(argv[2]);
/* Start Sniffing and print Hex of every packet */
while(packets_to_sniff–)
{
if((len = recvfrom(raw, packet_buffer, 2048, 0, (struct sockaddr*)&packet_info, &packet_info_size)) == -1)
{
perror(“Recv from returned -1: “);
exit(-1);
}
else
{
/* Packet has been received successfully !! */
PrintPacketInHex(packet_buffer, len);
/* Parse Ethernet Header */
ParseEthernetHeader(packet_buffer, len);
/* Parse IP Header */
ParseIpHeader(packet_buffer, len);
/* Parse TCP Header */
ParseTcpHeader(packet_buffer, len);
if(IsIpAndTcpPacket(packet_buffer, len))
{
if(!ParseData(packet_buffer, len))
packets_to_sniff++;
}
}
}
return 0;
}

Packet Injector

Packet Injection in a network
Packet injection is a computer networking term which refers to sending a packet on a network into an already established connection, usually by a party not otherwise participating in the said connection. This is accomplished by crafting a packet using raw sockets. Sometimes IP address spoofing is used.

Raw socket
Most socket application programming interfaces (APIs), especially those based on Berkeley sockets, support raw sockets.
Usually raw sockets receive packets inclusive of the header, as opposed to standard sockets which receive just the packet payload without headers. When transmitting packets, the automatic addition of a header may be a configurable option of the socket.

Flow chart diagram

Packet injector code
#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<features.h>
#include<linux/if_packet.h>
#include<linux/if_ether.h>
#include<errno.h>
#include<sys/ioctl.h>
#include<net/if.h>
#include<net/ethernet.h>
#include<linux/ip.h>
#include<linux/tcp.h>
#include<arpa/inet.h>
#include<string.h>
#include<sys/time.h>

#define DATA_SIZE 100
#define SRC_ETHER_ADDR “aa:aa:aa:aa:aa:aa”
#define DST_ETHER_ADDR “bb:bb:bb:bb:bb:bb”
#define SRC_IP “192.168.0.10”
#define DST_IP “192.168.0.11”
#define SRC_PORT 80
#define DST_PORT 100

typedef struct PseudoHeader{
unsigned long int source_ip;
unsigned long int dest_ip;
unsigned char reserved;
unsigned char protocol;
unsigned short int tcp_length;
}PseudoHeader;

int CreateRawSocket(int protocol_to_sniff)
{
int rawsock;
if((rawsock = socket(PF_PACKET, SOCK_RAW, htons(protocol_to_sniff)))== -1)
{
perror(“Error creating raw socket: “);
exit(-1);
}
return rawsock;
}

int BindRawSocketToInterface(char *device, int rawsock, int protocol)
{
struct sockaddr_ll sll;
struct ifreq ifr;
bzero(&sll, sizeof(sll));
bzero(&ifr, sizeof(ifr));
/* First Get the Interface Index */
strncpy((char *)ifr.ifr_name, device, IFNAMSIZ);
if((ioctl(rawsock, SIOCGIFINDEX, &ifr)) == -1)
{
printf(“Error getting Interface index !\n”);
exit(-1);
}

/* Bind our raw socket to this interface */
sll.sll_family = AF_PACKET;
sll.sll_ifindex = ifr.ifr_ifindex;
sll.sll_protocol = htons(protocol);
if((bind(rawsock, (struct sockaddr *)&sll, sizeof(sll)))== -1)
{
perror(“Error binding raw socket to interface\n”);
exit(-1);
}
return 1;
}

int SendRawPacket(int rawsock, unsigned char *pkt, int pkt_len)
{
int sent= 0;
/* A simple write on the socket ..thats all it takes ! */
if((sent = write(rawsock, pkt, pkt_len)) != pkt_len)
{
/* Error */
printf(“Could only send %d bytes of packet of length %d\n”, sent, pkt_len);
return 0;
}
return 1;
}

struct ethhdr* CreateEthernetHeader(char *src_mac, char *dst_mac, int protocol)
{
struct ethhdr *ethernet_header;
ethernet_header = (struct ethhdr *)malloc(sizeof(struct ethhdr));
/* copy the Src mac addr */
memcpy(ethernet_header->h_source, (void *)ether_aton(src_mac), 6);
/* copy the Dst mac addr */
memcpy(ethernet_header->h_dest, (void *)ether_aton(dst_mac), 6);
/* copy the protocol */
ethernet_header->h_proto = htons(protocol);
/* done …send the header back */
return (ethernet_header);
}

/* Ripped from Richard Stevans Book */
unsigned short ComputeChecksum(unsigned char *data, int len)
{
long sum = 0; /* assume 32 bit long, 16 bit short */
unsigned short *temp = (unsigned short *)data;
while(len > 1){
sum += *temp++;
if(sum & 0x80000000) /* if high order bit set, fold */
sum = (sum & 0xFFFF) + (sum >> 16);
len -= 2;
}
if(len) /* take care of left over byte */
sum += (unsigned short) *((unsigned char *)temp);
while(sum>>16)
sum = (sum & 0xFFFF) + (sum >> 16);
return ~sum;

}

struct iphdr *CreateIPHeader(/* Customize this as an exercise */)
{
struct iphdr *ip_header;
ip_header = (struct iphdr *)malloc(sizeof(struct iphdr));
ip_header->version = 4;
ip_header->ihl = (sizeof(struct iphdr))/4 ;
ip_header->tos = 0;
ip_header->tot_len = htons(sizeof(struct iphdr) + sizeof(struct tcphdr) + DATA_SIZE);
ip_header->id = htons(111);
ip_header->frag_off = 0;
ip_header->ttl = 111;
ip_header->protocol = IPPROTO_TCP;
ip_header->check = 0; /* We will calculate the checksum later */
(in_addr_t)ip_header->saddr = inet_addr(SRC_IP);
(in_addr_t)ip_header->daddr = inet_addr(DST_IP);
/* Calculate the IP checksum now :
The IP Checksum is only over the IP header */
ip_header->check = ComputeChecksum((unsigned char *)ip_header, ip_header->ihl*4);

return (ip_header);
}

struct tcphdr *CreateTcpHeader(/* Customization Exercise */)
{
struct tcphdr *tcp_header;
/* Check /usr/include/linux/tcp.h for header definiation */
tcp_header = (struct tcphdr *)malloc(sizeof(struct tcphdr));
tcp_header->source = htons(SRC_PORT);
tcp_header->dest = htons(DST_PORT);
tcp_header->seq = htonl(111);
tcp_header->ack_seq = htonl(111);
tcp_header->res1 = 0;
tcp_header->doff = (sizeof(struct tcphdr))/4;
tcp_header->syn = 1;
tcp_header->window = htons(100);
tcp_header->check = 0; /* Will calculate the checksum with pseudo-header later */
tcp_header->urg_ptr = 0;

return (tcp_header);
}

CreatePseudoHeaderAndComputeTcpChecksum(struct tcphdr *tcp_header, struct iphdr *ip_header, unsigned char *data)
{
/*The TCP Checksum is calculated over the PseudoHeader + TCP header +Data*/
/* Find the size of the TCP Header + Data */
int segment_len = ntohs(ip_header->tot_len) – ip_header->ihl*4;
/* Total length over which TCP checksum will be computed */
int header_len = sizeof(PseudoHeader) + segment_len;
/* Allocate the memory */
unsigned char *hdr = (unsigned char *)malloc(header_len);
/* Fill in the pseudo header first */
PseudoHeader *pseudo_header = (PseudoHeader *)hdr;
pseudo_header->source_ip = ip_header->saddr;
pseudo_header->dest_ip = ip_header->daddr;
pseudo_header->reserved = 0;
pseudo_header->protocol = ip_header->protocol;
pseudo_header->tcp_length = htons(segment_len);
/* Now copy TCP */
memcpy((hdr + sizeof(PseudoHeader)), (void *)tcp_header, tcp_header->doff*4);
/* Now copy the Data */
memcpy((hdr + sizeof(PseudoHeader) + tcp_header->doff*4), data, DATA_SIZE);
/* Calculate the Checksum */
tcp_header->check = ComputeChecksum(hdr, header_len);
/* Free the PseudoHeader */
free(hdr);
return ;
}

unsigned char *CreateData(int len)
{
unsigned char *data = (unsigned char *)malloc(len);
struct timeval tv;
struct timezone tz;
int counter = len;
/* get time of the day */
gettimeofday(&tv, &tz);
/* seed the random number generator */
srand(tv.tv_sec);
/* Add random data for now */
for(counter = 0 ; counter < len; counter++)
data[counter] = 255.0 *rand()/(RAND_MAX +1.0);
return data;
}

/* argv[1] is the device e.g. eth0 */
main(int argc, char **argv)
{
int raw;
unsigned char *packet;
struct ethhdr* ethernet_header;
struct iphdr *ip_header;
struct tcphdr *tcp_header;
unsigned char *data;
int pkt_len;
/* Create the raw socket */
raw = CreateRawSocket(ETH_P_ALL);
/* Bind raw socket to interface */
BindRawSocketToInterface(argv[1], raw, ETH_P_ALL);
/* create Ethernet header */
ethernet_header = CreateEthernetHeader(SRC_ETHER_ADDR, DST_ETHER_ADDR, ETHERTYPE_IP);
/* Create IP Header */
ip_header = CreateIPHeader();
/* Create TCP Header */
tcp_header = CreateTcpHeader();
/* Create Data */
data = CreateData(DATA_SIZE);
/* Create PseudoHeader and compute TCP Checksum */
CreatePseudoHeaderAndComputeTcpChecksum(tcp_header, ip_header, data);
/* Packet length = ETH + IP header + TCP header + Data*/
pkt_len = sizeof(struct ethhdr) + ntohs(ip_header->tot_len);
/* Allocate memory */
packet = (unsigned char *)malloc(pkt_len);
/* Copy the Ethernet header first */
memcpy(packet, ethernet_header, sizeof(struct ethhdr));
/* Copy the IP header — but after the ethernet header */
memcpy((packet + sizeof(struct ethhdr)), ip_header, ip_header->ihl*4);
/* Copy the TCP header after the IP header */
memcpy((packet + sizeof(struct ethhdr) + ip_header->ihl*4),tcp_header, tcp_header->doff*4);
/* Copy the Data after the TCP header */
memcpy((packet + sizeof(struct ethhdr) + ip_header->ihl*4 + tcp_header->doff*4), data, DATA_SIZE);
/* send the packet on the wire */
if(!SendRawPacket(raw, packet, pkt_len))
{
perror(“Error sending packet”);
}
else
printf(“Packet sent successfully\n”);
/* Free the headers back to the heavenly heap */
free(ethernet_header);
free(ip_header);
free(tcp_header);
free(data);
free(packet);
close(raw);
return 0;
}

Rapid protocol stack development framework

Introduction
IP Protocol stacks market is highly competitive, cost and schedule sensitive and is highly vulnerable to the highly volatile telecom market and rapidly evolving technology standards.
Consequently Protocol stack development is subject to requirements changes and resource constraints due to evolving technical standards, changing customer needs and deployment scenarios. In addition the quality expectations have gone up with increased security and reliability needs. This requires a highly flexible and “always available” development processes that can minimize cost of change in business plans and software design.
A holistic Rapid development framework encompassing re-usable components, tools and recommended processes is proposed to address these issues. This framework, as depicted in figure;

Inside a protocol stack
By definition protocol stacks are message processing software components. Basic functionality of a protocol stack can be viewed as a black box wherein:

  1. a message in a predefined format enters a node
  2. the stack processes the message as per rules pre-defined by standard bodies
  3. the node updates it’s own state and
  4. Possibly generates and sends a new message as per pre-defined format.

 Protocol stacks have the following interfaces:

  1. Programmable APIs, SNMP and configuration file for configuring the stack.
  2. Physical interface on the host for capturing relevant network messages.
  3. Platform/OS APIs for memory management, signals, Inter-process communication and other system facilities.

Rapid Protocol development components
As can be seen in Figure below, there are common functionalities shared across protocols. Reusable components are proposed for each of the identified functions to support portability and reduce defects. Design criteria for the development of these components were
a)    Portability (Segregation of Platform independent and dependent code),
b)    Generic interface and
c)    simple intuitive design.


Message Capture
Messages relevant to a protocol are captured through sockets. The message handler library developed to facilitate this functionality provides as an abstraction to the OS for any type of the socket like net link sockets, TCP and UDP. The advantage of such a portable library is that the protocol can be independent of the operating system idiosyncrasies of socket interfaces.

Multi threading library

Multi-threading architectures are fairly common in protocol stacks for processing multiple messages. Multi-threading introduces additional complexity through the need for thread management and inter-process process communication. A reusable library which provides thread management and inter-thread communication functionality with OS independent interfaces is proposed. This library can also enhance the scalability and configurability of the resultant protocol stacks through the capability to configure number of threads to be used in the application.

Timer library
In most of the protocol stacks, timer functionality is needed for periodic or onetime callback for state updates or keeping the sessions live etc. The Timer library provides coarse grained piggybacked sub-millisecond timers for efficient utilization of CPU resources at high load levels.

Database storage
This reusable library has multiple storage and search implementations like AVL Tree, Hash Tables, BTrees, etc which can be chosen to match the performance characteristics for target application. The interface to these varied implementation is standardized which enhances maintainability of the code.

Integrated Skeleton framework
A bare-bones implementation of a protocol stack with the reusable components and sample glue code is depicted in Figure 3. A new development effort has to include only the glue code and the core processing modules as described in the figure in lighter colored boxes. With the help of the framework, the development team can concentrates only on the core protocol processing functionality saving time and reducing the development effort.

ECos Architecture

ECos ROTS
The eCos is an open source, royalty free, real time operating system intended for embedded applications. The eCos has configurable component architecture consisting of several key software components such as the kernel and the hardware abstraction layer (HAL). The software components are designed in layered architecture that abstracts the details of the target hardware from the application, enhancing both application portability and reuse. This creates a well define interface between application and target-specific components. The under mentioned is the eCos architecture;

In the above figure; the dashed lines divides the layer of software components. The first layer above the dashed lines, the kernel, networking stack, and file system are independent of the processing hardware or board product. Theses components interface with upper compatibility and library layers to present a consistent platform for the application layer.

Below the dashed line is the redboot ROM monitor, the hardware abstraction layer (HAL) and the device drivers. These components are written, configured and optimized for the specific target hardware and should be supplied by the hardware vendor.

Idle Cell selection in LTE

Cell Selection and Re-Selection
Cell selection entity handles the cell selection/ reselection functionality, it is responsible for selecting the most suitable cell to camp on for the UE. The cell selections are made on different parameters. These parameters include the Power parameters and the parameters included in the system information Broadcasts.

Cell Selection Criteria
In order to be the candidate for a suitable or acceptable cell, the following criterion is to be met:
Srxlev  > 0
Where
Srxlev = Qrxmeasured – Qrxlevmin – Pcomposition

Cell Re-Selection Criteria
The cell reselection is performed on the basis of the ranking of the current and the neighboring cells. The ranking is performed on the basis of the following R values
Rs = Qmeas,s + Qhysts
Rn = Qmeas,n – Qoffsets,n ­

Cell Selection Process Diagram
In the Idle mode the CSE performs the following tasks in order to select a cell:
1. When a PLMN is selected the cell selection is made through one of the cell selection procedures
2. If suitable cell is found the UE camp normally on the cell.
3. In camp normally state the cell reselection is made depending upon the triggers described later in this document.
4. If no suitable cell found in 2, the UE searches for an acceptable cell and enters the camp on any cell state
5. In camp on any state the cell reselection is made depending upon the triggers, if no suitable cell found the UE move to any cell state
6. If suitable cell found in 5 the UE move to the camp normally state.
7. In either state i.e. camped normally or camped on any when ever the UE returns from the connected mode to the idle mode the UE tries to camp on the last cell used for camping in the connected mode.
8. In case last cell in 5 is not a suitable or acceptable cell, procedure 1 and 4 are repeated depending upon the camped state.

The following figure shows the process diagram of the cell selection and re-selection
in idle mode for LTE.

OSAL Architecture

OSAL Architecture
OSAL acts as a middle layer between a real time operating system and an application code. Therefore; the application code and real time operating system has a clear and a distinct separation. The general architecture of the OSAL is shown in the figure below;

The above figure describes that; OSAL provides the elimination of the tight coupling between real time operating system and an application. Moreover; an application code has been completely unaffected and an application code can be moved to a new real time operating system without modification to an application. The concept is illustrated in the under mentioned figure;

The above figure describes that; an application calls the applications programming interface (API) of OSAL instead to directly invoke the kernel API. Therefore; moving from one RTOS to another, no modification is required in an application code.

Example of OSAL using ECos RTOS