Send over raw socket?

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

Ben N. 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”