Brief introduction to linux socket filtering

We give a brief overview of the linux socket filtering framework. Starting with a breif overview of raw sockets we look at the attaching filters to sockets and give a breif overview of the berekely packet filter machine implemented in the kernel.

Introduction.

Linux socket filter is a register based packet filtering machine. It has two components a network tap and a packet filter. The tap handles the copying and delivery, the filter handles decision to copy. The user has the ability to specify how many bytes of the packet he would like to copied for his use. Only bytes needed for filtering are ever referenced.

Raw Sockets

The user can attach filters to sockets via a call to the setsockopt system call specifying either the SO_ATTACH_FILTER or SO_ATTACH_BPF to the socket. Now in our case assuming we created our socket as a SOCK_RAW socket

The entry for SOCK_RAW in the inetsw_array looks like

       {
	       .type =       SOCK_RAW,
	       .protocol =   IPPROTO_IP,	/* wild card */
	       .prot =       &raw_prot,
	       .ops =        &inet_sockraw_ops,
	       .flags =      INET_PROTOSW_REUSE,
       }
// From af_inet.c       
// From  raw.c
struct proto raw_prot = {
	.name		   = "RAW",
	.owner		   = THIS_MODULE,
	.close		   = raw_close,
	.destroy	   = raw_destroy,
	.connect	   = ip4_datagram_connect,
	.disconnect	   = udp_disconnect,
	.ioctl		   = raw_ioctl,
	.init		   = raw_init,
	.setsockopt	   = raw_setsockopt,
	.getsockopt	   = raw_getsockopt,
	.sendmsg	   = raw_sendmsg,
	.recvmsg	   = raw_recvmsg,
	.bind		   = raw_bind,
	.backlog_rcv	   = raw_rcv_skb,
	.release_cb	   = ip4_datagram_release_cb,
	.hash		   = raw_hash_sk,
	.unhash		   = raw_unhash_sk,
	.obj_size	   = sizeof(struct raw_sock),
	.h.raw_hash	   = &raw_v4_hashinfo,
#ifdef CONFIG_COMPAT
	.compat_setsockopt = compat_raw_setsockopt,
	.compat_getsockopt = compat_raw_getsockopt,
	.compat_ioctl	   = compat_raw_ioctl,
#endif
};

Going back the function of ip_local_deliver_finish Where the ip header is checked to find out the protocol to dispatch incoming packet to.

We see the function raw_local_deliver is used to made the determination if the packet is meant for delivery to a raw socket.

int raw_local_deliver(struct sk_buff *skb, int protocol)
{
	int hash;
	struct sock *raw_sk;

	hash = protocol & (RAW_HTABLE_SIZE - 1);
	raw_sk = sk_head(&raw_v4_hashinfo.ht[hash]);

	/* If there maybe a raw socket we must check - if not we
	 * don't care less
	 */
	if (raw_sk && !raw_v4_input(skb, ip_hdr(skb), hash))
		raw_sk = NULL;

	return raw_sk != NULL;

}

This will determine if there is a raw socket that is waiting on the protocol. If there is then we must deliver the packet to this socket. The __raw_v4_lookup function is used to look up raw sockets that would be interested in this skb. Attributes of the look up may include protocol, source address, destination address, device interface index. We call skb_clone function to clone the skb and call the raw_rcv on the socket with the cloned packet. raw_rcv will reset the packet headers back to pointing to this IP layer. Finally adding the packet to the socket’s receive queue. The raw_rcv will eventually call sock_queue_rcv_skb. Which will check if the packet ought to be filtered via a call to the sk_filter function.

