Skip to content

Commit 4325fc4

Browse files
committed
Add ARP header support and refactor protocol handling in analyzer
1 parent aab0c11 commit 4325fc4

File tree

5 files changed

+143
-38
lines changed

5 files changed

+143
-38
lines changed

lib/redhound/analyzer.rb

+4
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@ def analyze
1818

1919
l3 = layer3_header(l2, l2.size)
2020
l3.dump
21+
return if @msg.bytes.size <= l2.size + l3.size
2122
unless l3.supported_protocol?
2223
puts " └─ Unsupported protocol #{l3.protocol}"
2324
return
2425
end
2526

27+
2628
layer4_header(l3, l2.size + l3.size).dump
2729
end
2830

@@ -31,6 +33,8 @@ def layer3_header(l2, offset)
3133
Header::Ipv4.generate(bytes: @msg.bytes[offset..])
3234
elsif l2.type.ipv6?
3335
Header::Ipv6.generate(bytes: @msg.bytes[offset..])
36+
elsif l2.type.arp?
37+
Header::Arp.generate(bytes: @msg.bytes[offset..])
3438
end
3539
end
3640

lib/redhound/header.rb

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# frozen_string_literal: true
22

3+
require_relative 'header/arp'
34
require_relative 'header/ether'
45
require_relative 'header/ethernet_protocol'
56
require_relative 'header/icmp'

lib/redhound/header/arp.rb

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# frozen_string_literal: true
2+
3+
module Redhound
4+
class Header
5+
class Arp
6+
class << self
7+
def generate(bytes:)
8+
new(bytes:).generate
9+
end
10+
end
11+
12+
def initialize(bytes:)
13+
raise ArgumentError, "bytes must be bigger than #{arp_size} bytes" unless bytes.size >= arp_size
14+
15+
@bytes = bytes
16+
end
17+
18+
def generate
19+
@htype = @bytes[0..1]
20+
@ptype = @bytes[2..3]
21+
@hlen = @bytes[4]
22+
@plen = @bytes[5]
23+
@oper = @bytes[6..7]
24+
@sha = @bytes[8..13]
25+
@spa = @bytes[14..17]
26+
@tha = @bytes[18..23]
27+
@tpa = @bytes[24..27]
28+
@type = EthernetProtocol.new(protocol: ptype)
29+
@l3 = generate_l3
30+
self
31+
end
32+
33+
def arp_size = 28
34+
35+
def size
36+
if @l3.nil?
37+
arp_size
38+
else
39+
arp_size + @l3.size
40+
end
41+
end
42+
43+
def dump
44+
puts self
45+
end
46+
47+
def to_s
48+
" └─ ARP HType: #{htype} PType: #{ptype} HLen: #{@hlen} PLen: #{@plen} Oper: #{oper} SHA: #{sha} SPA: #{spa} THA: #{tha} TPA: #{tpa}"
49+
end
50+
51+
def supported_protocol?
52+
@l3.supported_protocol? if @l3
53+
end
54+
55+
def protocol
56+
@l3.protocol if @l3
57+
end
58+
59+
private
60+
61+
def htype
62+
@htype.map { |b| b.to_s(16).rjust(2, '0') }.join.to_i(16)
63+
end
64+
65+
def ptype
66+
@ptype.map { |b| b.to_s(16).rjust(2, '0') }.join.to_i(16)
67+
end
68+
69+
def oper
70+
@oper.map { |b| b.to_s(16).rjust(2, '0') }.join.to_i(16)
71+
end
72+
73+
def sha
74+
@sha.map { |b| b.to_s(16).rjust(2, '0') }.join(':')
75+
end
76+
77+
def spa
78+
@spa.map { |b| b.to_s(16).rjust(2, '0') }.join('.')
79+
end
80+
81+
def tha
82+
@tha.map { |b| b.to_s(16).rjust(2, '0') }.join(':')
83+
end
84+
85+
def tpa
86+
@tpa.map { |b| b.to_s(16).rjust(2, '0') }.join('.')
87+
end
88+
89+
def generate_l3
90+
return if @bytes.size == arp_size
91+
92+
if @type.ipv4?
93+
Header::Ipv4.generate(bytes: @bytes[arp_size..])
94+
elsif @type.ipv6?
95+
Header::Ipv6.generate(bytes: @bytes[arp_size..])
96+
end
97+
end
98+
end
99+
end
100+
end

lib/redhound/header/ether.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def shost
4444
end
4545

4646
def supported_type?
47-
@type.ipv4? || @type.ipv6?
47+
@type.ipv4? || @type.ipv6? || @type.arp?
4848
end
4949

5050
private

lib/redhound/header/ipv6.rb

+37-37
Original file line numberDiff line numberDiff line change
@@ -7,53 +7,53 @@ class << self
77
def generate(bytes:)
88
new(bytes:).generate
99
end
10+
end
1011

11-
attr_reader :protocol
12+
attr_reader :protocol
1213

13-
def initialize(bytes:)
14-
raise ArgumentError, "bytes must be bigger than #{header_size} bytes" unless bytes.size >= header_size
14+
def initialize(bytes:)
15+
raise ArgumentError, "bytes must be bigger than #{size} bytes" unless bytes.size >= size
1516

16-
@bytes = bytes
17-
end
17+
@bytes = bytes
18+
end
1819

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
20+
def size = 40
21+
22+
def generate
23+
version_traffic_flow = @bytes[0..3].unpack('N')
24+
@version = (version_traffic_flow >> 28) & 0xF
25+
@traffic_class = (version_traffic_flow >> 20) & 0xFF
26+
@flow_label = version_traffic_flow & 0xFFFFF
27+
@payload_length = @bytes[4..5]
28+
@next_header = @bytes[6]
29+
@hop_limit = @bytes[7]
30+
@saddr = @bytes[8..23]
31+
@daddr = @bytes[24..39]
32+
@protocol = InternetProtocol.new(protocol: @next_header)
33+
end
3334

34-
def dump
35-
puts self
36-
end
35+
def dump
36+
puts self
37+
end
3738

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
39+
def to_s
40+
" └─ 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}"
41+
end
4142

42-
def supported_protocol?
43-
@protocol.udp?
44-
end
43+
def supported_protocol?
44+
@protocol.udp?
45+
end
4546

46-
def payload_length
47-
@payload_length.map { |b| b.to_s(16).rjust(2, '0') }.join.to_i(16)
48-
end
47+
def payload_length
48+
@payload_length.map { |b| b.to_s(16).rjust(2, '0') }.join.to_i(16)
49+
end
4950

50-
def saddr
51-
@saddr.map { |b| b.to_s(16).rjust(2, '0') }.join(':')
52-
end
51+
def saddr
52+
@saddr.map { |b| b.to_s(16).rjust(2, '0') }.join(':')
53+
end
5354

54-
def daddr
55-
@daddr.map { |b| b.to_s(16).rjust(2, '0') }.join(':')
56-
end
55+
def daddr
56+
@daddr.map { |b| b.to_s(16).rjust(2, '0') }.join(':')
5757
end
5858
end
5959
end

0 commit comments

Comments
 (0)