2

I'm presently trying to write something in Ruby which will send data over a raw socket. It may be in part due to a somewhat shaky understanding of sockets, but I feel as though the resources are almost-but-not-quite there.

In particular, I'm trying to convert the following Python code (in case it helps):

#!/usr/bin/env python
from socket import socket, AF_PACKET, SOCK_RAW
s = socket(AF_PACKET, SOCK_RAW)
s.bind(("lo", 0))

geonet_frame = "\x00\x1f\xc6\x51\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\xc6
\x51\x07\x07\x07\x07\x07\x07\xef\x06\x07\x35\x97\x00\x24\x8c\x7a\xdf\x6f\x08
\x00\x45\x00\x00\x3d\xf3\x7f\x40\x00\x40\x11\x30\xc6\x0a\x01\x01\x68\x0a\x01
\x01\x01\x99\x80\x00\x35\x00\x29\x16\xa5\x01\x76\x01\x00\x00\xff\x00\x00\x01
\x00\x00\x00"

s.send(geonet_frame)

In searching the matter, the results I see most often are this Stack Overflow question, which does not directly provide any workable code examples, and this explanation which seems to do a lot more than I need and also seems to include a file which I cannot access.

I have tried a few things listed in the documentation for the Socket class, but I can never quite seem to achieve the same results even if I can make it work. For example, I might try:

soc = Socket.open(Socket::PF_INET, Socket::SOCK_RAW, Socket::IPPROTO_RAW)
soc.send(mypacket, 0, Socket.pack_sockaddr_in(0, "127.0.0.1"))

...and while it will complete, it does not produce the desired result (in this case, it does not create a DOS on TCPDump as denoted here, while the equivalent Python code would).

Is this more complicated in Ruby than I'm expecting? Or am I just missing some magical combination of functions which would allow me to do this?

Update: Here is a picture of a capture of the desired packet.

https://i.stack.imgur.com/1e4f5.png

Community
  • 1
  • 1
Rstevoa
  • 271
  • 4
  • 17

2 Answers2

3

This will do it:

require 'socket'

interface = 'lo'         # loopback interface
interface_index = 0x8933 # SIOCGIFINDEX
geonet_frame = "\x00\x1f\xc6\x51\x07\x07\x07\x07\x07\x07\x07\x07\x07\x07\xc6\x51\x07\x07\x07\x07\x07\x07\xef\x06\x07\x35\x97\x00\x24\x8c\x7a\xdf\x6f\x08\x00\x45\x00\x00\x3d\xf3\x7f\x40\x00\x40\x11\x30\xc6\x0a\x01\x01\x68\x0a\x01\x01\x01\x99\x80\x00\x35\x00\x29\x16\xa5\x01\x76\x01\x00\x00\xff\x00\x00\x01\x00\x00\x00"

socket = Socket.new(Socket::AF_PACKET, Socket::SOCK_RAW, Socket::IPPROTO_RAW)
ifreq = [interface.dup].pack 'a32'
socket.ioctl(interface_index, ifreq)
socket.bind([Socket::AF_PACKET].pack('s') + [Socket::IPPROTO_RAW].pack('n') + ifreq[16..20]+ ("\x00" * 12))

socket.send(geonet_frame, 0)

I used AF_PACKET because it matched the python example, but you can replace it with PF_PACKET if your system doesn't support it.

Replacing the geonet_frame variable as Cocoabean suggested also works:

geonet_frame = [ 0x00, 0x1f, 0xc6, 0x51, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0xc6, 0x51, 0x07, 0x07, 0x07, 0x07,0x07, 0x07, 0xef, 0x06, 0x07, 0x35, 0x97, 0x00, 0x24, 0x8c, 0x7a, 0xdf, 0x6f, 0x08, 0x00, 0x45, 0x00, 0x00, 0x3d, 0xf3, 0x7f, 0x40, 0x00, 0x40, 0x11, 0x30, 0xc6, 0x0a, 0x01, 0x01, 0x68, 0x0a, 0x01, 0x01, 0x01, 0x99, 0x80, 0x00, 0x35, 0x00, 0x29, 0x16, 0xa5, 0x01, 0x76, 0x01, 0x00, 0x00, 0xff, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00].pack("C*")

Tested with ruby 1.9.3p484 on CentOS 6.6. Hope it helps.

Cheers.

rebelshrug
  • 682
  • 7
  • 10
  • It worked! Thank you so much! Optionally, if you get the chance, I'd like to ask for you to elaborate on what was incorrect and why these changes produced the correct result. – Rstevoa Dec 05 '14 at 03:36
0

I think you got it right as far as creating the socket, you just are sending the data in the wrong format. Python sees a series of bytes whereas ruby sees a string of characters. The pack method will create a String from the array of bytes.

http://www.ruby-doc.org/core-2.1.5/Array.html#method-i-pack

Try using this as the value for mypacket in your example attempt:

    mypacket = [ 0x00, 0x1f, 0xc6, 0x51, 0x07, 0x07, 0x07, 
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0xc6, 0x51, 0x07, 
0x07, 0x07, 0x07, 0x07, 0x07, 0xef, 0x06, 0x07, 0x35, 0x97, 
0x00, 0x24, 0x8c, 0x7a, 0xdf, 0x6f, 0x08, 0x00, 0x45, 0x00, 
0x00, 0x3d, 0xf3, 0x7f, 0x40, 0x00, 0x40, 0x11, 0x30, 0xc6, 
0x0a, 0x01, 0x01, 0x68, 0x0a, 0x01, 0x01, 0x01, 0x99, 0x80, 
0x00, 0x35, 0x00, 0x29, 0x16, 0xa5, 0x01, 0x76, 0x01, 0x00, 
0x00, 0xff, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00].pack("C*")

simplest way to send raw Byte-arrays using Ruby's TCPSocket-Class

Community
  • 1
  • 1
Michael
  • 101
  • 2
  • This unfortunately doesn't produce the correct packet. I will update the question with a picture of a capture of the correct packet. For comparison, here is the result of the one my / your code produces: http://i.imgur.com/76K8bWs.png?1 – Rstevoa Dec 01 '14 at 16:42
  • I think posting the correct packet would be a huge help. – Michael Dec 01 '14 at 19:27
  • The capture of the packet is on the main question in addition to the comment above. – Rstevoa Dec 01 '14 at 21:42