//from raw.c 
static int raw_v4_input(struct sk_buff *skb, const struct iphdr *iph, int hash)
{
	struct sock *sk;
	struct hlist_head *head;
	int delivered = 0;
	struct net *net;

	read_lock(&raw_v4_hashinfo.lock);
	head = &raw_v4_hashinfo.ht[hash];
	if (hlist_empty(head))
		goto out;

	net = dev_net(skb->dev);
	sk = __raw_v4_lookup(net, __sk_head(head), iph->protocol,
			     iph->saddr, iph->daddr,
			     skb->dev->ifindex);

	while (sk) {
		delivered = 1;
		if ((iph->protocol != IPPROTO_ICMP || !icmp_filter(sk, skb)) &&
		    ip_mc_sf_allow(sk, iph->daddr, iph->saddr,
				   skb->dev->ifindex)) {
			struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);

			/* Not releasing hash table! */
			if (clone)
				raw_rcv(sk, clone);
		}
		sk = __raw_v4_lookup(net, sk_next(sk), iph->protocol,
				     iph->saddr, iph->daddr,
				     skb->dev->ifindex);
	}
out:
	read_unlock(&raw_v4_hashinfo.lock);
	return delivered;
}

If we had attached a socket filter the raw socket we are going to run this filter at this point.

The packet filter details

The call to sk_filter gets passed in a socket and an skb. Inside the socket if we have attached a socket filter this socket filter struct sk_filter *filter is going to be found. Resulting in a call to SK_RUN_FILTER. The return type of which is amount of packet information the user program is interested in.

// from filter.h
/* Macro to invoke filter function. */
#define SK_RUN_FILTER(filter, ctx) \
(*filter->prog->bpf_func)(ctx, filter->prog->insnsi)

.....

struct bpf_prog {
	u16			pages;		/* Number of allocated pages */
	bool			jited;		/* Is our filter JIT'ed? */
	u32			len;		/* Number of filter blocks */
	struct sock_fprog_kern	*orig_prog;	/* Original BPF program */
	struct bpf_prog_aux	*aux;		/* Auxiliary fields */
	unsigned int		(*bpf_func)(const struct sk_buff *skb,
					    const struct bpf_insn *filter);
	/* Instructions for interpreter */
	union {
		struct sock_filter	insns[0];
		struct bpf_insn		insnsi[0];
	};
};

struct sk_filter {
	atomic_t	refcnt;
	struct rcu_head	rcu;
	struct bpf_prog	*prog;
};

During the creation of the packet filter the program that the user passed in will be copied into the socket filter and jit compiled via bpf_jit_compile or in case no jit is available the program is translated into an optimized interpreter via bpf_migrate_filter.

Understanding the instruction set

The BPF engine contains a low level asm-like filter language. Consisting of the following basic elements

  Element          Description

  A                32 bit wide accumulator
  X                32 bit wide X register
  M[]              16 x 32 bit wide misc registers aka "scratch memory
                   store", addressable from 0 to 15

An instruction would look like

    op:16, jt:8, jf:8, k:32

op is a 16 bit opcode for an instruction, jt and jf are two 8-bit jump targets where jt is jump target if true and jf is jump target if false k is an instruction dependent argument.

Following table lists the instructions based on the above machine model. With A as accumulator, X as register and M as scratch memory.

  Instruction      Addressing mode      Description

  ld               1, 2, 3, 4, 10       Load word into A
  ldi              4                    Load word into A
  ldh              1, 2                 Load half-word into A
  ldb              1, 2                 Load byte into A
  ldx              3, 4, 5, 10          Load word into X
  ldxi             4                    Load word into X
  ldxb             5                    Load byte into X

  st               3                    Store A into M[]
  stx              3                    Store X into M[]

  jmp              6                    Jump to label
  ja               6                    Jump to label
  jeq              7, 8                 Jump on k == A
  jneq             8                    Jump on k != A
  jne              8                    Jump on k != A
  jlt              8                    Jump on k < A
  jle              8                    Jump on k <= A
  jgt              7, 8                 Jump on k > A
  jge              7, 8                 Jump on k >= A
  jset             7, 8                 Jump on k & A

  add              0, 4                 A + <x>
  sub              0, 4                 A - <x>
  mul              0, 4                 A * <x>
  div              0, 4                 A / <x>
  mod              0, 4                 A % <x>
  neg              0, 4                 !A
  and              0, 4                 A & <x>
  or               0, 4                 A | <x>
  xor              0, 4                 A ^ <x>
  lsh              0, 4                 A << <x>
  rsh              0, 4                 A >> <x>

  tax                                   Copy A into X
  txa                                   Copy X into A

  ret              4, 9                 Return
  

