module PacketFu # The Capture class is used to construct PcapRub objects in order to collect # packets from an interface. # # This class requires PcapRub. In addition, you will need root (or root-like) privileges # in order to capture from the interface. # # Note, on some wireless cards, setting :promisc => true will disable capturing. # # == Example # # # Typical use # cap = PacketFu::Capture.new(:iface => 'eth0', :promisc => true) # cap.start # sleep 10 # cap.save # first_packet = cap.array[0] # # # Tcpdump-like use # cap = PacketFu::Capture.new(:start => true) # cap.show_live(:save => true, :filter => 'tcp and not port 22') # # == See Also # # Read, Write class Capture attr_accessor :array, :stream # Leave these public and open. attr_reader :iface, :snaplen, :promisc, :timeout # Cant change after the init. def initialize(args={}) @array = [] # Where the packet array goes. @stream = [] # Where the stream goes. @iface = args[:iface] || ENV['IFACE'] || Pcap.lookupdev || "lo" @snaplen = args[:snaplen] || 0xffff @promisc = args[:promisc] || false # Sensible for some Intel wifi cards @timeout = args[:timeout] || 1 setup_params(args) end # Used by new(). def setup_params(args={}) filter = args[:filter] # Not global; filter criteria can change. start = args[:start] || false capture if start bpf(:filter=>filter) if filter end # capture() initializes the @stream varaible. Valid arguments are: # # :filter # Provide a bpf filter to enable for the capture. For example, 'ip and not tcp' # :start # When true, start capturing packets to the @stream variable. Defaults to true def capture(args={}) if Process.euid.zero? filter = args[:filter] start = args[:start] || true if start begin @stream = Pcap.open_live(@iface,@snaplen,@promisc,@timeout) rescue RuntimeError $stderr.print "Are you sure you're root? Error: " raise end bpf(:filter=>filter) if filter else @stream = [] end @stream else raise RuntimeError,"Not root, so can't capture packets. Error: " end end # start() is equivalent to capture(). def start(args={}) capture(args) end # clear() clears the @stream and @array variables, essentially starting the # capture session over. Valid arguments are: # # :array # If true, the @array is cleared. # :stream # If true, the @stream is cleared. def clear(args={}) array = args[:array] || true stream = args[:stream] || true @array = [] if array @stream = [] if stream end # bpf() sets a bpf filter on a capture session. Valid arugments are: # # :filter # Provide a bpf filter to enable for the capture. For example, 'ip and not tcp' def bpf(args={}) filter = args[:filter] capture if @stream.class == Array @stream.setfilter(filter) end # wire_to_array() saves a packet stream as an array of binary strings. From here, # packets may accessed by other functions. Note that the wire_to_array empties # the stream, so multiple calls will append new packets to @array. # Valid arguments are: # # :filter # Provide a bpf filter to apply to packets moving from @stream to @array. def wire_to_array(args={}) filter = args[:filter] bpf(:filter=>filter) if filter while this_pkt = @stream.next @array << this_pkt end @array.size end # next() exposes the Stream object's next method to the outside world. def next return @stream.next end # w2a() is a equivalent to wire_to_array() def w2a(args={}) wire_to_array(args) end # save() is a equivalent to wire_to_array() def save(args={}) wire_to_array(args) end # show_live() is a method to capture packets and display peek() data to stdout. Valid arguments are: # # :filter # Provide a bpf filter to captured packets. # :save # Save the capture in @array # :verbose # TODO: Not implemented yet; do more than just peek() at the packets. # :quiet # TODO: Not implemented yet; do less than peek() at the packets. def show_live(args={}) filter = args[:filter] save = args[:save] verbose = args[:verbose] || args[:v] || false quiet = args[:quiet] || args[:q] || false # Setting q and v doesn't make a lot of sense but hey. # Ensure the capture's started. if @stream.class == Array capture end @stream.setfilter(filter) if filter while true @stream.each do |pkt| puts Packet.parse(pkt).peek @array << pkt if args[:save] end end end end end