packetfu 1.1.10 → 1.1.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +2 -0
  4. data/.gitignore +3 -0
  5. data/.travis.yml +8 -0
  6. data/CONTRIBUTING.md +47 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.txt +1 -1
  9. data/README.rdoc +35 -30
  10. data/Rakefile +4 -4
  11. data/bench/octets.rb +9 -9
  12. data/examples/100kpackets.rb +12 -12
  13. data/examples/ackscan.rb +16 -16
  14. data/examples/arp.rb +35 -35
  15. data/examples/arphood.rb +36 -36
  16. data/examples/dissect_thinger.rb +6 -6
  17. data/examples/new-simple-stats.rb +23 -23
  18. data/examples/packetfu-shell.rb +25 -25
  19. data/examples/simple-sniffer.rb +9 -9
  20. data/examples/simple-stats.rb +23 -23
  21. data/examples/slammer.rb +3 -3
  22. data/gem-public_cert.pem +21 -0
  23. data/lib/packetfu.rb +149 -127
  24. data/lib/packetfu/capture.rb +169 -169
  25. data/lib/packetfu/config.rb +52 -52
  26. data/lib/packetfu/inject.rb +56 -56
  27. data/lib/packetfu/packet.rb +531 -528
  28. data/lib/packetfu/pcap.rb +579 -579
  29. data/lib/packetfu/protos/arp.rb +90 -90
  30. data/lib/packetfu/protos/arp/header.rb +158 -158
  31. data/lib/packetfu/protos/arp/mixin.rb +36 -36
  32. data/lib/packetfu/protos/eth.rb +44 -44
  33. data/lib/packetfu/protos/eth/header.rb +243 -243
  34. data/lib/packetfu/protos/eth/mixin.rb +3 -3
  35. data/lib/packetfu/protos/hsrp.rb +69 -69
  36. data/lib/packetfu/protos/hsrp/header.rb +107 -107
  37. data/lib/packetfu/protos/hsrp/mixin.rb +29 -29
  38. data/lib/packetfu/protos/icmp.rb +71 -71
  39. data/lib/packetfu/protos/icmp/header.rb +82 -82
  40. data/lib/packetfu/protos/icmp/mixin.rb +14 -14
  41. data/lib/packetfu/protos/invalid.rb +49 -49
  42. data/lib/packetfu/protos/ip.rb +69 -69
  43. data/lib/packetfu/protos/ip/header.rb +291 -291
  44. data/lib/packetfu/protos/ip/mixin.rb +40 -40
  45. data/lib/packetfu/protos/ipv6.rb +50 -50
  46. data/lib/packetfu/protos/ipv6/header.rb +188 -188
  47. data/lib/packetfu/protos/ipv6/mixin.rb +29 -29
  48. data/lib/packetfu/protos/tcp.rb +176 -176
  49. data/lib/packetfu/protos/tcp/ecn.rb +35 -35
  50. data/lib/packetfu/protos/tcp/flags.rb +74 -74
  51. data/lib/packetfu/protos/tcp/header.rb +268 -268
  52. data/lib/packetfu/protos/tcp/hlen.rb +32 -32
  53. data/lib/packetfu/protos/tcp/mixin.rb +46 -46
  54. data/lib/packetfu/protos/tcp/option.rb +321 -321
  55. data/lib/packetfu/protos/tcp/options.rb +95 -95
  56. data/lib/packetfu/protos/tcp/reserved.rb +35 -35
  57. data/lib/packetfu/protos/udp.rb +159 -123
  58. data/lib/packetfu/protos/udp/header.rb +91 -91
  59. data/lib/packetfu/protos/udp/mixin.rb +3 -3
  60. data/lib/packetfu/structfu.rb +280 -280
  61. data/lib/packetfu/utils.rb +292 -225
  62. data/lib/packetfu/version.rb +41 -41
  63. data/packetfu.gemspec +14 -3
  64. data/spec/arp_spec.rb +191 -0
  65. data/spec/eth_spec.rb +148 -0
  66. data/spec/icmp_spec.rb +97 -0
  67. data/spec/ip_spec.rb +78 -0
  68. data/spec/ipv6_spec.rb +81 -0
  69. data/spec/packet_spec.rb +61 -59
  70. data/spec/packet_subclasses_spec.rb +9 -10
  71. data/spec/packetfu_spec.rb +55 -62
  72. data/spec/sample3.pcap +0 -0
  73. data/spec/spec_helper.rb +44 -0
  74. data/spec/structfu_spec.rb +270 -271
  75. data/spec/tcp_spec.rb +76 -77
  76. data/spec/udp_spec.rb +32 -0
  77. data/spec/utils_spec.rb +95 -0
  78. data/test/all_tests.rb +14 -17
  79. data/test/func_lldp.rb +3 -3
  80. data/test/ptest.rb +2 -2
  81. data/test/test_capture.rb +45 -45
  82. data/test/test_eth.rb +70 -68
  83. data/test/test_hsrp.rb +9 -9
  84. data/test/test_inject.rb +18 -18
  85. data/test/test_invalid.rb +16 -16
  86. data/test/test_octets.rb +23 -21
  87. data/test/test_packet.rb +156 -154
  88. data/test/test_pcap.rb +172 -170
  89. data/test/test_structfu.rb +99 -97
  90. data/test/test_tcp.rb +322 -320
  91. data/test/test_udp.rb +78 -76
  92. metadata +108 -44
  93. metadata.gz.sig +2 -0
  94. data/spec/ethpacket_spec.rb +0 -74
  95. data/test/test_arp.rb +0 -135
  96. data/test/test_icmp.rb +0 -62
  97. data/test/test_ip.rb +0 -50
  98. data/test/test_ip6.rb +0 -68
