From: Florian Frank Date: 2004-03-17T15:03:33+09:00 Subject: Re: Manipulating UDP sender's address On 2004-03-17 04:14:18 +0900, Gennady wrote: > Is there a way, in general, to "fake" a sender's IP address when > sending a UDP packet? I need to receive a UDP packet on one port and > redirect it to another "pretending" that it is from the original > sender. > > Even if it is not possible from Ruby now, I would appreciate any > pointers so that I can make an extension to do it (if at all possible). Spoofing seems to work pretty good in Ruby (see below). You would have to recalculate the ip header checksum (and probably also the udp checksum), if you want to change the source address of a given packet . BTW, the ipchecksum algorithm is really beautiful in Ruby: def ipchecksum(data) checksum = data.unpack("n*").inject(0) { |s, x| s + x } ((checksum >> 16) + (checksum & 0xffff)) ^ 0xffff end Here comes my little hack: require 'socket' class UDPSpoofer @@id = 1234 - 1 def initialize @s = Socket.new(Socket::PF_INET, Socket::SOCK_RAW, Socket::IPPROTO_RAW) @s.setsockopt(Socket::IPPROTO_IP, Socket::IP_HDRINCL, 1) end def send(src, sport, dst, dport, payload = '') dst_numeric = dst.split(/\./).map { |x| x.to_i } to = [ Socket::AF_INET, dport, dst_numeric, '' ].flatten.pack('snCCCCa8') packet = make_ip_header([ [ '01000101', 'B8' ], # version, hlen [ 0, 'C' ], # tos [ 28 + payload.size, 'n' ], # total len [ @@id += 1, 'n' ], # id [ 0, 'n' ], # flags, offset [ 64, 'C' ], # ttl [ 17, 'C' ], # protocol [ 0, 'n' ], # checksum [ ip2long(src), 'N' ], # source [ ip2long(dst), 'N' ], # destination ]) packet << make_udp_header([ [ sport, 'n'], # source port [ dport, 'n' ], # destination port [ 8 + payload.size, 'n' ], # len [ 0, 'n' ] # checksum (mandatory) ]) packet << payload @s.send(packet, 0, to) end private def ip2long(ip) long = 0 ip.split(/\./).reverse.each_with_index do |x, i| long += x.to_i << (i * 8) end long end def ipchecksum(data) checksum = data.unpack("n*").inject(0) { |s, x| s + x } ((checksum >> 16) + (checksum & 0xffff)) ^ 0xffff end def make_ip_header(parts) template = '' data = [] parts.each do |part| data += part[0..-2] template << part[-1] end data_str = data.pack(template) checksum = ipchecksum(data_str) data[-3] = checksum data.pack(template) end def make_udp_header(parts) template = '' data = [] parts.each do |part| data += part[0..-2] template << part[-1] end data.pack(template) end def ip2long(ip) long = 0 ip.split(/\./).reverse.each_with_index do |x, i| long += x.to_i << (i * 8) end long end def ipchecksum(data) checksum = data.unpack("n*").inject(0) { |s, x| s + x } ((checksum >> 16) + (checksum & 0xffff)) ^ 0xffff end def make_ip_header(parts) template = '' data = [] parts.each do |part| data += part[0..-2] template << part[-1] end data_str = data.pack(template) checksum = ipchecksum(data_str) data[-3] = checksum data.pack(template) end def make_udp_header(parts) template = '' data = [] parts.each do |part| data += part[0..-2] template << part[-1] end data.pack(template) end end us = UDPSpoofer.new loop do # fire! us.send('192.168.1.66', 666, '192.168.1.1', 42, 'i am on the ether') end -- lambda { |c| lambda { |f| f[f] } [ lambda { |f| c[lambda { |x| f[f][x] } ] }] }