Along with the above instructions we have the following addressing modes for addressing locations in packets, in scratch memory and registers.

  Addressing mode  Syntax               Description

   0               x/%x                 Register X
   1               [k]                  BHW at byte offset k in the packet
   2               [x + k]              BHW at the offset X + k in the packet
   3               M[k]                 Word at offset k in M[]
   4               #k                   Literal value stored in k
   5               4*([k]&0xf)          Lower nibble * 4 at byte offset k in the packet
   6               L                    Jump label L
   7               #k,Lt,Lf             Jump to Lt if true, otherwise jump to Lf
   8               #k,Lt                Jump to Lt if predicate is true
   9               a/%a                 Accumulator A
   10               extension            BPF extension

Some linux extensions to BPF which allow more convenient access to frequently needed data.

Possible BPF extensions are shown in the following table:

  Extension                             Description

  len                                   skb->len
  proto                                 skb->protocol
  type                                  skb->pkt_type
  poff                                  Payload start offset
  ifidx                                 skb->dev->ifindex
  nla                                   Netlink attribute of type X with offset A
  nlan                                  Nested Netlink attribute of type X with offset A
  mark                                  skb->mark
  queue                                 skb->queue_mapping
  hatype                                skb->dev->type
  rxhash                                skb->hash
  cpu                                   raw_smp_processor_id()
  vlan_tci                              vlan_tx_tag_get(skb)
  vlan_pr                               vlan_tx_tag_present(skb)
  rand                                  prandom_u32()
  

To get a feel for some of these instructions and their usages consider a sample BPF programs.

/* IPv4 TCP packet filter */
ldh [12]           /* not sure */
jne #0x800, drop   /*  If loaded half word(16 bits?) is not #0x800  go to label drop */
                   /*  I think checking for ip. */
ldb [23]           /*  load byte at offset 23  the protocol field of the ip packet */
jneq #6, drop      /*  compare with protocol number for tcp which  0x6 not go to drop */
ret #-1            /*  accept whole packet */
drop: ret #0       /*  drop the packet */
  

We can now save this into a file say tcp.f , and run the accompanying assembler. We can generate code that can be directly loaded by the bpf_dbg where commands get converted into their op codes.

$ ./bpf_asm tcp.f
6,40 0 0 12,21 0 3 2048,48 0 0 23,21 0 1 6,6 0 0 4294967295,6 0 0 0,

Often one needs to get bpf code in syntax used to specify filters in C code. We can get this by running

$ ./bpf_asm -c tcp.f
{ 0x28,  0,  0, 0x0000000c },
{ 0x15,  0,  3, 0x00000800 },
{ 0x30,  0,  0, 0x00000017 },
{ 0x15,  0,  1, 0x00000006 },
{ 0x06,  0,  0, 0xffffffff },
{ 0x06,  0,  0, 0000000000 },

This code can be used in one’s c code to while attaching to sockets like so

// untested.
// Where the instructionformat is
// bpf.h
struct bpf_insn {
	__u8	code;		/* opcode */
	__u8	dst_reg:4;	/* dest register */
	__u8	src_reg:4;	/* source register */
	__s16	off;		/* signed offset */
	__s32	imm;		/* signed immediate constant */
};

// in user code.
struct bpf_insn insns[] = {
     { 0x28,  0,  0, 0x0000000c },
     { 0x15,  0,  3, 0x00000800 },
     { 0x30,  0,  0, 0x00000017 },
     { 0x15,  0,  1, 0x00000006 },
     { 0x06,  0,  0, 0xffffffff },
     { 0x06,  0,  0, 0000000000 },
};

