redhound 0.2.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -3
- data/Dockerfile +1 -1
- data/README.md +2 -2
- data/Rakefile +12 -1
- data/Steepfile +4 -0
- data/lib/redhound/analyzer.rb +19 -15
- data/lib/redhound/builder/packet_mreq.rb +56 -0
- data/lib/redhound/builder/socket.rb +35 -0
- data/lib/redhound/builder.rb +4 -0
- data/lib/redhound/command.rb +6 -1
- data/lib/redhound/l2/ether.rb +68 -0
- data/lib/redhound/l2/protocol.rb +33 -0
- data/lib/redhound/l2.rb +4 -0
- data/lib/redhound/l3/arp.rb +114 -0
- data/lib/redhound/l3/base.rb +49 -0
- data/lib/redhound/{header → l3}/ipv4.rb +27 -44
- data/lib/redhound/l3/ipv6.rb +69 -0
- data/lib/redhound/l3/protocol.rb +173 -0
- data/lib/redhound/l3/resolver.rb +30 -0
- data/lib/redhound/l3.rb +9 -0
- data/lib/redhound/l4/base.rb +31 -0
- data/lib/redhound/{header → l4}/icmp.rb +20 -26
- data/lib/redhound/l4/resolver.rb +28 -0
- data/lib/redhound/{header → l4}/udp.rb +19 -19
- data/lib/redhound/l4.rb +6 -0
- data/lib/redhound/receiver.rb +17 -5
- data/lib/redhound/resolver.rb +22 -0
- data/lib/redhound/source/socket.rb +18 -0
- data/lib/redhound/source.rb +3 -0
- data/lib/redhound/version.rb +2 -1
- data/lib/redhound/writer.rb +9 -2
- data/lib/redhound.rb +6 -3
- data/rbs_collection.lock.yaml +20 -0
- data/rbs_collection.yaml +17 -0
- data/sig/generated/redhound/analyzer.rbs +14 -0
- data/sig/generated/redhound/builder/packet_mreq.rbs +39 -0
- data/sig/generated/redhound/builder/socket.rbs +24 -0
- data/sig/generated/redhound/command.rbs +19 -0
- data/sig/generated/redhound/l2/ether.rbs +41 -0
- data/sig/generated/redhound/l2/protocol.rbs +15 -0
- data/sig/generated/redhound/l3/arp.rbs +57 -0
- data/sig/generated/redhound/l3/base.rbs +28 -0
- data/sig/generated/redhound/l3/ipv4.rbs +53 -0
- data/sig/generated/redhound/l3/ipv6.rbs +38 -0
- data/sig/generated/redhound/l3/protocol.rbs +16 -0
- data/sig/generated/redhound/l3/resolver.rbs +16 -0
- data/sig/generated/redhound/l4/base.rbs +19 -0
- data/sig/generated/redhound/l4/icmp.rbs +33 -0
- data/sig/generated/redhound/l4/resolver.rbs +16 -0
- data/sig/generated/redhound/l4/udp.rbs +36 -0
- data/sig/generated/redhound/receiver.rbs +19 -0
- data/sig/generated/redhound/resolver.rbs +11 -0
- data/sig/generated/redhound/source/socket.rbs +13 -0
- data/sig/generated/redhound/version.rbs +5 -0
- data/sig/generated/redhound/writer.rbs +25 -0
- metadata +48 -11
- data/lib/redhound/header/ether.rb +0 -67
- data/lib/redhound/header.rb +0 -6
- data/lib/redhound/packet_mreq.rb +0 -46
- data/lib/redhound/socket_builder.rb +0 -30
- data/sig/redhound.rbs +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7fc9146df6d249b2bdd3ca7c529c6d72eaad8c78970e6cc21d4be41f9e233a25
|
4
|
+
data.tar.gz: 9f66932249c4235042d3934579fdf7f9be92ad531f30e1e5c05ed1a55ae436e4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4ee95bd38fa1717c5286ae994db3b4d2c024c07119489d65f7b48a2cd9f5337b1a0cf0ebf36659f37d113052aaee178cd8ee29d50c2d995bee06d97443335fc6
|
7
|
+
data.tar.gz: 895d8f8b92858aaf63ac5bfc9b7ee2dc4a4fb594313e20052a96bdf893380aaae4853988c8e2966657ea3f2788eabf604a3ef44ebb16cf9954f0df5c4aeef444
|
data/CHANGELOG.md
CHANGED
@@ -1,9 +1,22 @@
|
|
1
|
-
|
1
|
+
# Changelog
|
2
2
|
|
3
|
-
##
|
3
|
+
## Unreleased
|
4
|
+
|
5
|
+
## 1.0.1 - 2025-01-17
|
6
|
+
|
7
|
+
- Fix an NameError in Redhound::L3::Arp
|
8
|
+
|
9
|
+
## 1.0.0 - 2025-01-16
|
10
|
+
|
11
|
+
- Add ARP header support.
|
12
|
+
- Add IPv6 header support.
|
13
|
+
- Improve formatting of packet output.
|
14
|
+
- Remove debug print statement from IPv4 header parsing.
|
15
|
+
|
16
|
+
## 0.2.0 - 2025-01-03
|
4
17
|
|
5
18
|
- Add option to write packets to file as PCAP Capture File Format.
|
6
19
|
|
7
|
-
##
|
20
|
+
## 0.1.0 - 2024-11-05
|
8
21
|
|
9
22
|
- Initial release
|
data/Dockerfile
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
FROM ruby:3.3.4
|
2
|
-
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs
|
2
|
+
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs iputils-ping iproute2 iptables
|
3
3
|
WORKDIR /app
|
4
4
|
COPY Gemfile /app/Gemfile
|
5
5
|
RUN bundle install
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Redhound
|
1
|
+
# Redhound [](https://badge.fury.io/rb/redhound) [](https://github.com/ydah/redhound/actions/workflows/main.yml)
|
2
2
|
|
3
3
|
Pure Ruby packet analyzer.
|
4
4
|
At this time, it is only guaranteed to work on Linux.
|
@@ -25,7 +25,7 @@ gem install redhound
|
|
25
25
|
/ , _/ -_) _ / _ \/ _ \/ // / _ \/ _ /
|
26
26
|
/_/|_|\__/\_,_/_//_/\___/\_,_/_//_/\_,_/
|
27
27
|
|
28
|
-
Version: 0.1
|
28
|
+
Version: 1.0.1
|
29
29
|
Dump and analyze network packets.
|
30
30
|
|
31
31
|
Usage: redhound [options] ...
|
data/Rakefile
CHANGED
@@ -1,4 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'bundler/gem_tasks'
|
4
|
-
|
4
|
+
|
5
|
+
desc "steep check"
|
6
|
+
task :steep do
|
7
|
+
sh "bundle exec steep check"
|
8
|
+
end
|
9
|
+
|
10
|
+
desc "Run rbs-inline"
|
11
|
+
task :rbs_inline do
|
12
|
+
sh "bundle exec rbs-inline --output lib/"
|
13
|
+
end
|
14
|
+
|
15
|
+
task default: %i[rbs_inline steep]
|
data/Steepfile
ADDED
data/lib/redhound/analyzer.rb
CHANGED
@@ -1,30 +1,34 @@
|
|
1
|
+
# rbs_inline: enabled
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
4
|
module Redhound
|
4
5
|
class Analyzer
|
5
|
-
|
6
|
-
|
6
|
+
# @rbs (msg: String, count: Integer) -> void
|
7
|
+
def self.analyze(msg:, count:)
|
8
|
+
new(msg:, count:).analyze
|
7
9
|
end
|
8
10
|
|
9
|
-
|
11
|
+
# @rbs (msg: String, count: Integer) -> void
|
12
|
+
def initialize(msg:, count:)
|
10
13
|
@msg = msg
|
14
|
+
@count = count
|
11
15
|
end
|
12
16
|
|
17
|
+
# @rbs () -> void
|
13
18
|
def analyze
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
return unless ether.ipv4?
|
19
|
+
l2 = L2::Ether.generate(bytes: @msg.bytes[0..], count: @count)
|
20
|
+
l2.dump
|
21
|
+
return unless l2.supported_type?
|
18
22
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
icmp = Header::Icmp.generate(bytes: @msg.bytes[34..41])
|
26
|
-
icmp.dump
|
23
|
+
l3 = L3::Resolver.resolve(bytes: @msg.bytes[l2.size..], l2:)
|
24
|
+
return if !l3 || @msg.bytes.size <= l2.size + l3.size
|
25
|
+
l3.dump
|
26
|
+
unless l3.supported_protocol?
|
27
|
+
puts " └─ Unsupported protocol #{l3.protocol}"
|
28
|
+
return
|
27
29
|
end
|
30
|
+
|
31
|
+
L4::Resolver.resolve(bytes: @msg.bytes[(l2.size + l3.size)..], l3:)&.dump
|
28
32
|
end
|
29
33
|
end
|
30
34
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# rbs_inline: enabled
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'socket'
|
5
|
+
|
6
|
+
module Redhound
|
7
|
+
module Builder
|
8
|
+
class PacketMreq
|
9
|
+
PACKET_MR_PROMISC = 0x0001 # NOTE: netpacket/packet.h
|
10
|
+
|
11
|
+
# @rbs (ifname: String) -> void
|
12
|
+
def initialize(ifname:)
|
13
|
+
@ifname = ifname
|
14
|
+
end
|
15
|
+
|
16
|
+
# see: https://man7.org/linux/man-pages/man7/packet.7.html
|
17
|
+
# struct packet_mreq {
|
18
|
+
# int mr_ifindex; /* interface index */
|
19
|
+
# unsigned short mr_type; /* action */
|
20
|
+
# unsigned short mr_alen; /* address length */
|
21
|
+
# unsigned char mr_address[8]; /* physical-layer address */
|
22
|
+
# };
|
23
|
+
# @rbs () -> String
|
24
|
+
def build
|
25
|
+
mr_ifindex + mr_type + mr_alen + mr_address
|
26
|
+
end
|
27
|
+
|
28
|
+
# @rbs () -> String
|
29
|
+
def mr_ifindex
|
30
|
+
@mr_ifindex ||= [[index].pack('I')].pack('a4')
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
# @rbs () -> Integer?
|
36
|
+
def index
|
37
|
+
::Socket.getifaddrs.find { |ifaddr| ifaddr.name == @ifname }&.ifindex
|
38
|
+
end
|
39
|
+
|
40
|
+
# @rbs () -> String
|
41
|
+
def mr_type
|
42
|
+
@mr_type ||= [PACKET_MR_PROMISC].pack('S')
|
43
|
+
end
|
44
|
+
|
45
|
+
# @rbs () -> String
|
46
|
+
def mr_alen
|
47
|
+
@mr_alen ||= [0].pack('S')
|
48
|
+
end
|
49
|
+
|
50
|
+
# @rbs () -> String
|
51
|
+
def mr_address
|
52
|
+
@mr_address ||= [0].pack('C') * 8
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# rbs_inline: enabled
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'socket'
|
5
|
+
|
6
|
+
module Redhound
|
7
|
+
module Builder
|
8
|
+
class Socket
|
9
|
+
SOL_PACKET = 0x0107 # bits/socket.h
|
10
|
+
PACKET_ADD_MEMBERSHIP = 0x0001 # NOTE: netpacket/packet.h
|
11
|
+
ETH_P_ALL = 768 # NOTE: htons(ETH_P_ALL) => linux/if_ether.h
|
12
|
+
PACKED_ETH_P_ALL = [ETH_P_ALL].pack('S').unpack1('S>')
|
13
|
+
|
14
|
+
class << self
|
15
|
+
# @rbs (ifname: String) -> Redhound::Source::Socket
|
16
|
+
def build(ifname:)
|
17
|
+
new(ifname:).build
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# @rbs (ifname: String) -> void
|
22
|
+
def initialize(ifname:)
|
23
|
+
@mq_req = PacketMreq.new(ifname:)
|
24
|
+
end
|
25
|
+
|
26
|
+
# @rbs () -> Redhound::Source::Socket
|
27
|
+
def build
|
28
|
+
socket = ::Socket.new(::Socket::AF_PACKET, ::Socket::SOCK_RAW, ETH_P_ALL)
|
29
|
+
socket.bind([::Socket::AF_PACKET, PACKED_ETH_P_ALL, @mq_req.mr_ifindex].pack('SS>a16'))
|
30
|
+
socket.setsockopt(SOL_PACKET, PACKET_ADD_MEMBERSHIP, @mq_req.build)
|
31
|
+
Redhound::Source::Socket.new(socket:)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/redhound/command.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# rbs_inline: enabled
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'optparse'
|
@@ -5,10 +6,12 @@ require 'socket'
|
|
5
6
|
|
6
7
|
module Redhound
|
7
8
|
class Command
|
9
|
+
# @rbs () -> void
|
8
10
|
def initialize
|
9
11
|
@options = { ifname: nil }
|
10
12
|
end
|
11
13
|
|
14
|
+
# @rbs (Array[untyped] argv) -> void
|
12
15
|
def run(argv)
|
13
16
|
parse(argv)
|
14
17
|
if @options[:ifname].nil?
|
@@ -18,8 +21,9 @@ module Redhound
|
|
18
21
|
Receiver.run(ifname: @options[:ifname], filename: @options[:filename])
|
19
22
|
end
|
20
23
|
|
24
|
+
# @rbs (Array[untyped] argv) -> void
|
21
25
|
def parse(argv)
|
22
|
-
OptionParser.new do |o|
|
26
|
+
OptionParser.new do |o| # steep:ignore
|
23
27
|
o.banner = <<~'BANNER' + <<~BANNER2
|
24
28
|
___ ____ __
|
25
29
|
/ _ \___ ___/ / / ___ __ _____ ___/ /
|
@@ -55,6 +59,7 @@ module Redhound
|
|
55
59
|
|
56
60
|
private
|
57
61
|
|
62
|
+
# @rbs () -> void
|
58
63
|
def list_interfaces
|
59
64
|
::Socket.getifaddrs.each { |ifaddr| puts ifaddr.name }
|
60
65
|
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# rbs_inline: enabled
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Redhound
|
5
|
+
class L2
|
6
|
+
class Ether
|
7
|
+
attr_reader :type #: Protocol
|
8
|
+
|
9
|
+
class << self
|
10
|
+
# @rbs (bytes: Array[Integer], count: Integer) -> Redhound::L2::Ether
|
11
|
+
def generate(bytes:, count:)
|
12
|
+
new(bytes:, count:).generate
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# @rbs (bytes: Array[Integer], count: Integer) -> void
|
17
|
+
def initialize(bytes:, count:)
|
18
|
+
raise ArgumentError, "bytes must be #{size} bytes" unless bytes.size >= size
|
19
|
+
|
20
|
+
@bytes = bytes
|
21
|
+
@count = count
|
22
|
+
end
|
23
|
+
|
24
|
+
# @rbs () -> Integer
|
25
|
+
def size = 14
|
26
|
+
|
27
|
+
# @rbs () -> Redhound::L2::Ether
|
28
|
+
def generate
|
29
|
+
@dhost = @bytes[0..5]
|
30
|
+
@shost = @bytes[6..11]
|
31
|
+
@type = Protocol.new(protocol: hex_type(@bytes[12..13]))
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
# @rbs () -> void
|
36
|
+
def dump
|
37
|
+
puts self
|
38
|
+
end
|
39
|
+
|
40
|
+
# @rbs () -> String
|
41
|
+
def to_s
|
42
|
+
"[#{@count}] Ethernet Dst: #{dhost} Src: #{shost} Type: #{@type}"
|
43
|
+
end
|
44
|
+
|
45
|
+
# @rbs () -> bool
|
46
|
+
def supported_type?
|
47
|
+
@type.ipv4? || @type.ipv6? || @type.arp? # steep:ignore
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
# @rbs () -> String
|
53
|
+
def dhost
|
54
|
+
@dhost.map { |b| b.to_s(16).rjust(2, '0') }.join(':')
|
55
|
+
end
|
56
|
+
|
57
|
+
# @rbs () -> String
|
58
|
+
def shost
|
59
|
+
@shost.map { |b| b.to_s(16).rjust(2, '0') }.join(':')
|
60
|
+
end
|
61
|
+
|
62
|
+
# @rbs (Array[Integer] type) -> Integer
|
63
|
+
def hex_type(type)
|
64
|
+
type.map { |b| b.to_s(16).rjust(2, '0') }.join.to_i(16)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# rbs_inline: enabled
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Redhound
|
5
|
+
class L2
|
6
|
+
class Protocol
|
7
|
+
PROTO_TABLE = {
|
8
|
+
0x0060 => 'Loopback',
|
9
|
+
0x0800 => 'IPv4',
|
10
|
+
0x0806 => 'ARP',
|
11
|
+
0x86DD => 'IPv6',
|
12
|
+
0x8100 => 'VLAN',
|
13
|
+
}
|
14
|
+
|
15
|
+
# @rbs (protocol: Integer) -> void
|
16
|
+
def initialize(protocol:)
|
17
|
+
@protocol = protocol
|
18
|
+
end
|
19
|
+
|
20
|
+
# @rbs () -> String
|
21
|
+
def to_s
|
22
|
+
PROTO_TABLE[@protocol] || 'Unknown'
|
23
|
+
end
|
24
|
+
|
25
|
+
PROTO_TABLE.each do |id, name|
|
26
|
+
method_name = name.downcase.gsub(/[ \-]/, '_') + '?'
|
27
|
+
define_method(method_name) do
|
28
|
+
@protocol == id
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/redhound/l2.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
# rbs_inline: enabled
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Redhound
|
5
|
+
class L3
|
6
|
+
class Arp < Base
|
7
|
+
class << self
|
8
|
+
# @rbs (bytes: Array[Integer]) -> Redhound::L3::Arp
|
9
|
+
def generate(bytes:)
|
10
|
+
new(bytes:).generate
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# @rbs (bytes: Array[Integer]) -> void
|
15
|
+
def initialize(bytes:)
|
16
|
+
raise ArgumentError, "bytes must be bigger than #{arp_size} bytes" unless bytes.size >= arp_size
|
17
|
+
|
18
|
+
@bytes = bytes
|
19
|
+
end
|
20
|
+
|
21
|
+
# @rbs () -> Redhound::L3::Arp
|
22
|
+
def generate
|
23
|
+
@htype = @bytes[0..1]
|
24
|
+
@ptype = @bytes[2..3]
|
25
|
+
@hlen = @bytes[4]
|
26
|
+
@plen = @bytes[5]
|
27
|
+
@oper = @bytes[6..7]
|
28
|
+
@sha = @bytes[8..13]
|
29
|
+
@spa = @bytes[14..17]
|
30
|
+
@tha = @bytes[18..23]
|
31
|
+
@tpa = @bytes[24..27]
|
32
|
+
@type = Redhound::L2::Protocol.new(protocol: ptype)
|
33
|
+
@l3 = generate_l3
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
# @rbs () -> Integer
|
38
|
+
def arp_size = 28
|
39
|
+
|
40
|
+
# @rbs () -> Integer
|
41
|
+
def size
|
42
|
+
if @l3.nil?
|
43
|
+
arp_size
|
44
|
+
else
|
45
|
+
arp_size + @l3.size
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# @rbs () -> String
|
50
|
+
def to_s
|
51
|
+
" └─ ARP HType: #{htype} PType: #{ptype} HLen: #{@hlen} PLen: #{@plen} Oper: #{oper} SHA: #{sha} SPA: #{spa} THA: #{tha} TPA: #{tpa}"
|
52
|
+
end
|
53
|
+
|
54
|
+
# @rbs () -> bool
|
55
|
+
def supported_protocol?
|
56
|
+
return false if @l3.nil?
|
57
|
+
@l3.supported_protocol?
|
58
|
+
end
|
59
|
+
|
60
|
+
# @rbs () -> String?
|
61
|
+
def protocol
|
62
|
+
@l3.protocol if @l3
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
# @rbs () -> Integer
|
68
|
+
def htype
|
69
|
+
@htype.map { |b| b.to_s(16).rjust(2, '0') }.join.to_i(16)
|
70
|
+
end
|
71
|
+
|
72
|
+
# @rbs () -> Integer
|
73
|
+
def ptype
|
74
|
+
@ptype.map { |b| b.to_s(16).rjust(2, '0') }.join.to_i(16)
|
75
|
+
end
|
76
|
+
|
77
|
+
# @rbs () -> Integer
|
78
|
+
def oper
|
79
|
+
@oper.map { |b| b.to_s(16).rjust(2, '0') }.join.to_i(16)
|
80
|
+
end
|
81
|
+
|
82
|
+
# @rbs () -> String
|
83
|
+
def sha
|
84
|
+
@sha.map { |b| b.to_s(16).rjust(2, '0') }.join(':')
|
85
|
+
end
|
86
|
+
|
87
|
+
# @rbs () -> String
|
88
|
+
def spa
|
89
|
+
@spa.map { |b| b.to_s(16).rjust(2, '0') }.join('.')
|
90
|
+
end
|
91
|
+
|
92
|
+
# @rbs () -> String
|
93
|
+
def tha
|
94
|
+
@tha.map { |b| b.to_s(16).rjust(2, '0') }.join(':')
|
95
|
+
end
|
96
|
+
|
97
|
+
# @rbs () -> String
|
98
|
+
def tpa
|
99
|
+
@tpa.map { |b| b.to_s(16).rjust(2, '0') }.join('.')
|
100
|
+
end
|
101
|
+
|
102
|
+
# @rbs () -> Redhound::L3::Base?
|
103
|
+
def generate_l3
|
104
|
+
return if @bytes.size == arp_size
|
105
|
+
|
106
|
+
if @type.ipv4?
|
107
|
+
Ipv4.generate(bytes: @bytes[arp_size..])
|
108
|
+
elsif @type.ipv6?
|
109
|
+
Ipv6.generate(bytes: @bytes[arp_size..])
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# rbs_inline: enabled
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Redhound
|
5
|
+
class L3
|
6
|
+
class Base
|
7
|
+
class << self
|
8
|
+
# @rbs (bytes: Array[Integer]) -> Redhound::L3::Base
|
9
|
+
def generate(bytes:)
|
10
|
+
new(bytes:).generate
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# @rbs (bytes: Array[Integer]) -> void
|
15
|
+
def initialize(bytes:)
|
16
|
+
warn 'initialize method must be implemented'
|
17
|
+
end
|
18
|
+
|
19
|
+
# @rbs () -> Redhound::L3::Base
|
20
|
+
def generate
|
21
|
+
warn 'generate method must be implemented'
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
# @rbs () -> void
|
26
|
+
def dump
|
27
|
+
puts self
|
28
|
+
end
|
29
|
+
|
30
|
+
# @rbs () -> Integer
|
31
|
+
def size
|
32
|
+
warn 'size method must be implemented'
|
33
|
+
0
|
34
|
+
end
|
35
|
+
|
36
|
+
# @rbs () -> bool
|
37
|
+
def supported_protocol?
|
38
|
+
warn 'supported_protocol? method must be implemented'
|
39
|
+
false
|
40
|
+
end
|
41
|
+
|
42
|
+
# @rbs () -> Protocol
|
43
|
+
def protocol
|
44
|
+
warn 'protocol method must be implemented'
|
45
|
+
Protocol.new(protocol: 0)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -1,24 +1,26 @@
|
|
1
|
+
# rbs_inline: enabled
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
4
|
module Redhound
|
4
|
-
class
|
5
|
-
class Ipv4
|
5
|
+
class L3
|
6
|
+
class Ipv4 < Base
|
6
7
|
class << self
|
8
|
+
# @rbs (bytes: Array[Integer]) -> Redhound::L3::Ipv4
|
7
9
|
def generate(bytes:)
|
8
10
|
new(bytes:).generate
|
9
11
|
end
|
10
12
|
end
|
11
13
|
|
12
|
-
|
13
|
-
ICMP = 1
|
14
|
-
UDP = 17
|
14
|
+
attr_reader :protocol #: Protocol
|
15
15
|
|
16
|
+
# @rbs (bytes: Array[Integer]) -> void
|
16
17
|
def initialize(bytes:)
|
17
|
-
raise ArgumentError,
|
18
|
+
raise ArgumentError, "bytes must be #{size} bytes" unless bytes.size >= size
|
18
19
|
|
19
20
|
@bytes = bytes
|
20
21
|
end
|
21
22
|
|
23
|
+
# @rbs () -> Redhound::L3::Ipv4
|
22
24
|
def generate
|
23
25
|
@version = @bytes[0]
|
24
26
|
@ihl = @bytes[0]
|
@@ -27,85 +29,66 @@ module Redhound
|
|
27
29
|
@id = @bytes[4..5]
|
28
30
|
@frag_off = @bytes[6..7]
|
29
31
|
@ttl = @bytes[8]
|
30
|
-
@protocol = @bytes[9]
|
32
|
+
@protocol = Protocol.new(protocol: @bytes[9])
|
31
33
|
@check = @bytes[10..11]
|
32
34
|
@saddr = @bytes[12..15]
|
33
35
|
@daddr = @bytes[16..19]
|
34
36
|
self
|
35
37
|
end
|
36
38
|
|
37
|
-
|
38
|
-
|
39
|
-
end
|
40
|
-
|
41
|
-
def udp?
|
42
|
-
@protocol == UDP
|
43
|
-
end
|
39
|
+
# @rbs () -> Integer
|
40
|
+
def size = 20
|
44
41
|
|
45
|
-
|
46
|
-
|
47
|
-
|
42
|
+
# @rbs () -> String
|
43
|
+
def to_s
|
44
|
+
" └─ 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}"
|
48
45
|
end
|
49
46
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
IHL: #{@ihl}
|
54
|
-
TOS: #{@tos}
|
55
|
-
Total Length: #{tot_len}
|
56
|
-
ID: #{id}
|
57
|
-
Fragment Offset: #{frag_off}
|
58
|
-
TTL: #{@ttl}
|
59
|
-
Protocol: #{protocol}
|
60
|
-
Checksum: #{check}
|
61
|
-
Source IP: #{saddr}
|
62
|
-
Destination IP: #{daddr}
|
63
|
-
IPV4
|
47
|
+
# @rbs () -> bool
|
48
|
+
def supported_protocol?
|
49
|
+
@protocol.udp? || @protocol.icmp? # steep:ignore
|
64
50
|
end
|
65
51
|
|
66
52
|
private
|
67
53
|
|
54
|
+
# @rbs () -> Integer
|
68
55
|
def version
|
69
56
|
@version & 0xF0
|
70
57
|
end
|
71
58
|
|
59
|
+
# @rbs () -> Integer
|
72
60
|
def ihl
|
73
61
|
@ihl & 0x0F
|
74
62
|
end
|
75
63
|
|
64
|
+
# @rbs () -> Integer
|
76
65
|
def tot_len
|
77
66
|
@tot_len.map { |b| b.to_s(16).rjust(2, '0') }.join.to_i(16)
|
78
67
|
end
|
79
68
|
|
69
|
+
# @rbs () -> Integer
|
80
70
|
def id
|
81
71
|
@id.map { |b| b.to_s(16).rjust(2, '0') }.join.to_i(16)
|
82
72
|
end
|
83
73
|
|
74
|
+
# @rbs () -> Integer
|
84
75
|
def frag_off
|
85
76
|
@frag_off.map { |b| b.to_s(16).rjust(2, '0') }.join.to_i(16) & 0x1FFF
|
86
77
|
end
|
87
78
|
|
88
|
-
|
89
|
-
case @protocol
|
90
|
-
when ICMP
|
91
|
-
'ICMP'
|
92
|
-
when UDP
|
93
|
-
'UDP'
|
94
|
-
else
|
95
|
-
'Unknown'
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
79
|
+
# @rbs () -> Integer
|
99
80
|
def check
|
100
81
|
@check.map { |b| b.to_s(16).rjust(2, '0') }.join.to_i(16)
|
101
82
|
end
|
102
83
|
|
84
|
+
# @rbs () -> String
|
103
85
|
def saddr
|
104
|
-
@saddr.
|
86
|
+
@saddr.join('.')
|
105
87
|
end
|
106
88
|
|
89
|
+
# @rbs () -> String
|
107
90
|
def daddr
|
108
|
-
@daddr.
|
91
|
+
@daddr.join('.')
|
109
92
|
end
|
110
93
|
end
|
111
94
|
end
|