@@ -3,105 +3,105 @@ require 'packetfu/protos/tcp/option'
3
3
 
4
4
  module PacketFu
5
5
 
6
- class TcpOptions < Array
6
+ class TcpOptions < Array
7
7
 
8
- include StructFu
8
+ include StructFu
9
9
 
10
- # If args[:pad] is set, the options line is automatically padded out
11
- # with NOPs.
12
- def to_s(args={})
13
- opts = self.map {|x| x.to_s}.join
14
- if args[:pad]
15
- unless (opts.size % 4).zero?
16
- (4 - (opts.size % 4)).times { opts << "\x01" }
17
- end
18
- end
19
- opts
20
- end
10
+ # If args[:pad] is set, the options line is automatically padded out
11
+ # with NOPs.
12
+ def to_s(args={})
13
+ opts = self.map {|x| x.to_s}.join
14
+ if args[:pad]
15
+ unless (opts.size % 4).zero?
16
+ (4 - (opts.size % 4)).times { opts << "\x01" }
17
+ end
18
+ end
19
+ opts
20
+ end
21
21
 
22
- # Reads a string to populate the object.
23
- def read(str)
24
- self.clear
25
- PacketFu.force_binary(str)
26
- return self if(!str.respond_to? :to_s || str.nil?)
27
- i = 0
28
- while i < str.to_s.size
29
- this_opt = case str[i,1].unpack("C").first
30
- when 0; ::PacketFu::TcpOption::EOL.new
31
- when 1; ::PacketFu::TcpOption::NOP.new
32
- when 2; ::PacketFu::TcpOption::MSS.new
33
- when 3; ::PacketFu::TcpOption::WS.new
34
- when 4; ::PacketFu::TcpOption::SACKOK.new
35
- when 5; ::PacketFu::TcpOption::SACK.new
36
- when 6; ::PacketFu::TcpOption::ECHO.new
37
- when 7; ::PacketFu::TcpOption::ECHOREPLY.new
38
- when 8; ::PacketFu::TcpOption::TS.new
39
- else; ::PacketFu::TcpOption.new
40
- end
41
- this_opt.read str[i,str.size]
42
- unless this_opt.has_optlen?
43
- this_opt.value = nil
44
- this_opt.optlen = nil
45
- end
46
- self << this_opt
47
- i += this_opt.sz
48
- end
49
- self
50
- end
22
+ # Reads a string to populate the object.
23
+ def read(str)
24
+ self.clear
25
+ PacketFu.force_binary(str)
26
+ return self if(!str.respond_to? :to_s || str.nil?)
27
+ i = 0
28
+ while i < str.to_s.size
29
+ this_opt = case str[i,1].unpack("C").first
30
+ when 0; ::PacketFu::TcpOption::EOL.new
31
+ when 1; ::PacketFu::TcpOption::NOP.new
32
+ when 2; ::PacketFu::TcpOption::MSS.new
33
+ when 3; ::PacketFu::TcpOption::WS.new
34
+ when 4; ::PacketFu::TcpOption::SACKOK.new
35
+ when 5; ::PacketFu::TcpOption::SACK.new
36
+ when 6; ::PacketFu::TcpOption::ECHO.new
37
+ when 7; ::PacketFu::TcpOption::ECHOREPLY.new
38
+ when 8; ::PacketFu::TcpOption::TS.new
39
+ else; ::PacketFu::TcpOption.new
40
+ end
41
+ this_opt.read str[i,str.size]
42
+ unless this_opt.has_optlen?
43
+ this_opt.value = nil
44
+ this_opt.optlen = nil
45
+ end
46
+ self << this_opt
47
+ i += this_opt.sz
48
+ end
49
+ self
50
+ end
51
51
 