Since tools like tcpdump use the libpcap library to compile user specified filter commands to bpf they can be helpful aids to quickly generating bpf code. This is where tcpdump options -d , -dd and -ddd are helpful. As shown bellow. We can use -d to see the mnemonic code which the tcpdump will generate for an expression. -dd to generate C-code for an expression and finally -ddd will generate them as decimal numbers loadable directly into bpf_dbg and other tools.

# tcpdump -iwlan0 -d  'tcp'
(000) ldh      [12]
(001) jeq      #0x86dd          jt 2	jf 7
(002) ldb      [20]
(003) jeq      #0x6             jt 10	jf 4
(004) jeq      #0x2c            jt 5	jf 11
(005) ldb      [54]
(006) jeq      #0x6             jt 10	jf 11
(007) jeq      #0x800           jt 8	jf 11
(008) ldb      [23]
(009) jeq      #0x6             jt 10	jf 11
(010) ret      #262144
(011) ret      #0

# sudo tcpdump -iwlan0 -dd  'tcp'
{ 0x28, 0, 0, 0x0000000c },
{ 0x15, 0, 5, 0x000086dd },
{ 0x30, 0, 0, 0x00000014 },
{ 0x15, 6, 0, 0x00000006 },
{ 0x15, 0, 6, 0x0000002c },
{ 0x30, 0, 0, 0x00000036 },
{ 0x15, 3, 4, 0x00000006 },
{ 0x15, 0, 3, 0x00000800 },
{ 0x30, 0, 0, 0x00000017 },
{ 0x15, 0, 1, 0x00000006 },
{ 0x6, 0, 0, 0x00040000 },
{ 0x6, 0, 0, 0x00000000 },

# sudo tcpdump -iwlan0 -ddd 'tcp
12
40 0 0 12
21 0 5 34525
48 0 0 20
21 6 0 6
21 0 6 44
48 0 0 54
21 3 4 6
21 0 3 2048
48 0 0 23
21 0 1 6
6 0 0 262144
6 0 0 0

One additional tool that my be helpful to mention in the context of the bpf. Is the bpf_dbg tool(in tools/net directory of kernel source) which can be used to debug bpf filters over pcap files.This a bpf debugger allowing us to run bpf , step through the code and more.

// First we capture some traffic in the the pcap format
# sudo tcpdump -s 0 -i wlan0 -w mycap.pcap

// we generate the decimal notation of our desired packet filter.
# sudo tcpdump -iwlan0 -ddd  'tcp' | tr '\n' ','
12,40 0 0 12,21 0 5 34525,48 0 0 20,21 6 0 6,21 0 6 44,48 0 0 54,21 3 4 6,21 0 3 2048,48 0 0 23,21 0 1 6,6 0 0 262144,6 0 0 0,

# sudo ./bpf_dbg
> load pcap mycap.pcap
> load bpf 12,40 0 0 12,21 0 5 34525,48 0 0 20,21 6 0 6,21 0 6 44,48 0 0 54,21 3 4 6,21 0 3 2048,48 0 0 23,21 0 1 6,6 0 0 262144,6 0 0 0,
> run
bpf passes:1716 fails:432
> step
-- register dump --
pc:       [0]
code:     [40] jt[0] jf[0] k[12]
curr:     l0:	ldh [12]
A:        [00000000][0]
X:        [00000000][0]
M[0,15]:  [00000000][0]
-- packet dump --
len: 86
  0: 74 9d dc 8d 68 61 00 22 68 ac 53 2f 86 dd 60 00 
 16: 00 00 00 20 06 40 26 02 03 06 bc ca f1 00 4c 72 
 32: 39 e9 c1 5c 0c 1a 20 01 4d e0 41 01 00 01 00 00 
 48: 00 00 d1 6b c2 58 d2 54 01 bb f0 71 8c f0 3b ef 
 64: 34 b6 80 10 01 ec 51 2b 00 00 01 01 08 0a 00 b6 
 80: 2b 78 81 b3 46 5f 
 

