Forum: Ruby Send over raw socket?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
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"
This topic is locked and can not be replied to.