6
\$\begingroup\$

I'm working on a network sniffer program, whose structure uses 3 main classes:

  1. Frame Class - One per monitored frame, holds reference to all the other sub frames
  2. Parse Class - Holds the hex value of the entire frame
  3. Protocol Classes - Individual classes for each network protocol

When a new frame is captured its sent to the Frame() class and put inside a Parse() object for unpacking at each layer:

enter image description here

Inside the frame object, a check is made to see what protocol comes next, that's then registered up at the top of the frame class (e.g., IPv4 ipv4 = new IPv4(this);), and the Frame instance is passed to that object. When the processing is done in the protocol class, a call is made to one of the switch methods (back in Frame()) to determine which protocol comes next.

Frame Class:

package frames;

import java.util.ArrayList;

import frames.Specification.PROTO;
import utils.Parser;
import utils.Utils;

public class Frame
{
    public static int pakCount = 0;
    public static int parseErrCount = 0;
    public static boolean print = true;

    public int dlt = -1;
    public Parser parse = null;

    public IEEE_802_3_ethernet_header ieee_802_3_ethernet_header = null;
    public IEEE_802_11_radiotap_header ieee_802_11_radiotap_header = null;
    public IEEE_802_11_mgmt_beacon ieee_802_11_mgmt_beacon = null;
    public IPv4 ipv4 = null;
    public UDP udp = null;
    public TCP tcp = null;
    public ICMP icmp = null;
    public ICMPv6 icmpv6 = null;
    public IPv6 ipv6 = null;

    private ArrayList<PROTO> protocols = new ArrayList<PROTO>();

    public static Specification spec = new Specification();

    public Frame(String dlt, String hex) 
    {           
        Frame.pakCount++;
        parse = new Parser(hex);

        if (Frame.print)
        {
            System.out.print("-" + Frame.pakCount);
            System.out.println(Utils.repeat(400, "-"));
            parse.printHex();
        }

        dltSwitch(Integer.valueOf(dlt));

        if (parse.parseErr == true)
        {
            parseErrCount++;
        }
    }

    // See [1] for DLT numbers
    public void dltSwitch(int dlt)
    {
        if (Frame.print)
            System.out.println("[+] DLT: " + dlt);

        switch(dlt)
        {
            case 1:
                ieee_802_3_ethernet_header = new IEEE_802_3_ethernet_header(this);
                break;
            case 127:
                ieee_802_11_radiotap_header = new IEEE_802_11_radiotap_header(this);
                ieee_802_11_mgmt_beacon = new IEEE_802_11_mgmt_beacon(this);
                break;
        }
    }

    public void etherTypeSwitch(int etherType)
    {       
        switch (etherType)
        {
            case 2048:
                ipv4 = new IPv4(this);
                break;
            case 34525:
                ipv6 = new IPv6(this);
                break;
        }
    }

    public void ipProtoSwitch(int proto)
    {       
        switch (proto)
        {
            case 1:
                icmp = new ICMP(this);
                break;
            case 6:
                tcp = new TCP(this);
                break;
            case 17:
                udp = new UDP(this);
                break;
            case 58:
                icmpv6 = new ICMPv6(this);
                break;
        }
    }

    public static void spec()
    {
        spec.setLinks(1, "https://github.com/the-tcpdump-group/libpcap/blob/master/pcap/dlt.h");
    }

    public void setProto(PROTO p)
    {
        protocols.add(p);
    }
}

IpV4 Class: (all the other protocol classes are structurally similar)

package frames;

import frames.Specification.ENDIANNESS;
import frames.Specification.PROTO;
import utils.Types;

public class IPv4
{
    public String version = "";
    public String internetHeaderLength = "";
    public String typeOfService = "";
    public int totalLengthBits = -1;

    public String identification = "";
    public String unassigned = "";
    public String dontFrag = ""; 
    public String moreFrag = "";
    public String fragOffset = "";