Where the load can be used in to load in captured packets and bpf code which can then be stepped through and debugged.

Other helpful commands available in the debugger are

disassemble    - get the mnemonic code for filter
dump           - dump c like code 
breakpoint #   -  set a line break point
select     #   - select a given packet for running index of 1
step    #      - step and dump registers
quit           - quit the debugger

Complete Example

Finally to wrap up we present a complete example program that uses raw sockets to listen to http requests and responses and prints it out to standard out. This is just a modification of the code presented in the binary tides article for illustration purposes.

/** References : https://gist.github.com/msantos/939154
    http://www.binarytides.com/packet-sniffer-code-in-c-using-linux-sockets-bsd-part-2/
*/
#include <stdio.h>
#include<errno.h>

#include<stdio.h> //For standard things
#include<stdlib.h>    //malloc
#include<string.h>    //strlen

#include<netinet/tcp.h>   //Provides declarations for tcp header
#include<netinet/ip.h>    //Provides declarations for ip header
#include<net/ethernet.h>  //For ether_header

#include<sys/socket.h>
#include<arpa/inet.h>

#include <linux/filter.h>

//sudo tcpdump -A -dd -s 0 'tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)'
struct sock_filter tcp_filter [] = {
{ 0x28, 0, 0, 0x0000000c },
{ 0x15, 27, 0, 0x000086dd },
{ 0x15, 0, 26, 0x00000800 },
{ 0x30, 0, 0, 0x00000017 },
{ 0x15, 0, 24, 0x00000006 },
{ 0x28, 0, 0, 0x00000014 },
{ 0x45, 22, 0, 0x00001fff },
{ 0xb1, 0, 0, 0x0000000e },
{ 0x48, 0, 0, 0x0000000e },
{ 0x15, 2, 0, 0x00000050 },
{ 0x48, 0, 0, 0x00000010 },
{ 0x15, 0, 17, 0x00000050 },
{ 0x28, 0, 0, 0x00000010 },
{ 0x2, 0, 0, 0x00000001 },
{ 0x30, 0, 0, 0x0000000e },
{ 0x54, 0, 0, 0x0000000f },
{ 0x64, 0, 0, 0x00000002 },
{ 0x7, 0, 0, 0x00000005 },
{ 0x60, 0, 0, 0x00000001 },
{ 0x1c, 0, 0, 0x00000000 },
{ 0x2, 0, 0, 0x00000005 },
{ 0xb1, 0, 0, 0x0000000e },
{ 0x50, 0, 0, 0x0000001a },
{ 0x54, 0, 0, 0x000000f0 },
{ 0x74, 0, 0, 0x00000002 },
{ 0x7, 0, 0, 0x00000009 },
{ 0x60, 0, 0, 0x00000005 },
{ 0x1d, 1, 0, 0x00000000 },
{ 0x6, 0, 0, 0x00040000 },
{ 0x6, 0, 0, 0x00000000 },
};

void print_tcp_packet(unsigned char* packet, int size);
void process_packet(char* packet, int packet_size);
void print_ip_header(unsigned char* packet, int packet_size);
void print_data (unsigned char* data , int Size);


