# # icmp.rb -- ICMPModule # # Copyright (C) 2000 GOTOU YUUZOU # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # $Id: icmp.rb,v 1.2 2001/09/20 17:13:42 gotoyuzo Exp $ require 'socket' module ICMPModule # Protocol IPPROTO_IP = 0 IPPROTO_ICMP = 1 # Type and code field values ICMP_ECHOREPLY = 0 # echo reply ICMP_UNREACH = 3 # dest unreachable, codes: ICMP_UNREACH_NET = 0 # bad net ICMP_UNREACH_HOST = 1 # bad host ICMP_UNREACH_PROTOCOL = 2 # bad protocol ICMP_UNREACH_PORT = 3 # bad port ICMP_UNREACH_NEEDFRAG = 4 # IP_DF caused drop ICMP_UNREACH_SRCFAIL = 5 # src route failed ICMP_UNREACH_NET_UNKNOWN = 6 # unknown net ICMP_UNREACH_HOST_UNKNOWN = 7 # unknown host ICMP_UNREACH_ISOLATED = 8 # src host isolated ICMP_UNREACH_NET_PROHIB = 9 # prohibited access ICMP_UNREACH_HOST_PROHIB = 10 # ditto ICMP_UNREACH_TOSNET = 11 # bad tos for net ICMP_UNREACH_TOSHOST = 12 # bad tos for host ICMP_UNREACH_ADMIN_PROHIBIT = 13 # communication administratively # prohibited ICMP_SOURCEQUENCH = 4 # packet lost, slow down ICMP_REDIRECT = 5 # shorter route, codes: ICMP_REDIRECT_NET = 0 # for network ICMP_REDIRECT_HOST = 1 # for host ICMP_REDIRECT_TOSNET = 2 # for tos and net ICMP_REDIRECT_TOSHOST = 3 # for tos and host ICMP_ECHO = 8 # echo service ICMP_ROUTERADVERT = 9 # router advertisement (RFC1256) ICMP_ROUTERSOLICIT = 10 # router solicitation (RFC1256) ICMP_TIMXCEED = 11 # time exceeded, code: ICMP_TIMXCEED_INTRANS = 0 # ttl==0 in transit ICMP_TIMXCEED_REASS = 1 # ttl==0 in reass ICMP_PARAMPROB = 12 # ip header bad, code: ICMP_PARAMPROB_OPTABSENT = 1 # req. opt. absent ICMP_TSTAMP = 13 # timestamp request ICMP_TSTAMPREPLY = 14 # timestamp reply ICMP_IREQ = 15 # information request ICMP_IREQREPLY = 16 # information reply ICMP_MASKREQ = 17 # address mask request (RFC950) ICMP_MASKREPLY = 18 # address mask reply (RFC950) ICMP_MINLEN = 8 ICMP_TSLEN = 20 ICMP_MASKLEN = 12 ICMP_ADVLENMIN = 36 class IMCPError_rb < StandardError; end class IP_rb < String def self.new(s=nil); s ? super(s) : super("\0" * 20); end def ip_v; (self[0] >> 4) & 0xf end def ip_hl; self[0] & 0xf end def ip_tos; self[1]; end def ip_len; self[2..3].unpack("n")[0]; end def ip_id; self[4..5].unpack("n")[0]; end def ip_off; self[6..7].unpack("n")[0]; end def ip_ttl; self[8]; end def ip_p; self[9]; end def ip_sum; self[10..11].unpack("n")[0]; end def ip_src; "%d.%d.%d.%d" % [self[12], self[13], self[14], self[15]]; end def ip_dst; "%d.%d.%d.%d" % [self[16], self[17], self[18], self[19]]; end def body; self[(ip_hl*4)..-1]; end end class ICMP_rb < String def self.new(s=nil); s ? super(s) : super("\0" * ICMP_ADVLENMIN); end def icmp_type; self[0]; end def icmp_type=v; self[0]=v; end def icmp_code; self[1]; end def icmp_code=v; self[1]=v; end def icmp_cksum; self[2..3].unpack("n")[0]; end def icmp_cksum=v; self[2..3]=[v].pack("n"); end def icmp_id; self[4..5].unpack("n")[0]; end def icmp_id=v; self[4..5]=[v].pack("n"); end def icmp_seq; self[6..7].unpack("n")[0]; end def icmp_seq=v; self[6..7]=[v].pack("n"); end def icmp_data; self[8..-1]; end def icmp_data=v; self[8..-1]=v; end def icmp_gwaddr; "%d.%d.%d.%d" % [self[4], self[5], self[6], self[7]]; end def icmp_ip; IP.new(self[8..-1]); end def icmp_ip=v; self[8..-1]=v; end def truncate olen = self.size case self.icmp_type when ICMP_IREQ, ICMP_IREQREPLY nlen = ICMP_MINLEN when ICMP_UNREACH, ICMP_TIMXCEED, ICMP_PARAMPROB, ICMP_SOURCEQUENCH, ICMP_REDIRECT nlen = ICMP_ADVLENMIN when ICMP_TSTAMP, ICMP_TSTAMPREPLY nlen = ICMP_TSLEN when ICMP_ROUTERADVERT, ICMP_ROUTERSOLICIT nlen = icmp_advlen when ICMP_MASKREQ, ICMP_MASKREPLY nlen = ICMP_MASKLEN when ICMP_ECHO, ICMP_ECHOREPLY nlen = olen else raise ICMPError, "unknown icmp_type %d" % self.icmp_type end self[nlen..-1] = "" end def icmp_advlen 8 + (icmp_ip.ip_hl * 4) + 8 end def set_cksum self.icmp_cksum = 0 sum = 0 self.unpack("n*").each{ |i| sum += i } sum += (self[-1] << 8) if self.size % 2 == 1 sum = (sum & 0xffff) + (sum >> 16) sum += (sum >> 16) self.icmp_cksum = ~sum & 0xffff self end def setup truncate set_cksum end end class ICMPSocket < Socket def ICMPSocket.new super("AF_INET", "SOCK_RAW", IPPROTO_ICMP) end end def make_sockaddr_in(family, port, addr) s = [ family ].pack("S") s << [ port ].pack("n") # network byteorder s << addr s << "\0" * 8 s end def split(s) ip = IP.new s len = ip.ip_hl * 4 # ip_hl is header length in 32 bit word. ip[len..-1] = "" [ ip, ICMP.new(s[len..-1]) ] end begin require 'icmpmodule' IP = IP_c ICMP = ICMP_c ICMPError = ICMPError_c rescue LoadError IP = IP_rb ICMP = ICMP_rb ICMPError = ICMPError_rb end end