    public String ttl = "";
    public int protocol = -1;
    public String checksum = "";

    public String sourceIP = "";
    public String destIP = "";

    public static Specification spec = new Specification();

    public IPv4(Frame frame) 
    {   
        this.version = frame.parse.nextBits(4);
        this.internetHeaderLength = frame.parse.nextBits(4);
        this.typeOfService = frame.parse.nextBytes(1);
        this.totalLengthBits = (frame.parse.nextBytesNum(2) * 8);

        this.identification = frame.parse.nextBytes(2);
        this.unassigned = frame.parse.nextBits(1);
        this.dontFrag = frame.parse.nextBits(1);
        this.moreFrag = frame.parse.nextBits(1);
        this.fragOffset = frame.parse.nextBits(13);

        this.ttl = frame.parse.nextBytes(1);
        this.protocol = Types.hexToNum(frame.parse.nextBytes(1));
        this.checksum = frame.parse.nextBytes(2);

        this.sourceIP = frame.parse.nextBytes(4);
        this.destIP = frame.parse.nextBytes(4);

        print();
        frame.ipProtoSwitch(protocol);
    }

    public void print()
    {
        if (Frame.print)
        {
            System.out.printf("IPv4 Header \n");
            System.out.printf("    | [Version: %s][IHL: %s][TOS: %s][LEN: %s]\n", version, internetHeaderLength, typeOfService, totalLengthBits);
            System.out.printf("    | [ID: %s][Unassigned: %s][DF: %s][MF: %s][FragOffset: %s]\n", identification, unassigned, dontFrag, moreFrag, fragOffset);
            System.out.printf("    | [TTL: %s][PROTO: %s][Checsum: %s]\n", ttl, protocol, checksum);
            System.out.printf("    | [Source: %s][Dest: %s]\n", Types.hexToIP(sourceIP), Types.hexToIP(destIP));
        }
    }

    public void spec()
    {
        spec.setEndianess(ENDIANNESS.big);
        spec.setName(PROTO.ipv4);
        spec.setLinks(1, "https://en.wikipedia.org/wiki/List_of_IP_protocol_numbers");
    }
}

Parser class for breaking up the frame bits:

package utils;

import java.util.zip.CRC32;

public class Parser 
{
    public String hex = null;
    public String binary = null;
    public boolean parseErr = false;

    public int offset = 0;
    public int hexLen = 0;
    public int binLen = 0;
    private int crcIndex = 0;

    public Parser(String hex) 
    {
        this.hex = hex;
        this.binary = Types.hexToBin(hex);

        hexLen = hex.length();
        binLen = binary.length();
    }

    public void printHex()
    {
        System.out.println(hex);
        System.out.println("Raw: " + Utils.repeat((offset / 4) + 5, " ") + "^");
    }

    public void printBin()
    {
        System.out.println(binary);
    }

    public void initialiseCRC()
    {
        crcIndex = offset;
    }

    /** Performs the Cyclic redundancy check.
     *  Prerequisite: must call crcInit() at the start of the frame
     *  Input: The CRC from the frame
     *  Process: Substrings the binary string using the CRC index - the 32 bit CRC value
     *  Returns: True if matches, else false   */
    public boolean checkCRC32(String crc)
    {
        CRC32 crc32 = new CRC32();
        String substr = "";

        try {
            substr = binary.substring(crcIndex, binLen - 32);
        } catch (StringIndexOutOfBoundsException e) {
            this.parseErr = true;
            System.out.println(e);
            return false;
        }

        crc32.update(Types.hexToBytes(Types.binToHex(substr)));
        String calculatedCRC = String.format("%X", crc32.getValue());

        if (crc.equals(calculatedCRC))
        {
            return true;
        }
        else
        {
            this.parseErr = true;
            return false;
        }
    }

    public String lastBits(int n)
    {
        String substr = "";

        try {
            substr = binary.substring((binLen - n), binLen);
        } catch (StringIndexOutOfBoundsException e) {
            this.parseErr = true;
            System.out.println(e);
            return "ERROR";
        }

        return Types.binToHex(substr);
    }