int main(int argc , char* argv[]){
  unsigned char *packet ;
  int saddr_size ;
  int data_size;
  struct sockaddr saddr;         

  const int max_packet_size = 65536; //Its Big!
  printf("begin: http sniff\n");
  
  packet  = (unsigned char *) malloc(max_packet_size); 
  
  int sock_fd = socket(AF_PACKET,SOCK_RAW,htons(ETH_P_ALL)) ;
  if(sock_fd < 0)    {    
    perror("Socket Error");
    return 1;
  }
  struct sock_fprog fcode = {0};
  fcode.len = sizeof(tcp_filter) / sizeof(struct sock_filter);
  fcode.filter = &tcp_filter[0];
  
  setsockopt(sock_fd,SOL_SOCKET,SO_ATTACH_FILTER,&fcode,sizeof(fcode));
  
  while(1)
    {
        saddr_size = sizeof saddr;
        //Receive a packet
        data_size = recvfrom(sock_fd , packet , max_packet_size , 0 , &saddr , (socklen_t*)&saddr_size);
        if(data_size <0 )
        {
            printf("Recvfrom error , failed to get packets\n");
            return 1;
        }
        process_packet(packet,data_size);
    }
  
  free(packet);
  printf("end: http sniff\n");
  return 0;
}

void process_packet(char* packet, int packet_size)
{
  printf("process_packet: packet size %d \n",packet_size);
  print_tcp_packet(packet, packet_size);
}


void print_ip_header(unsigned char* Buffer, int Size)
{
  //    print_ethernet_header(Buffer , Size);
  struct sockaddr_in source,dest;
    unsigned short iphdrlen;
         
    struct iphdr *iph = (struct iphdr *)(Buffer  + sizeof(struct ethhdr) );
    iphdrlen =iph->ihl*4;

    memset(&source, 0, sizeof(source));
    source.sin_addr.s_addr = iph->saddr;
     
    memset(&dest, 0, sizeof(dest));
    dest.sin_addr.s_addr = iph->daddr;
     
    fprintf(stdout , "\n");
    fprintf(stdout , "IP Header\n");
    fprintf(stdout , "   |-IP Version        : %d\n",(unsigned int)iph->version);
    fprintf(stdout , "   |-IP Header Length  : %d DWORDS or %d Bytes\n",(unsigned int)iph->ihl,((unsigned int)(iph->ihl))*4);
    fprintf(stdout , "   |-Type Of Service   : %d\n",(unsigned int)iph->tos);
    fprintf(stdout , "   |-IP Total Length   : %d  Bytes(Size of Packet)\n",ntohs(iph->tot_len));
    fprintf(stdout , "   |-Identification    : %d\n",ntohs(iph->id));
    //fprintf(stdout , "   |-Reserved ZERO Field   : %d\n",(unsigned int)iphdr->ip_reserved_zero);
    //fprintf(stdout , "   |-Dont Fragment Field   : %d\n",(unsigned int)iphdr->ip_dont_fragment);
    //fprintf(stdout , "   |-More Fragment Field   : %d\n",(unsigned int)iphdr->ip_more_fragment);
    fprintf(stdout , "   |-TTL      : %d\n",(unsigned int)iph->ttl);
    fprintf(stdout , "   |-Protocol : %d\n",(unsigned int)iph->protocol);
    fprintf(stdout , "   |-Checksum : %d\n",ntohs(iph->check));
    fprintf(stdout , "   |-Source IP        : %s\n",inet_ntoa(source.sin_addr));
    fprintf(stdout , "   |-Destination IP   : %s\n",inet_ntoa(dest.sin_addr));

}