52
- # Decode parses the TcpOptions object's member options, and produces a
53
- # human-readable string by iterating over each element's decode() function.
54
- # If TcpOptions elements were not initially created as TcpOptions, an
55
- # attempt will be made to convert them.
56
- #
57
- # The output of decode is suitable as input for TcpOptions#encode.
58
- def decode
59
- decoded = self.map do |x|
60
- if x.kind_of? TcpOption
61
- x.decode
62
- else
63
- x = TcpOptions.new.read(x).decode
64
- end
65
- end
66
- decoded.join(",")
67
- end
52
+ # Decode parses the TcpOptions object's member options, and produces a
53
+ # human-readable string by iterating over each element's decode() function.
54
+ # If TcpOptions elements were not initially created as TcpOptions, an
55
+ # attempt will be made to convert them.
56
+ #
57
+ # The output of decode is suitable as input for TcpOptions#encode.
58
+ def decode
59
+ decoded = self.map do |x|
60
+ if x.kind_of? TcpOption
61
+ x.decode
62
+ else
63
+ x = TcpOptions.new.read(x).decode
64
+ end
65
+ end
66
+ decoded.join(",")
67
+ end
68
68
 
69
- # Encode takes a human-readable string and appends the corresponding
70
- # binary options to the TcpOptions object. To completely replace the contents
71
- # of the object, use TcpOptions#encode! instead.
72
- #
73
- # Options are comma-delimited, and are identical to the output of the
74
- # TcpOptions#decode function. Note that the syntax can be unforgiving, so
75
- # it may be easier to create the subclassed TcpOptions themselves directly,
76
- # but this method can be less typing if you know what you're doing.
77
- #
78
- # Note that by using TcpOptions#encode, strings supplied as values which
79
- # can be converted to numbers will be converted first.
80
- #
81
- # === Example
82
- #
83
- # t = TcpOptions.new
84
- # t.encode("MS:1460,WS:6")
85
- # t.to_s # => "\002\004\005\264\002\003\006"
86
- # t.encode("NOP")
87
- # t.to_s # => "\002\004\005\264\002\003\006\001"
88
- def encode(str)
89
- opts = str.split(/[\s]*,[\s]*/)
90
- opts.each do |o|
91
- kind,value = o.split(/[\s]*:[\s]*/)
92
- klass = TcpOption.const_get(kind.upcase)
93
- value = value.to_i if value =~ /^[0-9]+$/
94
- this_opt = klass.new
95
- this_opt.encode(value)
96
- self << this_opt
97
- end
98
- self
99
- end
69
+ # Encode takes a human-readable string and appends the corresponding
70
+ # binary options to the TcpOptions object. To completely replace the contents
71
+ # of the object, use TcpOptions#encode! instead.
72
+ #
73
+ # Options are comma-delimited, and are identical to the output of the
74
+ # TcpOptions#decode function. Note that the syntax can be unforgiving, so
75
+ # it may be easier to create the subclassed TcpOptions themselves directly,
76
+ # but this method can be less typing if you know what you're doing.
77
+ #
78
+ # Note that by using TcpOptions#encode, strings supplied as values which
79
+ # can be converted to numbers will be converted first.
80
+ #
81
+ # === Example
82
+ #
83
+ # t = TcpOptions.new
84
+ # t.encode("MS:1460,WS:6")
85
+ # t.to_s # => "\002\004\005\264\002\003\006"
86
+ # t.encode("NOP")
87
+ # t.to_s # => "\002\004\005\264\002\003\006\001"
88
+ def encode(str)
89
+ opts = str.split(/[\s]*,[\s]*/)
90
+ opts.each do |o|
91
+ kind,value = o.split(/[\s]*:[\s]*/)
92
+ klass = TcpOption.const_get(kind.upcase)
93
+ value = value.to_i if value =~ /^[0-9]+$/
94
+ this_opt = klass.new
95
+ this_opt.encode(value)
96
+ self << this_opt
97
+ end
98
+ self
99
+ end
100
100
 
