= Libnet4r Libnet4r is a Ruby extension that provides bindings to the excellent Libnet[http://www.packetfactory.net/libnet] packet construction and injection library. It is however more than just a straight port of the Libnet API to Ruby methods. In addition to the packet builder and injection functions that Libnet provides, you also get object classes for each header type that contain decode methods for decoding packed byte strings. Also, the header builder methods have been written in a much more Ruby like manner using objects and blocks as apposed to the long parameter lists that the Libnet C functions use. = Installing Libnet4r Get Libnet4r from RubyForge. $ gem install libnet4r = Example require 'libnet4r' # create a new Libnet object for injecting at the link layer using device en0 l = Libnet.new(:link, 'en0') # create a random 200 byte string to use as our UDP datagram payload srand Time.now.to_i bytes = Array.new(200) { |i| rand(255) } payload = bytes.pack("C*") # build the UDP header udp = l.build_udp do |udp| udp.src_port = 0xdead udp.dst_port = 0xbeef udp.length = Libnet::HL_UDP + payload.length udp.checksum = 0 # set to 0 to instruct Libnet to calculate this for us udp.payload = payload end # build the IPv4 header ip = l.build_ipv4 do |ip| ip.tos = 0 ip.length = Libnet::HL_IPV4 + Libnet::HL_UDP + payload.length ip.id = 0x4321 ip.frag_off = 0 ip.ttl = 72 ip.protocol = Libnet::IPPROTO_UDP ip.checksum = 0 # set to 0 to instruct Libnet to calculate this for us ip.src_ip = "192.168.1.101" # IP addresses can be assigned as a dotted-decimal ip.dst_ip = 0xc0a80103 # string or a network byte ordered integer end # finally, build the ethernet header src_mac = [0x11, 0x22, 0x33, 0x44, 0x55, 0x66].pack("C*") eth = l.build_ethernet do |eth| eth.dst = "aa:bb:cc:dd:ee:ff" # MAC addresses can be assigned as a colon- eth.src = src_mac # separated hex string or a byte string eth.type = 0x0800 end # display a hex dump of the resulting packet packet = l.pack puts "packet (#{packet.length} bytes):\n#{Libnet.hexdump(packet)}" # write the packet to the wire n = l.write puts "wrote #{n} bytes" # decode the packet back into header objects eth = Libnet::Ethernet.decode(packet) ip = Libnet::IPv4.decode(packet[Libnet::HL_ETH .. -1]) udp = Libnet::UDP.decode(packet[Libnet::HL_ETH + Libnet::HL_IPV4 .. -1]) puts "eth.dst: #{Libnet.hexdump(eth.dst)}" puts "eth.src: #{Libnet.hexdump(eth.src)}" puts "eth.type: 0x%04x" % [ eth.type ] puts "ip.version: #{ip.version}" puts "ip.ihl: #{ip.ihl}" puts "ip.tos: #{ip.tos}" puts "ip.length: #{ip.length}" puts "ip.id: 0x%04x" % [ ip.id ] puts "ip.frag_off: #{ip.frag_off}" puts "ip.ttl: #{ip.ttl}" puts "ip.protocol: #{ip.protocol}" puts "ip.checksum: 0x%04x" % [ ip.checksum ] puts "ip.src_ip: #{Libnet.ipv4_ntoa(ip.src_ip)}" puts "ip.dst_ip: #{Libnet.ipv4_ntoa(ip.dst_ip)}" puts "udp.src_port: 0x%04x" % [ udp.src_port ] puts "udp.dst_port: 0x%04x" % [ udp.dst_port ] puts "udp.checksum: 0x%04x" % [ udp.checksum ] puts "udp.payload:\n#{Libnet.hexdump(udp.payload)}" The above script results in the following output: packet (242 bytes): 0x00000000: aabbccdd eeff1122 33445566 08004500 0x00000010: 00e44321 00004811 ab2fc0a8 0165c0a8 0x00000020: 0103dead beef00d0 c2ad57a2 7e436761 0x00000030: a7f58e9b 277d4218 9f5e918e dd8f2379 0x00000040: 18047ac6 245b2e25 19e89c01 69900551 0x00000050: c195a6e1 fd2eb200 0c9baed0 c50e220e 0x00000060: 9fe7c7c8 6d59cd01 3bdfef59 2d0b4768 0x00000070: 776b33a8 452adb2c 4fae4584 fcc9892f 0x00000080: 7fa57498 a6ab1f00 66590c6e 5145e37f 0x00000090: 330875dc 38b48709 a92ab231 212f411d 0x000000a0: 23cb0ed2 3fc18dee dc0fa417 a897557b 0x000000b0: 7ad9fad2 2e334bec 6d62bc06 2d1dfa1c 0x000000c0: 896c6182 d91570c5 11b69dc0 9472c32c 0x000000d0: 3ab32aa7 3f5e9771 3db6db39 1a5c34d5 0x000000e0: dfa308be 5716b343 3bfd0428 e4da4aac 0x000000f0: 9504 wrote 242 bytes eth.dst: 0x00000000: aabbccdd eeff eth.src: 0x00000000: 11223344 5566 eth.type: 0x0800 ip.version: 4 ip.ihl: 5 ip.tos: 0 ip.length: 228 ip.id: 0x4321 ip.frag_off: 0 ip.ttl: 72 ip.protocol: 17 ip.checksum: 0x002f ip.src_ip: 192.168.1.101 ip.dst_ip: 192.168.1.3 udp.src_port: 0xdead udp.dst_port: 0xbeef udp.checksum: 0xc2ad udp.payload: 0x00000000: 57a27e43 6761a7f5 8e9b277d 42189f5e 0x00000010: 918edd8f 23791804 7ac6245b 2e2519e8 0x00000020: 9c016990 0551c195 a6e1fd2e b2000c9b 0x00000030: aed0c50e 220e9fe7 c7c86d59 cd013bdf 0x00000040: ef592d0b 4768776b 33a8452a db2c4fae 0x00000050: 4584fcc9 892f7fa5 7498a6ab 1f006659 0x00000060: 0c6e5145 e37f3308 75dc38b4 8709a92a 0x00000070: b231212f 411d23cb 0ed23fc1 8deedc0f 0x00000080: a417a897 557b7ad9 fad22e33 4bec6d62 0x00000090: bc062d1d fa1c896c 6182d915 70c511b6 0x000000a0: 9dc09472 c32c3ab3 2aa73f5e 97713db6 0x000000b0: db391a5c 34d5dfa3 08be5716 b3433bfd 0x000000c0: 0428e4da 4aac9504 = Supported Header Types The Libnet C library provides builder functions for many different protocol headers. Libnet4r is still very much a work in progress and only supports a subset of the header types that Libnet does. Patches for additional header types are welcome! Here is the list of currently supported header types: * Ethernet * VLAN (802.1q) * ARP * IPv4 * IPv6 * UDP = Notes The Libnet C source code comes packaged with this library and is built along with the extension so it is not necessary for you to already have Libnet installed on your system. If you do have Libnet already installed, it will *not* be used, the Libnet that comes with this package will still be used as it contains several patches that the extension depends on. The following changes have been made to the Libnet 1.1.2.1 source code in this package: * Fixed bug that incorrectly packed the traffic class and flow label fields of the IPv6 header. * Fixed checksum bug when IPv6 header is present. * Removed check for root user from +libnet_init()+. This check is now only done in the +libnet_write*+ functions. This allows all of the header builder functions to be used by a normal user. Its only necesssary to be the root user if you wish to actually inject the packets that you construct. = Project Page http://rubyforge.org/projects/libnet4r = Author Corey Burrows (mailto:corey.burrows@gmail.com)