Jpcap
Jon Hoffman
Have you ever tried to write a systems
administration or network security application in Java? If so, you know
that Java lacks the necessarily low-level network functionality needed for
these types of applications. You cannot handcraft a TCP/IP packet, set the
flags, or even ping a computer with the standard Java network classes. That
is where jpcap comes in.
Jpcap is a Java class that allows a Java application
to read/write packets to the network, and it is available from:
http://netresearch.ics.uci.edu/kfujii/jpcap/doc/index.html
Jpcap is based on winpcap/libpcap (winpcap.org and
tcpdump.org), so it should work on any operating system that they support.
In this article, I will use jpcap to develop a Java
class for use in your own applications, which will contain some of the
low-level network functionality needed to develop a "heartbeat"
function to let you know if a remote computer is on. This heartbeat
function will send a TCP packet to a remote computer with the
"ACK" (acknowledge) flag set and then listen for a TCP packet
back with the "RST" flag set. If the remote computer does not
respond with the RST packet, it is either off the network or the port to
which you sent the ACK packet is being blocked by a firewall.
The Three-Way Handshake
When a computer wants to communicate with a remote
computer using TCP/IP, a three-way handshake must occur. The first machine
begins by sending a packet with the "SYN" flag set. This is
like saying, "Hi, Joe. Can we talk?" The remote computer is
supposed to respond with a packet that has the SYN/ACK
(synchronize/acknowledge) flags set, which is like Joe saying, "Sure,
we can talk. What's up?"
The three-way handshake is completed when the first
machine responds with a packet that has the ACK flag set. This is like
responding to Joe by saying "Good, because I have something to tell
you." It's actually a bit more complicated than that, but to
fully explain how TCP/IP works is well beyond the scope of this article. If
you are interested in learning more about TCP/IP and how computers
communicate, there are many good Web sites and books dedicated to that
subject. Figure 1 shows the three-way handshake between two computers.
For the rest of this article, if I refer to a TCP
packet as an ACK packet or RST packet or any other TCP flag before the word
packet, that means it is a TCP packet with that flag(s) set to true.
If the first computer begins the handshaking with the
ACK packet, the second computer will think it has missed the first part of
the handshake and send a RST (reset) packet to the first computer so they
can begin again. This also lets the first computer know that the second
computer is "alive" on the network and that the port to which
the packet was sent is not blocked. Figure 2 shows how our heartbeat method
will work.
The key to this type of heartbeat application is that
the remote computer does not need to have any
client application listening on the port to
which the "ACK" packet is sent. The operating system will detect that the handshake did not occur properly and send a
reset. The only requirement is the port cannot
be blocked by a firewall.
Before you can install jpcap, you will need to
install libpcap for Unix/Linux or winpcap for Windows. If you are running
Unix/Linux, you probably already have libpcap installed; just make sure you
have version 0.9.4 or later. For most Linux distributions, you should go to
the /usr/lib directory and then do a ls libpcap*. If libpcap is installed, you will
see something like "libpcap.so.X.X.X" with the X.X.X as the version installed. If
you do not have libpcap installed, or you have an older version, please see http://www.tcpdump.org for instructions on how
to install it.
If you are running Windows, download the installer
from http://www.winpcap.org and just install it. If you already have winpcap installed,
the installer will let you know.
After you have libpcap/winpcap installed, download jpcap from:
http://netresearch.ics.uci.edu/kfujii/jpcap/doc/index.html
and follow the installation instructions on the Web site.
The Networking Class
Once everything is installed, we can start writing
our networking class. We'll begin the networking class with a simple
constructor method that will obtain a list of network devices on the system
and put the information into an array of NetworkInterfaces called devices
(this is the jpcap.NetworkInterface not the
java.net.NetworkInterface). The devices array will be used throughout our
networking class, so it is defined as a global array. Listing 1 shows the
start of our networking class.
You can refer to the JavaDoc API documentation for the
jpcap classes at:
http://netresearch.ics.uci.edu/kfujii/jpcap/doc/javadoc/index.html
for more details about the jpcap classes.
The getNetworkInterfaces() method returns the devices array so that a list of
devices can be printed out. This is very useful in determining which
network interface device to use in the other methods.
If you are working on a Windows machine, you will want
to print the list of network devices with the following code:
NetworkInterface[] devices = net.getNetowrkInterfaces();
for (int i=0; i<devices.length; i++)
System.out.println(i + ": " + devices[i].description);
The corresponding output should look something like this:
0: Generic dialup adapter
1: Broadcom 440x 10/100 Integrated Controller (Microsoft's Packet Scheduler)
2: Instant Wireless Network PC Card V2.5 (Microsoft's Packet Scheduler)
If you are working with Linux, the output would be a
lot more cryptic, so you would want to use the following code (note that
you change the devices[i].description to devices[i].name):
NetworkInterface[] devices = net.getNetowrkInterfaces();
for (int i=0; i<devices.length; i++)
System.out.println(i + ": " + devices[i].description);
The corresponding output should look something like this:
0: eth0
1: any
2: lo
The number before each device name/description is the
location of the device in the array. I will call this number the NetwordInterfaceID for the remainder of this article, and
it will be utilized to select which interface to use in the upcoming
methods.
Obtaining MAC and IP Addresses
To handcraft a TCP packet to send to remote computers,
you must first know your IP and MAC addresses. These will be used for the
source (sender) information of the TCP/IP packets so that the remote
computer will know who sent the packet and where to send the response.
An IP address is the network identifier of a computer
or device on a TCP/IP network (e.g., the Internet is a TCP/IP network). An
IP address is a 32-bit numeric address written as four numbers separated by
periods. Each number can range from 0 to 255 and is usually in decimal
format (e.g., 69.l87.32.220 is an IP address).
A MAC (Media Access Control) address is a unique
identifier attached to most networking
equipment. The Mac address is a 48-bit numeric
address written as six numbers separated by colons. Each number can range
from 0 to 255 and is usually in hexadecimal format (e.g., 00:03:25:dc:2a:53
is a MAC address).
To find your IP and MAC addresses manually, you can
run ifconfig on a
Unix/Linux machine or ipconfig /all on a Windows machine. Listings 2 and 3 return the MAC
and IP addresses of the current computer given a NetworkInterfaceID number.
Keep in mind that the NetworkInterfaceID is the number of the network
interface you wish to use to send the packet.
The myMacAddress and myIPAddress methods both accept
one argument, which is the networkInterfaceID of the network device from
which you want to obtain the information. Both methods will then verify
that a valid networkInterfaceID has been received, and, if not, will return
a null to the method that called it. The next steps are to set the device
(which is a NetworkInterface object) to the NetworkInterface
specified by the networkInterfaceID, retrieve the address information
required from the device, and return the information
in an array of bytes.
Once you've obtained your MAC and IP addresses,
you need the MAC and IP addresses of the remote computer you wish to check.
If you only know the FQN (fully qualified name
(e.g., www.yahoo.com is a FQN) of the remote computer, you can get the IP
address by running nslookup FQN (where FQN is the fully qualified name of the remote
computer) at the terminal/command prompt.
It's a bit more difficult to obtain the MAC
address of the remote computer than to obtain the IP address. It is
possible to obtain the MAC address from the terminal/command prompt if you
have communicated with it recently, but we will write a function to obtain
it given the IP address, so that you only need to know the IP address to do
the heartbeat function.
Listing 4 contains the method to get the MAC address
of the remote machine. If you are familiar with TCP/IP networking, you will
know that ARP stands for "Address Request Protocol". ARP is an
actual protocol that is designed to get the MAC address of remote machine.
I called the method arp (lowercase) because it's purpose is the same
as the ARP protocol. For the remainder of the article, ARP in caps will
stand for the protocol, and arp in lower case will stand for our method.
The ARP protocol is a very basic protocol. It sends
out a broadcast that asks for the MAC address of the computer with the
specified IP address. The remote computer, if it is listening, should
respond with the MAC address. You might be thinking that if the computer
responds with its MAC address, it should be alive and listening and that is
all we need for a heartbeat application. This would be correct except for
the fact that ARP requests can be cached, so we cannot rely on this for the
heartbeat.
The arp method accepts three arguments: the Ipaddress
of the remote computer (ip), the Ipaddress of the local computer (srcip),
and the networkInterfaceID of the network card to use (networkInterfaceID).
The first part of the arp method should look familiar.
We begin by verifying that the NetworkInterfaceID is valid, then we set the
device (which is a NetworkInterface object) to the NetworkInterface
specified by the networkInterfaceID, and obtain the MAC address using the
myMacAddress method. Then it starts to get a little more complicated.
The JpcapCaptor.openDevice method initializes the
network interface (defined by the NetworkInterface object device), returns
an instance of the JpcapCaptor class, and assigns it to captor. The
openDevice method accepts four arguments: the NetworkInterface object to
use, Max number of BYTES to capture at once, set the interface to
promiscuous mode, and the time out.
We then assign a filter to the captor object with the
setFilter method. The setFilter method accepts filters in the TCPDump
format. For information on this format, refer to:
http://www.tcpdump.org/tcpdump_man.html
The second argument in the setFilter method is a
Boolean one, if you want the filter optimized.
The ARP and Ethernet packets have a number of fields
to set, and the code is commented to tell you what each field is. Once the
packet is crafted, you can send it out with the sender.sendPacket method.
It accepts a packet object as its one argument. The method finishes by
capturing any packets that fit the filter defined in the setFilter method.
If the capture times out, it will throw an IllegalArgumentException saying
the remote computer did not respond. If the remote computer does respond to
the ARP request, it returns the MAC address of the remote computer.
You now have all the information needed to
communicate with a remote computer over a TCP/IP network. In the next
method, you will craft the TCP packet and send it to the remote machine.
The ACKheartbeat Method
Listing 5 contains the ACKheartBeat method. It will
return a true, if the remote machine responded with the RST packet, or
false, if it did not get a response. The ACKheartBeat method accepts four
arguments: the NetworkInterfaceID of the network device to use, the IP
address of the remote machine, the tcp port from which to send the packet,
and the tcp port to which to send the packet.
Most of the ACKheartBeat method should look familiar
to you from the arp method. The main difference is that you are crafting a
TCP packet rather than an ARP packet. The following lines define the
settings for the TCP packet and the IP parameters:
TCPPacket p=new TCPPacket(srcPort,dstPort,60,60,false,true, \
false,false,false,false,false,false,65535,0);
p.setIPv4Parameter(0,false,false,false,0, \
false,false,false,0,0,200,IPPacket. \
IPPROTO_TCP,InetAddress. \
getByAddress(myIPAdd),InetAddress. \
getByAddress(recAddr));
The constructor for the TCPPacket accepts 14
parameters. From left to right they are:
srcPort - The port number to send the packet from on the local computer
dstPort - The port number to send the packet to on the remote computer
60 - sequence number
60 - ACK number
false - boolean URG flag
true - boolean ACK flag
false - boolean PSH flag
false - boolean RST flag
false - boolean SYN flag
false - boolean FIN flag
false - boolean RSV1
false - boolean RSV2
65535- window size
0 - urgent pointer
You can try setting different flags to see which type
of response you receive from the remote machine. You only want the ACK flag
set for the heartbeat method, so you can get a reset (packet with the RST
flag set) response from the remote machine. Keep in mind that if you want
to establish a communication link with another machine, you must start by
sending a packet with the SYN flag set, then wait for the remote machine to
return a packet with the SYN and ACK flags set, and finally you must send a
packet with the ACK flag set. There is more involved to this, but, as I
mentioned earlier, those details are well beyond the scope of this article.
After the TCP/IP and Ethernet packets are crafted,
you can send the packet out just like you did in the arp method and begin
listening for a response. In the arp method, you set the filter of the
captor method to "arp", which meant that you only wanted to
captor arp responses. In the ACKheartBeat method, the filter is set to
"tcp dst port " + srcPort, which states that you only want to
capture tcp packets destined to the port from which you sent the TCP
packet. The filter is set this way because the remote computer will respond
to the port that originally sent the packet from.
Using the Heartbeat Function
You would use the ACKheartBeat like this:
networking net = new networking();
boolean alive = net.ACKheartBeat(1, "192.168.10.55",4421,2110);
This would send a packet with the ACK flag set from
the 4421 TCP port of the local machine to the 2110 TCP port of the computer
located at the IP address of 192.168.10.55. If the computer responds,
"alive" is set to true; if it does not respond,
"alive" is set to false.
You can use the results of the heartbeat application
to trigger other processes. For example, if the heartbeat comes back false,
you can send an email alert to the systems administrator. You could also
put the results into a database and have another application display the
results for a network monitor. You could even have the ACKheartbeat go out
to the Internet and, if the results are false, you know that your
connection to the Internet is down and have the computer switch to a backup
connection.
You can now use the jpcap to expand the networking
class that we created in this article. Rather then sending an ACK packet,
try sending a packet with only the SYN flag set. If the remote computer
responds with a packet with both the SYN and ACK flags set, there is an
application listening on that port. If the remote computer responds with
the RST flag set, there is no application listening on that port but it is
not blocked by a firewall. If there is no response, then the remote
computer is either off the network or that port is being filtered by a
firewall. This procedure can be used as a port scanner.
Conclusion
Jpcap can be used to write many different systems
administrations utilities or network security tools that used to be off
limits to Java developers because of the lack of low-level network support
in Java. The Windows and Linux/Unix versions of jpcap can both be used to
write Networking tools, but I have noticed that that the
jpcap.NetworkInterface class for Windows does not always return the right
information. In the networking class that I wrote for this article, I put
in the following code:
if (macString.equalsIgnoreCase("0:0:0:0:0:0:"))
returnByte = myMacAddress(0);
0:0:0:0:0:0 is not a valid MAC address, but it does
show up in the Windows version of jpcap. When it does, the correct MAC
address is in the networkInterface with a networkInterfaceID of 0.
The jpcap Web site states that the latest version of
jpcap requires libpcap 0.9.4 (which is also the latest release) or later,
but it does not specify which version of winpcap is required. I believe the problem involves the latest version of winpcap
not being 100% compatible with latest version of libpcap. My recommendation is to use the Windows version of jpcap only for
development and the Linux/Unix version for production.
As a Systems Operation Manager, Jon works in the
Network/Systems Administration, Network Security, and Development fields.
Jon enjoys projects that combine all these areas, and that is where he got
the idea to start writing administration and security tools in Java. Jon
can be reached at: hoffman.jon@gmail.com.
|