void print_tcp_packet(unsigned char* Buffer, int Size)
{
    unsigned short iphdrlen;
     
    struct iphdr *iph = (struct iphdr *)( Buffer  + sizeof(struct ethhdr) );
    iphdrlen = iph->ihl*4;
     
    struct tcphdr *tcph=(struct tcphdr*)(Buffer + iphdrlen + sizeof(struct ethhdr));
             
    int header_size =  sizeof(struct ethhdr) + iphdrlen + tcph->doff*4;
     
    fprintf(stdout , "\n\n***********************TCP Packet*************************\n");  
         
    print_ip_header(Buffer,Size);
         
    fprintf(stdout , "\n");
    fprintf(stdout , "TCP Header\n");
    fprintf(stdout , "   |-Source Port      : %u\n",ntohs(tcph->source));
    fprintf(stdout , "   |-Destination Port : %u\n",ntohs(tcph->dest));
    fprintf(stdout , "   |-Sequence Number    : %u\n",ntohl(tcph->seq));
    fprintf(stdout , "   |-Acknowledge Number : %u\n",ntohl(tcph->ack_seq));
    fprintf(stdout , "   |-Header Length      : %d DWORDS or %d BYTES\n" ,(unsigned int)tcph->doff,(unsigned int)tcph->doff*4);
    //fprintf(stdout , "   |-CWR Flag : %d\n",(unsigned int)tcph->cwr);
    //fprintf(stdout , "   |-ECN Flag : %d\n",(unsigned int)tcph->ece);
    fprintf(stdout , "   |-Urgent Flag          : %d\n",(unsigned int)tcph->urg);
    fprintf(stdout , "   |-Acknowledgement Flag : %d\n",(unsigned int)tcph->ack);
    fprintf(stdout , "   |-Push Flag            : %d\n",(unsigned int)tcph->psh);
    fprintf(stdout , "   |-Reset Flag           : %d\n",(unsigned int)tcph->rst);
    fprintf(stdout , "   |-Synchronise Flag     : %d\n",(unsigned int)tcph->syn);
    fprintf(stdout , "   |-Finish Flag          : %d\n",(unsigned int)tcph->fin);
    fprintf(stdout , "   |-Window         : %d\n",ntohs(tcph->window));
    fprintf(stdout , "   |-Checksum       : %d\n",ntohs(tcph->check));
    fprintf(stdout , "   |-Urgent Pointer : %d\n",tcph->urg_ptr);
    fprintf(stdout , "\n");
    fprintf(stdout , "                        DATA Dump                         ");
    fprintf(stdout , "\n");

    /**
    fprintf(stdout , "IP Header\n");
    print_data(Buffer,iphdrlen);
         
    fprintf(stdout , "TCP Header\n");
    print_data(Buffer+iphdrlen,tcph->doff*4);
    */                           
    fprintf(stdout , "Data Payload\n");    
    print_data(Buffer + header_size , Size - header_size );

    fprintf(stdout , "\n###########################################################\n");
}



void print_data (unsigned char* data , int Size)
{
    int i , j;
    for(i=0 ; i < Size ; i++)
    {
        if( i!=0 && i%16==0)   //if one line of hex printing is complete...
        {
            fprintf(stdout , "         ");
            for(j=i-16 ; j<i ; j++)
            {
                if(data[j]>=32 && data[j]<=128)
                    fprintf(stdout , "%c",(unsigned char)data[j]); //if its a number or alphabet
                 
                else fprintf(stdout , "."); //otherwise print a dot
            }
            fprintf(stdout , "\n");
        } 
         
        if(i%16==0) fprintf(stdout , "   ");
            fprintf(stdout , " %02X",(unsigned int)data[i]);
                 
        if( i==Size-1)  //print the last spaces
        {
            for(j=0;j<15-i%16;j++) 
            {
              fprintf(stdout , "   "); //extra spaces
            }
             
            fprintf(stdout , "         ");
             
            for(j=i-i%16 ; j<=i ; j++)
            {
                if(data[j]>=32 && data[j]<=128) 
                {
                  fprintf(stdout , "%c",(unsigned char)data[j]);
                }
                else
                {
                  fprintf(stdout , ".");
                }
            }
             
            fprintf(stdout ,  "\n" );
        }
    }
}

Summary

This article scratches the surface of understanding the flexibility offered in the linux networking stack. Since this more as a brain dump of reading through the source in the 3.19-rc7 kernel networking subsystem,thus it may be highly unreliable and inaccurate. Users beware! With that fair warning lets try to begin.Any errors in the article are entirely my fault.


Discussion, links, and tweets

I'm a developer interested in most things programming and mathematics related and the full spectrum in between.

comments powered by Disqus