redhound 0.1.0 → 1.0.0
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 +15 -2
- data/Dockerfile +1 -1
- data/README.md +10 -1
- 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 +8 -2
- 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 +8 -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 +26 -6
- 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 +53 -0
- data/lib/redhound.rb +7 -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 +49 -11
- data/lib/redhound/header/ether.rb +0 -68
- 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: 21334546fae10c6cbeb5b8d679e658317e39b569741ef671c45b7261dbb42106
|
4
|
+
data.tar.gz: 28046b9bfb2814928662e4d52768537bfd780778554c3073c1aebabf45da443b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 81a384c6381bb1473013536513b1b5e12e2faf7c78300cebf0dde4d778e0521ac8d80d6e0c31b5e27199158464711b3cc450f8b00e2cefc6e650d5801fd94c9c
|
7
|
+
data.tar.gz: b9f2fec1904e462b2d650cfbdb3d1704408160005dc103f4d6db39fbffacb28b1e3d371dfc4f518306e349f1269f9bea36ffdbaeecf51e5ac28ac2f29fb55b8c
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,18 @@
|
|
1
|
-
|
1
|
+
# Changelog
|
2
2
|
|
3
|
-
##
|
3
|
+
## Unreleased
|
4
|
+
|
5
|
+
## 1.0.0 - 2025-01-16
|
6
|
+
|
7
|
+
- Add ARP header support.
|
8
|
+
- Add IPv6 header support.
|
9
|
+
- Improve formatting of packet output.
|
10
|
+
- Remove debug print statement from IPv4 header parsing.
|
11
|
+
|
12
|
+
## 0.2.0 - 2025-01-03
|
13
|
+
|
14
|
+
- Add option to write packets to file as PCAP Capture File Format.
|
15
|
+
|
16
|
+
## 0.1.0 - 2024-11-05
|
4
17
|
|
5
18
|
- 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.
|
@@ -20,11 +20,20 @@ gem install redhound
|
|
20
20
|
## Usage
|
21
21
|
|
22
22
|
```command
|
23
|
+
___ ____ __
|
24
|
+
/ _ \___ ___/ / / ___ __ _____ ___/ /
|
25
|
+
/ , _/ -_) _ / _ \/ _ \/ // / _ \/ _ /
|
26
|
+
/_/|_|\__/\_,_/_//_/\___/\_,_/_//_/\_,_/
|
27
|
+
|
28
|
+
Version: 1.0.0
|
29
|
+
Dump and analyze network packets.
|
30
|
+
|
23
31
|
Usage: redhound [options] ...
|
24
32
|
|
25
33
|
Options:
|
26
34
|
-i, --interface INTERFACE name or idx of interface
|
27
35
|
-D, --list-interfaces print list of interfaces and exit
|
36
|
+
-w FILE write packets to a pcap capture file format to file
|
28
37
|
-h, --help display this help and exit
|
29
38
|
-v, --version display version information and exit
|
30
39
|
```
|
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,21 +6,24 @@ 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?
|
15
18
|
warn 'Error: interface is required'
|
16
19
|
exit 1
|
17
20
|
end
|
18
|
-
Receiver.run(ifname: @options[:ifname])
|
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
|
/ _ \___ ___/ / / ___ __ _____ ___/ /
|
@@ -39,6 +43,7 @@ module Redhound
|
|
39
43
|
list_interfaces
|
40
44
|
exit
|
41
45
|
end
|
46
|
+
o.on('-w FILE', 'write packets to a pcap capture file format to file') { |v| @options[:filename] = v }
|
42
47
|
o.on('-h', '--help', 'display this help and exit') do
|
43
48
|
puts o
|
44
49
|
exit
|
@@ -54,6 +59,7 @@ module Redhound
|
|
54
59
|
|
55
60
|
private
|
56
61
|
|
62
|
+
# @rbs () -> void
|
57
63
|
def list_interfaces
|
58
64
|
::Socket.getifaddrs.each { |ifaddr| puts ifaddr.name }
|
59
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
|