101
- # Like TcpOption#encode, except the entire contents are replaced.
102
- def encode!(str)
103
- self.clear if self.size > 0
104
- encode(str)
105
- end
106
- end
101
+ # Like TcpOption#encode, except the entire contents are replaced.
102
+ def encode!(str)
103
+ self.clear if self.size > 0
104
+ encode(str)
105
+ end
106
+ end
107
107
  end
@@ -1,43 +1,43 @@
1
1
  # -*- coding: binary -*-
2
2
  module PacketFu
3
- # Implements the Reserved bits for TCPHeader.
4
- #
5
- # ==== Header Definition
6
- #
7
- #
8
- # Fixnum (1 bit) :r1
9
- # Fixnum (1 bit) :r2
10
- # Fixnum (1 bit) :r3
11
- class TcpReserved < Struct.new(:r1, :r2, :r3)
3
+ # Implements the Reserved bits for TCPHeader.
4
+ #
5
+ # ==== Header Definition
6
+ #
7
+ #
8
+ # Fixnum (1 bit) :r1
9
+ # Fixnum (1 bit) :r2
10
+ # Fixnum (1 bit) :r3
11
+ class TcpReserved < Struct.new(:r1, :r2, :r3)
12
12
 
13
- include StructFu
13
+ include StructFu
14
14
 
15
- def initialize(args={})
16
- super(
17
- args[:r1] || 0,
18
- args[:r2] || 0,
19
- args[:r3] || 0) if args.kind_of? Hash
20
- end
15
+ def initialize(args={})
16
+ super(
17
+ args[:r1] || 0,
18
+ args[:r2] || 0,
19
+ args[:r3] || 0) if args.kind_of? Hash
20
+ end
21
21
 
22
- # Returns the Reserved field as an integer.
23
- def to_i
24
- (r1.to_i << 2) + (r2.to_i << 1) + r3.to_i
25
- end
22
+ # Returns the Reserved field as an integer.
23
+ def to_i
24
+ (r1.to_i << 2) + (r2.to_i << 1) + r3.to_i
25
+ end
26
26
 
27
- # Reads a string to populate the object.
28
- def read(str)
29
- force_binary(str)
30
- return self if str.nil? || str.size.zero?
31
- if 1.respond_to? :ord
32
- byte = str[0].ord
33
- else
34
- byte = str[0]
35
- end
36
- self[:r1] = byte & 0b00000100 == 0b00000100 ? 1 : 0
37
- self[:r2] = byte & 0b00000010 == 0b00000010 ? 1 : 0
38
- self[:r3] = byte & 0b00000001 == 0b00000001 ? 1 : 0
39
- self
40
- end
27
+ # Reads a string to populate the object.
28
+ def read(str)
29
+ force_binary(str)
30
+ return self if str.nil? || str.size.zero?
31
+ if 1.respond_to? :ord
32
+ byte = str[0].ord
33
+ else
34
+ byte = str[0]
35
+ end
36
+ self[:r1] = byte & 0b00000100 == 0b00000100 ? 1 : 0
37
+ self[:r2] = byte & 0b00000010 == 0b00000010 ? 1 : 0
38
+ self[:r3] = byte & 0b00000001 == 0b00000001 ? 1 : 0
39
+ self
40
+ end
41
41
 
42
- end
42
+ end
43
43
  end