    public String lastBytes(int n)
    {
        return lastBits(n * 8);
    }

    /** Returns the next # of bytes as hex */
    public String nextBits(int n)
    {
        int numBits = n;
        int newOffset = offset + numBits;
        String substr = "";

        try {
            substr = binary.substring(offset, newOffset);
        } catch (StringIndexOutOfBoundsException e) {
            this.parseErr = true;
            return "0000";
        }

        offset = newOffset;

        return Types.binToHex(substr);
    }

    /** Returns the next 8 bits as hex */
    public String nextBytes(int n)
    {
        return nextBits(n * 8);
    }

    public int nextBytesNum(int n)
    {
        return Types.hexToNum(nextBits(n * 8));
    }

    public String nextBytesIP(int n)
    {
        return Types.hexToIP(nextBits(n * 8));
    }

    public String nextBytesMAC(int n)
    {
        return Types.hexToMac((nextBits(n * 8)));
    }

    /** Returns next 8 bits has hex with bytes flipped */
    public String nextBytesOrder(int n)
    {
        String bits = nextBits(n * 8);
        return Utils.flipBytes(bits);
    }
}

I'm trying to find a better class organisation to represent these nested classes. The requirements are that the current protocol class being processed needs to be able to access the fields of the protocol class above it. I was thinking of some sort of inheritance but with so many protocol class types I can't think of a way to make it work.

\$\endgroup\$
  • 1
    \$\begingroup\$ I don't see the class definitions for most of the classes that you list in the question. Rather than posting just a few functions why not post the entire classes. This will help us with the revoiew. \$\endgroup\$ – pacmaninbw May 23 at 19:43
  • \$\begingroup\$ @pacmaninbw as requested \$\endgroup\$ – Crizly May 23 at 22:49
3
\$\begingroup\$

I will focus on programming in this review.


Encapsulation

From Wikipedia

Encapsulation, in object-oriented programming, is the bundling of data with the methods that operate on that data, or the restricting of direct access to some of an objects components. Encapsulation is used to hide the values or state of a structured data object inside a class, preventing unauthorized parties' direct access to them.

All class fields are declared as public, which is reason they do not protect their data/state.

Two reasons why we should avoid public fields are:

  • unauthorized classes can change the data and break the code
  • we can't abstract multiple classes, when every one knows the internals.

Abstraction

From Wikipedia

In object-oriented programming theory, abstraction involves the facility to define objects that represent abstract "actors" that can perform work, report on and change their state, and "communicate" with other objects in the system.

In the current form, the code is bloated with concrete things like IPv4, IPv6, TCP, ... but actually these things are just different types of a Protocol.

Class Design of Frame

Variables

public IEEE_802_3_ethernet_header ieee_802_3_ethernet_header = null;
public IEEE_802_11_radiotap_header ieee_802_11_radiotap_header = null;
public IEEE_802_11_mgmt_beacon ieee_802_11_mgmt_beacon = null;
public IPv4 ipv4 = null;
public UDP udp = null;
public TCP tcp = null;
public ICMP icmp = null;
public ICMPv6 icmpv6 = null;
public IPv6 ipv6 = null;

If I' wrong please ignore this, but I thing a Frame can only include:

  • ieee_802_3 or ieee_802_11 on the Link Layer
  • ipv4 or ipv6 or icmp on the Internet Layer
  • tcp or udp on the Transport Layer

After we add an abstract Protocol to the system, we can achieve something like

private Protocol link;
private Protocol internet;
private Protocol transport;

We would reduce the complexity a lot by decreasing the number of variables.

"switch methods"

With the introduction of the abstraction Protocol we can use the Factory Method Pattern for each TCP/IP layer to remove the logic from the Frame into its own class. When we encapsulate logic that belongs together we can achieve Separation Of Concerns.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.