Packet Purgatory Tutorial
Part 2: Using a Proxy IP to Capture Packets
Return to Part 1
The program we wrote in the first part of this tutorial used
the default settings for everything. In a lot of cases, the defaults are
good enough, but sometimes they're not. This section of the tutorial
will discuss when the defaults won't work for you, and what you
need to do in order to change them.
By default, Packet Purgatory does some deep surgery in order to do what
it does. It changes the route tables to point out the loopback, modifies
firewall rules to block inbound traffic, and puts the interface into
promiscuous mode. This can break in a number of situations:
- No built-in firewall exists on the system
- The built-in firewall is not supported by libdnet (IPTables)
- The loopback interface is not a full interface (Solaris)
If any of the above are true, then your sample program will not
run successfully. All is not yet lost, however. Packet Purgatory
supports a second type of packet capture/redirection which is a
little more complex in terms of programming, but should be portable
across all systems. This type is called a proxy IP, or fake IP capture.
Using proxy IP capture, an unused IP address on the local subnet
is used to proxy communication. Packet Purgatory will handle ARPing
for that IP address, so packets destined to it will be properly
received by the network card. In order to enable proxy IP capture,
a few changes must be made to the sample program.
The first that we must do is to modify the type of packet capture that
Packet Purgatory will run. We can do this with the packetp_set_type()
function:
int packetp_set_type(struct packetp_ctx *ctx, int type);
The context is the same context we've been using before, the type is one of
PP_FAKEIP, or PP_LOOPFW, both defined in packetp.h. By default,
PP_LOOPFW is used. In the future, there may be additional types
added to Packet Purgatory. For right now, add the following line
above packetp_start():
packetp_set_type(pp_ctx, PP_FAKEIP);
In addition to setting changing the type of capture method Packet
Purgatory will use, we need to provide some additional information,
adding it into the context. We must add in the proxy IP address, which
must be an address on the local subnet, using the packetp_set_proxy()
function. We must also set the remote IP address that we are
proxying through the proxy IP address, using the packetp_set_target()
function. packetp_set_proxy() is declared as follows:
int packetp_set_proxy(struct packetp_ctx *ctx, struct addr *proxy);
The variable ctx is the context, as always, and the variable proxy is
a pointer to the addr structure containing the address of the
proxy IP. (See the dnet(3) manpage for additional information regarding
struct addr, it's a libdnet structure). You can use addr_aton() from
dnet to fill in the struct addr
packetp_set_target() has the following form:
int packetp_set_target(struct packetp_ctx *ctx, struct addr *target);
You know ctx by now, and the variable target contains the remote IP
address that packets sent to the proxy will be relayed to after modification.
Once again, use addr_aton() to fill in the struct addr.
Both packetp_set_proxy() and packetp_set_target() need to be called before
packetp_start(), and after packetp_init(), but other than that the order
is unimportant. Let's see how this looks in actual code:
int
main(int argc, char **argv)
{
int c;
struct addr temp_addr;
struct packetp_ctx *pp_ctx
/* Initialize the Packet Purgatory context */
pp_ctx = packetp_init();
/* Retreive arguments passed in, and add the required info to the context */
while((c=getopt(argc, argv, "p:r:")) != -1) {
switch(c) {
case 'p':
addr_aton(optarg, &temp_addr);
packetp_set_proxy(pp_ctx, &temp_addr);
break;
case 'r':
addr_aton(optarg, &temp_addr);
packetp_set_target(pp_ctx, &temp_addr);
break;
}
}
packetp_set_type(pp_ctx, PP_FAKEIP);
packetp_start(pp_ctx, outbound, NULL, NULL);
return(0);
}
The outbound function can remain the same. The full code is available
here. Compile with the following:
% gcc -o pp_tut2 pp_tut2.c -lpacketp -ldnet -lpcap -pthread
Let's say this program is run with the following command line:
# pp_tut2 -p 10.0.0.3 -r 192.168.0.2
(This assumes that you are in the 10.0.0.0 subnet)
At this point, any packets sent from your system to 10.0.0.3 will
be rewritten, both to change the source address to 10.0.0.3, and the
destination address to 192.168.0.2, and the TOS flag to the
PREC_FLASHOVERRIDE value. Any inbound packets from 192.168.0.2 to
10.0.0.3 will be rewritten as sourced from 10.0.0.3, with a destination
IP address of your machine.
This mode is more restrictive, in that it can only easily carry on
a conversation with one remote host at a time, but it is far more
portable. For most applications, this is sufficient.
So, we now know how to set up two different types of packet-rewriting
schemes for Packet Purgatory. In the future, there may be additional
types, (I've got my eye on the IP_MANGLE table in IPTables as
a logical next step), but the inbound and outbound functions will
maintain the same calling conventions, and all will be backward
compatible.
Next up, advanced mode! (What? There's an advanced mode? Why would
you want such a thing?)
Return to Part 1
Part 3: To be written...