@@ -5,138 +5,174 @@ require 'packetfu/protos/eth/mixin'
5
5
  require 'packetfu/protos/ip/header'
6
6
  require 'packetfu/protos/ip/mixin'
7
7
 
8
+ require 'packetfu/protos/ipv6/header'
9
+ require 'packetfu/protos/ipv6/mixin'
10
+
8
11
  require 'packetfu/protos/udp/header'
9
12
  require 'packetfu/protos/udp/mixin'
10
13
 
11
14
  module PacketFu
12
15
 
13
- # UDPPacket is used to construct UDP Packets. They contain an EthHeader, an IPHeader, and a UDPHeader.
14
- #
15
- # == Example
16
- #
17
- # udp_pkt = PacketFu::UDPPacket.new
18
- # udp_pkt.udp_src=rand(0xffff-1024) + 1024
19
- # udp_pkt.udp_dst=53
20
- #
21
- # udp_pkt.ip_saddr="1.2.3.4"
22
- # udp_pkt.ip_daddr="10.20.30.40"
23
- #
24
- # udp_pkt.recalc
25
- # udp_pkt.to_f('/tmp/udp.pcap')
26
- #
27
- # == Parameters
28
- #
29
- # :eth
30
- # A pre-generated EthHeader object.
31
- # :ip
32
- # A pre-generated IPHeader object.
33
- # :flavor
34
- # TODO: Sets the "flavor" of the UDP packet. UDP packets don't tend have a lot of
35
- # flavor, but their underlying ip headers do.
36
- # :config
37
- # A hash of return address details, often the output of Utils.whoami?
38
- class UDPPacket < Packet
16
+ # UDPPacket is used to construct UDP Packets. They contain an EthHeader, an IPHeader, and a UDPHeader.
17
+ #
18
+ # == Example
19
+ #
20
+ # udp_pkt = PacketFu::UDPPacket.new
21
+ # udp_pkt.udp_src=rand(0xffff-1024) + 1024
22
+ # udp_pkt.udp_dst=53
23
+ # udp_pkt.ip_saddr="1.2.3.4"
24
+ # udp_pkt.ip_daddr="10.20.30.40"
25
+ # udp_pkt.recalc
26
+ # udp_pkt.to_f('/tmp/udp.pcap')
27
+ #
28
+ # udp6_pkt = PacketFu::UDPPacket.new(:on_ipv6 => true)
29
+ # udp6_pkt.udp_src=rand(0xffff-1024) + 1024
30
+ # udp6_pkt.udp_dst=53
31
+ # udp6_pkt.ip6_saddr="4::1"
32
+ # udp6_pkt.ip6_daddr="12:3::4567"
33
+ # udp6_pkt.recalc
34
+ # udp6_pkt.to_f('/tmp/udp.pcap')
35
+ #
36
+ # == Parameters
37
+ #
38
+ # :eth
39
+ # A pre-generated EthHeader object.
40
+ # :ip
41
+ # A pre-generated IPHeader object.
42
+ # :flavor
43
+ # TODO: Sets the "flavor" of the UDP packet. UDP packets don't tend have a lot of
44
+ # flavor, but their underlying ip headers do.
45
+ # :config
46
+ # A hash of return address details, often the output of Utils.whoami?
47
+ class UDPPacket < Packet
39
48
  include ::PacketFu::EthHeaderMixin
40
49
  include ::PacketFu::IPHeaderMixin
50
+ include ::PacketFu::IPv6HeaderMixin
41
51
  include ::PacketFu::UDPHeaderMixin
42
52
 
