Skip to content

Commit aab0c11

Browse files
committed
Add IPv6 header support and refactor protocol handling in analyzer
1 parent 90adf57 commit aab0c11

File tree

7 files changed

+107
-17
lines changed

7 files changed

+107
-17
lines changed

lib/redhound/analyzer.rb

+26-13
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,33 @@ def initialize(msg:, count:)
1212
end
1313

1414
def analyze
15-
ether = Header::Ether.generate(bytes: @msg.bytes[0..13], count: @count)
16-
ether.dump
17-
return unless ether.type.ipv4?
15+
l2 = Header::Ether.generate(bytes: @msg.bytes[0..], count: @count)
16+
l2.dump
17+
return unless l2.supported_type?
1818

19-
ip = Header::Ipv4.generate(bytes: @msg.bytes[14..33])
20-
ip.dump
21-
if ip.protocol.udp?
22-
udp = Header::Udp.generate(bytes: @msg.bytes[34..])
23-
udp.dump
24-
elsif ip.protocol.icmp?
25-
icmp = Header::Icmp.generate(bytes: @msg.bytes[34..])
26-
icmp.dump
27-
else
28-
puts " └─ Unknown protocol #{ip.protocol}"
19+
l3 = layer3_header(l2, l2.size)
20+
l3.dump
21+
unless l3.supported_protocol?
22+
puts " └─ Unsupported protocol #{l3.protocol}"
23+
return
24+
end
25+
26+
layer4_header(l3, l2.size + l3.size).dump
27+
end
28+
29+
def layer3_header(l2, offset)
30+
if l2.type.ipv4?
31+
Header::Ipv4.generate(bytes: @msg.bytes[offset..])
32+
elsif l2.type.ipv6?
33+
Header::Ipv6.generate(bytes: @msg.bytes[offset..])
34+
end
35+
end
36+
37+
def layer4_header(l3, offset)
38+
if l3.protocol.udp?
39+
Header::Udp.generate(bytes: @msg.bytes[offset..])
40+
elsif l3.protocol.icmp?
41+
Header::Icmp.generate(bytes: @msg.bytes[offset..])
2942
end
3043
end
3144
end

lib/redhound/header.rb

+1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
require_relative 'header/ethernet_protocol'
55
require_relative 'header/icmp'
66
require_relative 'header/ipv4'
7+
require_relative 'header/ipv6'
78
require_relative 'header/internet_protocol'
89
require_relative 'header/udp'

lib/redhound/header/ether.rb

+7-1
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@ def generate(bytes:, count:)
1212
end
1313

1414
def initialize(bytes:, count:)
15-
raise ArgumentError, 'bytes must be 14 bytes' unless bytes.size == 14
15+
raise ArgumentError, "bytes must be #{size} bytes" unless bytes.size >= size
1616

1717
@bytes = bytes
1818
@count = count
1919
end
2020

21+
def size = 14
22+
2123
def generate
2224
@dhost = @bytes[0..5]
2325
@shost = @bytes[6..11]
@@ -41,6 +43,10 @@ def shost
4143
@shost.map { |b| b.to_s(16).rjust(2, '0') }.join(':')
4244
end
4345

46+
def supported_type?
47+
@type.ipv4? || @type.ipv6?
48+
end
49+
4450
private
4551

4652
def hex_type(type)

lib/redhound/header/icmp.rb

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ def generate(bytes:)
1010
end
1111

1212
def initialize(bytes:)
13-
raise ArgumentError, 'bytes must be bigger than 8 bytes' unless bytes.size >= 8
13+
raise ArgumentError, "bytes must be bigger than #{size} bytes" unless bytes.size >= size
1414

1515
@bytes = bytes
1616
end
@@ -30,6 +30,8 @@ def generate
3030
self
3131
end
3232

33+
def size = 8
34+
3335
def dump
3436
puts self
3537
end

lib/redhound/header/ipv4.rb

+7-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def generate(bytes:)
1212
attr_reader :protocol
1313

1414
def initialize(bytes:)
15-
raise ArgumentError, 'bytes must be 20 bytes' unless bytes.size == 20
15+
raise ArgumentError, "bytes must be #{size} bytes" unless bytes.size >= size
1616

1717
@bytes = bytes
1818
end
@@ -32,6 +32,8 @@ def generate
3232
self
3333
end
3434

35+
def size = 20
36+
3537
def dump
3638
puts self
3739
end
@@ -40,6 +42,10 @@ def to_s
4042
" └─ IPv4 Ver: #{version} IHL: #{ihl} TOS: #{@tos} Total Length: #{tot_len} ID: #{id} Offset: #{frag_off} TTL: #{@ttl} Protocol: #{@protocol} Checksum: #{check} Src: #{saddr} Dst: #{daddr}"
4143
end
4244

45+
def supported_protocol?
46+
@protocol.udp? || @protocol.icmp?
47+
end
48+
4349
private
4450

4551
def version

lib/redhound/header/ipv6.rb

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# frozen_string_literal: true
2+
3+
module Redhound
4+
class Header
5+
class Ipv6
6+
class << self
7+
def generate(bytes:)
8+
new(bytes:).generate
9+
end
10+
11+
attr_reader :protocol
12+
13+
def initialize(bytes:)
14+
raise ArgumentError, "bytes must be bigger than #{header_size} bytes" unless bytes.size >= header_size
15+
16+
@bytes = bytes
17+
end
18+
19+
def size = 40
20+
21+
def generate
22+
version_traffic_flow = @bytes[0..3].unpack('N')
23+
@version = (version_traffic_flow >> 28) & 0xF
24+
@traffic_class = (version_traffic_flow >> 20) & 0xFF
25+
@flow_label = version_traffic_flow & 0xFFFFF
26+
@payload_length = @bytes[4..5]
27+
@next_header = @bytes[6]
28+
@hop_limit = @bytes[7]
29+
@saddr = @bytes[8..23]
30+
@daddr = @bytes[24..39]
31+
@protocol = InternetProtocol.new(protocol: @next_header)
32+
end
33+
34+
def dump
35+
puts self
36+
end
37+
38+
def to_s
39+
" └─ IPv6 Ver: #{version} Traffic Class: #{traffic_class} Flow Label: #{flow_label} Payload Length: #{payload_length} Next Header: #{@protocol} Hop Limit: #{hop_limit} Src: #{saddr} Dst: #{daddr}"
40+
end
41+
42+
def supported_protocol?
43+
@protocol.udp?
44+
end
45+
46+
def payload_length
47+
@payload_length.map { |b| b.to_s(16).rjust(2, '0') }.join.to_i(16)
48+
end
49+
50+
def saddr
51+
@saddr.map { |b| b.to_s(16).rjust(2, '0') }.join(':')
52+
end
53+
54+
def daddr
55+
@daddr.map { |b| b.to_s(16).rjust(2, '0') }.join(':')
56+
end
57+
end
58+
end
59+
end
60+
end

lib/redhound/header/udp.rb

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ def generate(bytes:)
1010
end
1111

1212
def initialize(bytes:)
13-
raise ArgumentError, 'bytes must be bigger than 8 bytes' unless bytes.size >= 8
13+
raise ArgumentError, "bytes must be bigger than #{size} bytes" unless bytes.size >= size
1414

1515
@bytes = bytes
1616
end
@@ -24,6 +24,8 @@ def generate
2424
self
2525
end
2626

27+
def size = 8
28+
2729
def dump
2830
puts self
2931
end

0 commit comments

Comments
 (0)