class PacketThief::Impl::PFDivert

Untested and likely broken PacketThief implementation that uses PF’s divert-to to redirect traffic. It is currently untested, and it requires a newer version of PF to redirect traffic than is available in Mac OS X 10.7 (and probably 10.8)

Capturing Traffic

It works by dynamically changing the rules in the “packetthief” anchor. To use it, you must add the following to your /etc/pf.conf file in the

“Packet Filtering” section

anchor “packetthief”

Then you must reload your pf config by rebooting or by executing:

sudo pfctl -f /etc/pf.conf

When PacketThief adds a rule, it constructs a new rule set for the packetthief anchor and replaces the current ruleset in the anchor by calling:

echo "#{our rules}" | pfctl -a packetthief -f -

Rules look something like:

pass in on en1 proto tcp from any to any port 443 divert-to 127.0.0.1 port 54321

Acquiring the original destination

According to [1], if we use a divert-to rule, we can get the original destination using getsockname(2) instead of having to perform ioctl operations on the /dev/pf pseudo-device.

[1]: www.openbsd.org/cgi-bin/man.cgi?query=pf.conf&sektion=5&arch=&apropos=0&manpath=OpenBSD+Current

Alternative implementations

TODO:

divert-to is too new of Mac OS X 10.7 (Lion is using a relatively “old” implementation of PF).

A possible alternative is to use rdr rules:

rdr on en1 proto tcp from any to any port 443 -> 127.0.0.1 port 54321

This also requires:

rdr-anchor "packetthief"

in /etc/pf.conf in the “Translation” section of /etc/pf.conf. We can then use the ioctl with DIOCNATLOOK approach that Squid uses to get a pfioc_natlook data structure to get the original destination of the connection.

Public Class Methods

original_dest(sock) click to toggle source

Returns the [port, host] for the original destination of sock.

Sock can be a Ruby socket or an EventMachine::Connection (including handler modules, which are mixed in to an anonymous descendent of EM::Connection).

When PF uses a divert-to rule to redirect a connection to a local socket, the destination address remains unchanged, meaning that C’s getsockname() will return the original destination. [1]: www.openbsd.org/cgi-bin/man.cgi?query=pf.conf&sektion=5&arch=&apropos=0&manpath=OpenBSD+Current

# File lib/packetthief/impl/pf_divert.rb, line 155
def self.original_dest(sock)
  if sock.respond_to? :getsockname
    sockname = sock.getsockname
  elsif sock.respond_to? :get_sockname
    sockname = sock.get_sockname
  else
    raise ArgumentError, "#{sock.inspect} supports neither :getsockname nor :get_sockname!"
  end
  Socket::unpack_sockaddr_in(sockname)
end
redirect(args={}) click to toggle source
# File lib/packetthief/impl/pf_divert.rb, line 140
def self.redirect(args={})
  rule = PFDivertRule.new(self)
  rule.redirect(args)
end