43
- attr_accessor :eth_header, :ip_header, :udp_header
44
-
45
- def self.can_parse?(str)
46
- return false unless str.size >= 28
47
- return false unless EthPacket.can_parse? str
48
- return false unless IPPacket.can_parse? str
49
- return false unless str[23,1] == "\x11"
50
- return true
51
- end
52
-
53
- def read(str=nil, args={})
54
- raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
55
- @eth_header.read(str)
56
- if args[:strip]
57
- udp_body_len = self.ip_len - self.ip_hlen - 8
58
- @udp_header.body.read(@udp_header.body.to_s[0,udp_body_len])
59
- end
60
- super(args)
61
- self
62
- end
63
-
64
- def initialize(args={})
65
- @eth_header = EthHeader.new(args).read(args[:eth])
66
- @ip_header = IPHeader.new(args).read(args[:ip])
67
- @ip_header.ip_proto=0x11
68
- @udp_header = UDPHeader.new(args).read(args[:icmp])
69
- @ip_header.body = @udp_header
70
- @eth_header.body = @ip_header
71
- @headers = [@eth_header, @ip_header, @udp_header]
72
- super
73
- udp_calc_sum
74
- end
75
-
76
- # udp_calc_sum() computes the UDP checksum, and is called upon intialization.
77
- # It usually should be called just prior to dropping packets to a file or on the wire.
78
- def udp_calc_sum
79
- # This is /not/ delegated down to @udp_header since we need info
80
- # from the IP header, too.
81
- checksum = (ip_src.to_i >> 16)
82
- checksum += (ip_src.to_i & 0xffff)
83
- checksum += (ip_dst.to_i >> 16)
84
- checksum += (ip_dst.to_i & 0xffff)
85
- checksum += 0x11
86
- checksum += udp_len.to_i
87
- checksum += udp_src.to_i
88
- checksum += udp_dst.to_i
89
- checksum += udp_len.to_i
90
- if udp_len.to_i >= 8
91
- # For IP trailers. This isn't very reliable. :/
92
- real_udp_payload = payload.to_s[0,(udp_len.to_i-8)]
93
- else
94
- # I'm not going to mess with this right now.
95
- real_udp_payload = payload
96
- end
97
- chk_payload = (real_udp_payload.size % 2 == 0 ? real_udp_payload : real_udp_payload + "\x00")
98
- chk_payload.unpack("n*").each {|x| checksum = checksum+x}
99
- checksum = checksum % 0xffff
100
- checksum = 0xffff - checksum
101
- checksum == 0 ? 0xffff : checksum
102
- @udp_header.udp_sum = checksum
103
- end
104
-
105
- # udp_recalc() recalculates various fields of the UDP packet. Valid arguments are:
106
- #
107
- # :all
108
- # Recomputes all calculated fields.
109
- # :udp_sum
110
- # Recomputes the UDP checksum.
111
- # :udp_len
112
- # Recomputes the UDP length.
113
- def udp_recalc(args=:all)
114
- case args
115
- when :udp_len
116
- @udp_header.udp_recalc
117
- when :udp_sum
118
- udp_calc_sum
119
- when :all
120
- @udp_header.udp_recalc
121
- udp_calc_sum
122
- else
123
- raise ArgumentError, "No such field `#{arg}'"
124
- end
125
- end
126
-
127
- # Peek provides summary data on packet contents.
128
- def peek_format
129
- peek_data = ["U "]
130
- peek_data << "%-5d" % self.to_s.size
131
- peek_data << "%-21s" % "#{self.ip_saddr}:#{self.udp_sport}"
132
- peek_data << "->"
133
- peek_data << "%21s" % "#{self.ip_daddr}:#{self.udp_dport}"
134
- peek_data << "%23s" % "I:"
135
- peek_data << "%04x" % self.ip_id
136
- peek_data.join
137
- end
138
-
139
- end
53
+ attr_accessor :eth_header, :ip_header, :ipv6_header, :udp_header
54
+
55
+ def self.can_parse?(str)
56
+ return false unless str.size >= 28
57
+ return false unless EthPacket.can_parse? str
58
+ return false unless IPPacket.can_parse? str
59
+ return false unless str[23,1] == "\x11"
60
+ return true
61
+ end
62
+
63
+ def read(str=nil, args={})
64
+ raise "Cannot parse `#{str}'" unless self.class.can_parse?(str)
65
+ @eth_header.read(str)
66
+ if args[:strip]
67
+ udp_body_len = self.ip_len - self.ip_hlen - 8
68
+ @udp_header.body.read(@udp_header.body.to_s[0,udp_body_len])
69
+ end
70
+ super(args)
71
+ self
72
+ end
73
+
74
+ def initialize(args={})
75
+ if args[:on_ipv6] or args[:ipv6]
76
+ @eth_header = EthHeader.new(args.merge(:eth_proto => 0x86dd)).read(args[:eth])
77
+ @ipv6_header = IPv6Header.new(args).read(args[:ipv6])
78
+ @ipv6_header.ipv6_next=0x11
79
+ else
80
+ @eth_header = EthHeader.new(args).read(args[:eth])
81
+ @ip_header = IPHeader.new(args).read(args[:ip])
82
+ @ip_header.ip_proto=0x11
83
+ end
84
+ @udp_header = UDPHeader.new(args).read(args[:udp])
85
+ if args[:on_ipv6] or args[:ipv6]
86
+ @ipv6_header.body = @udp_header
87
+ @eth_header.body = @ipv6_header
88
+ @headers = [@eth_header, @ipv6_header, @udp_header]
89
+ else
90
+ @ip_header.body = @udp_header
91
+ @eth_header.body = @ip_header
92
+ @headers = [@eth_header, @ip_header, @udp_header]
93
+ end
94
+ super
95
+ udp_calc_sum
96
+ end
97
+
98
+ # udp_calc_sum() computes the UDP checksum, and is called upon intialization.
99
+ # It usually should be called just prior to dropping packets to a file or on the wire.
100
+ def udp_calc_sum
101
+ # This is /not/ delegated down to @udp_header since we need info
102
+ # from the IP header, too.
103
+ checksum = 0
104
+ if @ipv6_header
105
+ [ipv6_src, ipv6_dst].each do |iaddr|
106
+ 8.times do |i|
107
+ checksum += (iaddr >> (i * 16)) & 0xffff
108
+ end
109
+ end
110
+ else
111
+ checksum += (ip_src.to_i >> 16)
112
+ checksum += (ip_src.to_i & 0xffff)
113
+ checksum += (ip_dst.to_i >> 16)
114
+ checksum += (ip_dst.to_i & 0xffff)
115
+ end
116
+ checksum += 0x11
117
+ checksum += udp_len.to_i
118
+ checksum += udp_src.to_i
119
+ checksum += udp_dst.to_i
120
+ checksum += udp_len.to_i
121
+ if udp_len.to_i >= 8
122
+ # For IP trailers. This isn't very reliable. :/
123
+ real_udp_payload = payload.to_s[0,(udp_len.to_i-8)]
124
+ else
125
+ # I'm not going to mess with this right now.
126
+ real_udp_payload = payload
127
+ end
128
+ chk_payload = (real_udp_payload.size % 2 == 0 ? real_udp_payload : real_udp_payload + "\x00")
129
+ chk_payload.unpack("n*").each {|x| checksum = checksum+x}
130
+ checksum = checksum % 0xffff
131
+ checksum = 0xffff - checksum
132
+ checksum == 0 ? 0xffff : checksum
133
+ @udp_header.udp_sum = checksum
134
+ end
135
+
136
+ # udp_recalc() recalculates various fields of the UDP packet. Valid arguments are:
137
+ #
138
+ # :all
139
+ # Recomputes all calculated fields.
140
+ # :udp_sum
141
+ # Recomputes the UDP checksum.
142
+ # :udp_len
143
+ # Recomputes the UDP length.
144
+ def udp_recalc(args=:all)
145
+ case args
146
+ when :udp_len
147
+ @udp_header.udp_recalc
148
+ when :udp_sum
149
+ udp_calc_sum
150
+ when :all
151
+ @udp_header.udp_recalc
152
+ udp_calc_sum
153
+ else
154
+ raise ArgumentError, "No such field `#{arg}'"
155
+ end
156
+ end
157
+
158
+ # Peek provides summary data on packet contents.
159
+ def peek_format
160
+ peek_data = ["U "]
161
+ peek_data << "%-5d" % self.to_s.size
162
+ peek_data << "%-21s" % "#{self.ip_saddr}:#{self.udp_sport}"
163
+ peek_data << "->"
164
+ peek_data << "%21s" % "#{self.ip_daddr}:#{self.udp_dport}"
165
+ peek_data << "%23s" % "I:"
166
+ peek_data << "%04x" % self.ip_id
167
+ peek_data.join
168
+ end
169
+
170
+ # Is that packet an UDP on IPv6 packet ?
171
+ def ipv6?
172
+ @ipv6_header
173
+ end
174
+
175
+ end
140
176
 
141
177
  end
142
178