aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--01-hub2.rb21
-rw-r--r--02-hub3.rb21
-rw-r--r--03-switch3.rb61
-rwxr-xr-xforward-please35
-rw-r--r--frame.rb13
-rw-r--r--frame2.rb29
-rw-r--r--level.rb34
8 files changed, 152 insertions, 63 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1377554
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+*.swp
diff --git a/01-hub2.rb b/01-hub2.rb
index 2a88ad9..7ecd8a1 100644
--- a/01-hub2.rb
+++ b/01-hub2.rb
@@ -1,32 +1,33 @@
require './level'
require './frame'
-class Level
- interfaces %w(1 2)
- description <<~EOD
+$levels << Level.new do
+ @interfaces = %w(1 2)
+ @description = <<~EOD
You are a two-port hub. Your job is to forward frames from one interface to the other.
EOD
- clicks 3
- def self.target(frame)
+ def target(frame)
case frame.iface
when ?1
[frame.to(?2)]
when ?2
[frame.to(?1)]
else
- puts "ERR"
[]
end
end
- def self.generate
+ def generate
frames = {}
- @@interfaces.each do |iface|
+ @interfaces.each do |iface|
next if rand < 0.3
- frames[@@count.to_s] = Frame.new(iface, "Frame #{@@count}")
- @@count += 1
+ frames[@count.to_s] = Frame.new(iface, @count)
+ @count += 1
end
frames
end
+
+ def click
+ end
end
diff --git a/02-hub3.rb b/02-hub3.rb
index 8750027..5379bb4 100644
--- a/02-hub3.rb
+++ b/02-hub3.rb
@@ -1,14 +1,13 @@
require './level'
require './frame'
-class Level
- interfaces %w(1 2 3)
- description <<~EOD
+$levels << Level.new do
+ @interfaces = %w(1 2 3)
+ @description = <<~EOD
You are now a three-port hub. Your job is to forward frames from one interface to all the others.
EOD
- clicks 5
- def self.target(frame)
+ def target(frame)
case frame.iface
when ?1
[frame.to(?2), frame.to(?3)]
@@ -17,18 +16,20 @@ class Level
when ?3
[frame.to(?1), frame.to(?2)]
else
- puts "ERR"
[]
end
end
- def self.generate
+ def generate
frames = {}
- @@interfaces.each do |iface|
+ @interfaces.each do |iface|
next if rand < 0.3
- frames[@@count.to_s] = Frame.new(iface, "Frame #{@@count}")
- @@count += 1
+ frames[@count.to_s] = Frame.new(iface, @count)
+ @count += 1
end
frames
end
+
+ def click
+ end
end
diff --git a/03-switch3.rb b/03-switch3.rb
new file mode 100644
index 0000000..7cbdc2f
--- /dev/null
+++ b/03-switch3.rb
@@ -0,0 +1,61 @@
+require './level'
+require './frame2'
+
+$levels << Level.new do
+ @interfaces = %w(1 2 3)
+ @description = <<~EOD
+ You are now a three-port switch.
+ When a frame comes in on a port, you should keep track of the source address,
+ and any frames destined for that address in future clicks should only be
+ forwarded to that port. All other frames should be broadcast as before.
+ EOD
+ @clicks = 8
+
+ @fib = @interfaces.product([nil]).to_h
+ @next_fib = @fib.dup
+
+ def target(frame)
+ ifib = @fib.invert
+ if ifib.keys.include?(frame.dst_addr)
+ [frame.to(ifib[frame.dst_addr])]
+ else
+ case frame.iface
+ when ?1
+ [frame.to(?2), frame.to(?3)]
+ when ?2
+ [frame.to(?1), frame.to(?3)]
+ when ?3
+ [frame.to(?1), frame.to(?2)]
+ else
+ []
+ end
+ end
+ end
+
+ def generate
+ frames = {}
+ @interfaces.each do |iface|
+ next if rand < 0.3
+
+ if rand < 0.4 || @fib[iface].nil?
+ src = Frame2.gen_addr
+ @next_fib[iface] = src
+ else
+ src = @fib[iface]
+ end
+
+ dst = nil
+ dst = (@fib.values.compact - [src]).sample if rand < 0.5
+ dst = Frame2.gen_addr if dst.nil?
+
+ frames[@count.to_s] = Frame2.new(iface, src, dst)
+ @count += 1
+ end
+ frames
+ end
+
+ def click
+ @fib = @next_fib
+ @next_fib = @fib.dup
+ end
+end
diff --git a/forward-please b/forward-please
index 70c6986..69f5dd1 100755
--- a/forward-please
+++ b/forward-please
@@ -1,9 +1,17 @@
#!/usr/bin/env ruby
-$levels = %w(01-hub2 02-hub3)
-require "./#{$levels.shift}"
+$levels = []
-puts Level.description
+%w(
+ 01-hub2 02-hub3
+ 03-switch3
+).each do |file|
+ require "./#{file}"
+end
+
+$level = $levels.shift
+
+puts $level.description
def help
puts <<~EOF
@@ -46,7 +54,7 @@ def click
targetframes = []
$clickframes.each do |id, frame|
- targetframes += Level.target(frame)
+ targetframes += $level.target(frame)
end
targetframes.flatten!
@@ -64,19 +72,20 @@ def click
$instructions = []
+ $level.click
+
$click += 1
- if $click > Level.clicks
+ if $click > $level.clicks
$click = 1
- level = $levels.shift
- if level.nil?
+ $level = $levels.shift
+ if $level.nil?
puts "Demo all done"
exit 0
end
- require "./#{level}"
- puts Level.description
+ puts $level.description
end
- $clickframes = Level.generate
+ $clickframes = $level.generate
clickactions
end
@@ -92,9 +101,9 @@ def handle(cmd)
puts "f {frame} {iface}"
end
when ?d
- puts Level.description
+ puts $level.description
when ?i
- puts Level.interfaces
+ puts $level.interfaces
when ?a
clickactions
when ?c
@@ -104,7 +113,7 @@ def handle(cmd)
end
end
-$clickframes = Level.generate
+$clickframes = $level.generate
clickactions
loop do
diff --git a/frame.rb b/frame.rb
index a61c547..1fda655 100644
--- a/frame.rb
+++ b/frame.rb
@@ -1,10 +1,14 @@
class Frame
- def initialize(iface, desc)
+ def initialize(iface, id)
@iface = iface
- @description = desc
+ @id = id
end
- attr_accessor :iface, :description
+ attr_accessor :iface
+
+ def description
+ "Frame #{@id}"
+ end
def to(iface)
t = dup
@@ -13,7 +17,6 @@ class Frame
end
def ==(oth)
- @description == oth.description &&
- @iface == oth.iface
+ @iface == oth.iface && @id == oth.id
end
end
diff --git a/frame2.rb b/frame2.rb
new file mode 100644
index 0000000..04b015a
--- /dev/null
+++ b/frame2.rb
@@ -0,0 +1,29 @@
+class Frame2
+ def initialize(iface, src, dst)
+ @iface = iface
+ @src_addr = src
+ @dst_addr = dst
+ end
+
+ attr_accessor :iface, :src_addr, :dst_addr
+
+ def description
+ "{src = #{@src_addr}, dst = #{@dst_addr}}"
+ end
+
+ def to(iface)
+ t = dup
+ t.iface = iface
+ t
+ end
+
+ def ==(oth)
+ @iface == oth.iface &&
+ @src_addr == oth.src_addr &&
+ @dst_addr == oth.dst_addr
+ end
+
+ def self.gen_addr
+ "#{rand(16).to_s(16)}:#{rand(16).to_s(16)}:#{rand(16).to_s(16)}"
+ end
+end
diff --git a/level.rb b/level.rb
index 1a5cfee..9a34afc 100644
--- a/level.rb
+++ b/level.rb
@@ -1,32 +1,16 @@
class Level
- @@interfaces = []
- @@description = ""
- @@count = 0
- @@clicks = 5
+ def initialize(&block)
+ @interfaces = []
+ @description = ""
+ @count = 0
+ @clicks = 3
- attr_accessor :description
-
- def self.interfaces(arr = nil)
- if arr.nil?
- (["Interfaces:"] + @@interfaces).join("\n ")
- else
- @@interfaces = arr
- end
+ self.instance_eval(&block)
end
- def self.description(desc = nil)
- if desc.nil?
- @@description
- else
- @@description = desc
- end
- end
+ attr_reader :description, :clicks
- def self.clicks(clicks = nil)
- if clicks.nil?
- @@clicks
- else
- @@clicks = clicks
- end
+ def interfaces
+ (["Interfaces:"] + @@interfaces).join("\n ")
end
end