From 9a947b32a74e5f2866f12d9552e1265c3e3650a6 Mon Sep 17 00:00:00 2001 From: Nat Lasseter Date: Wed, 8 May 2024 14:23:26 +0100 Subject: Demo ends. For now... --- .gitignore | 1 + 01-hub2.rb | 21 ++++++++++---------- 02-hub3.rb | 21 ++++++++++---------- 03-switch3.rb | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ forward-please | 35 ++++++++++++++++++++------------- frame.rb | 13 ++++++++----- frame2.rb | 29 ++++++++++++++++++++++++++++ level.rb | 34 +++++++++----------------------- 8 files changed, 152 insertions(+), 63 deletions(-) create mode 100644 .gitignore create mode 100644 03-switch3.rb create mode 100644 frame2.rb 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 -- cgit v1.2.1