Forum: Ruby Send over raw socket?

Ce263187f309d71b404399802b7f7906?d=identicon&s=25 Ben Nagy (Guest)
on 2006-12-05 08:12
(Received via mailing list)
Hi,

This should be simple, but it doesn't seem to be documented anywhere I
can find...

I want to send data over a raw IP socket - not TCP or UDP. Ideally I'd
like the OS to provide it's IP header, but I can do that myself if I
have to. Normally you would open a socket as PF_INET, SOCK_RAW,
(protocol number), then use sendto, but sendto doesn't seem to exist in
ruby, and the Socket#send instance method is not documented anywhere, so
I don't know what arguments it needs.

Has anyone done this before, for any protocol? Oh, and to save time, I
don't want to use libnet or rubyforger or *random-lib-someone-wrote.

Thanks!

ben
47b1910084592eb77a032bc7d8d1a84e?d=identicon&s=25 Joel VanderWerf (Guest)
on 2006-12-05 08:40
(Received via mailing list)
Ben Nagy wrote:
> I don't know what arguments it needs.
>
> Has anyone done this before, for any protocol? Oh, and to save time, I
> don't want to use libnet or rubyforger or *random-lib-someone-wrote.

Hi, Ben,

Just use #send. It's documented in the BasicSocket part of the appendix
in the PickAxe.

I think it's platform dependent how much of the IP header gets filled in
for you (for example, the checksum is calculated by linux, but not IIRC
by QNX). On linux, man 7 raw will tell you:

        +---------------------------------------------------+
        |IP Header fields modified on sending by IP_HDRINCL |
        +----------------------+----------------------------+
        |IP Checksum           |Always filled in.           |
        +----------------------+----------------------------+
        |Source Address        |Filled in when zero.        |
        +----------------------+----------------------------+
        |Packet Id             |Filled in when zero.        |
        +----------------------+----------------------------+
        |Total Length          |Always filled in.           |
        +----------------------+----------------------------+

Here's an example from the bit-struct examples dir, but BitStruct is
only used to construct the packet, not open the socket or send the
packet. The example has two threads, a sender and a receiver.

$ cat raw.rb
require "socket"
require "./ip"

# A more substantial example of sending and receiving RAW packets.

begin
   rsock = Socket.open(Socket::PF_INET, Socket::SOCK_RAW,
Socket::IPPROTO_RAW)
rescue Errno::EPERM
   $stderr.puts "Must run #{$0} as root."
   exit!
end

begin
   ssock = Socket.open(Socket::PF_INET, Socket::SOCK_RAW,
Socket::IPPROTO_RAW)
   unless ssock.getsockopt(Socket::SOL_IP, Socket::IP_HDRINCL)
     puts "IP_HDRINCL is supposed to be the default for IPPROTO_RAW!"
     puts "setting IP_HDRINCL anyway"
     ssock.setsockopt(Socket::SOL_IP, Socket::IP_HDRINCL, true)
   end
rescue Errno::EPERM
   $stderr.puts "Must run #{$0} as root."
   exit!
end

Thread.new do
   loop do
     data, sender = rsock.recvfrom(8192)
     port, host = Socket.unpack_sockaddr_in(sender)
     out = "-"*80,
           "packet received from #{host}:#{port}:",
           IP.new(data).inspect_detailed,
           "-"*80
     puts out
     $stdout.flush
   end
end

addr = Socket.pack_sockaddr_in(1024, "localhost")
3.times do |i|
   ip = IP.new do |b|
     # ip_v and ip_hl are set for us by IP class
     b.ip_tos  = 0
     b.ip_id   = i+1
     b.ip_off  = 0
     b.ip_ttl  = 64
     b.ip_p    = Socket::IPPROTO_RAW
     b.ip_src  = "127.0.0.1"
     b.ip_dst  = "127.0.0.1"
     b.body    = "just another IP hacker"
     b.ip_len  = b.length
     b.ip_sum  = 0 # linux will calculate this for us (QNX won't?)
   end

   out = "-"*80,
         "packet sent:",
         ip.inspect_detailed,
         "-"*80
   puts out
   $stdout.flush
   ssock.send(ip, 0, addr)
   sleep 1
end

$ su
Password:
# ruby raw.rb
--------------------------------------------------------------------------------
packet sent:
IP:
                        Version = 4
                  Header length = 5
                            TOS = 0
                         Length = 42
                             ID = 1
                    Frag offset = 0
                            TTL = 64
                       Protocol = 255
                       Checksum = 0
                    Source addr = "127.0.0.1"
                      Dest addr = "127.0.0.1"
                Body of message = "just another IP hacker"
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
packet received from 127.0.0.1:0:
IP:
                        Version = 4
                  Header length = 5
                            TOS = 0
                         Length = 42
                             ID = 1
                    Frag offset = 0
                            TTL = 64
                       Protocol = 255
                       Checksum = 31698
                    Source addr = "127.0.0.1"
                      Dest addr = "127.0.0.1"
                Body of message = "just another IP hacker"
Please log in before posting. Registration is free and takes only a minute.
Existing account

NEW: Do you have a Google/GoogleMail, Yahoo or Facebook account? No registration required!
Log in with Google account | Log in with Yahoo account | Log in with Facebook account
No account? Register here.