Module: EtherShell::ShellDsl

Defined in:
lib/ether_shell/shell_dsl.rb

Overview

Provides the Ethernet shell DSL.

Include this in the evaluation context where you want the Ethernet shell DSL.

Defined Under Namespace

Classes: Nothing

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.nothingObject

:nodoc: value that doesn't show up in irb



142
143
144
# File 'lib/ether_shell/shell_dsl.rb', line 142

def self.nothing
  @nothing ||= Nothing.new
end

.parse_mac_data(mac_data) ⇒ Object

:nodoc: turns a packet pattern into a string of raw bytes



129
130
131
132
133
134
135
136
137
138
139
# File 'lib/ether_shell/shell_dsl.rb', line 129

def self.parse_mac_data(mac_data)
  if mac_data.length == 12
    [mac_data].pack('H*')
  elsif mac_data.length == 14 && mac_data[0, 2] == '0x'
    [mac_data[2, 12]].pack('H*')
  elsif mac_data.kind_of? Array
    mac_data.pack('C*')
  else
    mac_data
  end
end

.parse_packet_data(packet_data) ⇒ Object

:nodoc: turns a packet pattern into a string of raw bytes



115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/ether_shell/shell_dsl.rb', line 115

def self.parse_packet_data(packet_data)
  if packet_data.kind_of? Array
    # Array of integers.
    packet_data.pack('C*')
  elsif packet_data.kind_of? String
    if packet_data[0, 2] == '0x'
      [packet_data[2..-1]].pack('H*')
    else
      packet_data
    end
  end
end

Instance Method Details

#connect(eth_device, ether_type, dest_mac) ⇒ Object

Creates a Ethernet socket for this shell.

Args:

eth_device

an Ethernet device name, e.g. 'eth0'

ether_type

2-byte Ethernet packet type number

dest_mac

MAC address of the endpoint to be tested; it can be a raw 6-byte string,



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/ether_shell/shell_dsl.rb', line 17

def connect(eth_device, ether_type, dest_mac)
  raise "Already connected. did you forget to call disconnect?" if @_socket
  mac_bytes = EtherShell::ShellDsl.parse_mac_data dest_mac
  @_socket = Ethernet.socket eth_device, ether_type
  @_socket.connect mac_bytes
  if @_verbose
    print ['Connected to ', mac_bytes.unpack('H*').first, ' using ',
           '%04x' % ether_type, ' via ', eth_device, "\n"].join
  end
  @_nothing = ''
  class <<@_nothing
    def inspect
      ''
    end
  end
  EtherShell::ShellDsl.nothing
end

#disconnectObject

Disconnects this shell's Ethernet socket.

A socket should have been connected previously, using connect or socket. The shell can take further connect and socket calls.



39
40
41
42
43
44
45
# File 'lib/ether_shell/shell_dsl.rb', line 39

def disconnect
  raise "Not connected. did you forget to call connect?" unless @_socket
  @_socket.close
  @_socket = nil
  print "Disconnected\n" if @_verbose
  EtherShell::ShellDsl.nothing
end

#expect(packet_data) ⇒ Object

Receives a packet and matches it against an expected value.

Args:

packet_data

an Array of integers (bytes), a hex string starting with 0x, or a string of raw bytes

Raises:

RuntimeError

if the shell was not connected to a socket by a call to connect or socket

RuntimeError

if the received packet doesn't match the expected value



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/ether_shell/shell_dsl.rb', line 97

def expect(packet_data)
  raise "Not connected. did you forget to call connect?" unless @_socket
  expected_bytes = EtherShell::ShellDsl.parse_packet_data packet_data
  
  print "Receiving... " if @_verbose
  bytes = @_socket.recv
  print " #{bytes.unpack('H*').first} " if @_verbose
  if bytes == expected_bytes
    print "OK\n" if @_verbose
  else
    print "!= #{expected_bytes.unpack('H*').first} ERROR\n" if @_verbose
    raise EtherShell::ExpectationError,
        "#{bytes.unpack('H*').first} != #{expected_bytes.unpack('H*').first}"
  end
  EtherShell::ShellDsl.nothing
end

#out(packet_data) ⇒ Object

Outputs a packet.

Args:

packet_data

an Array of integers (bytes), a hex string starting with 0x, or a string of raw bytes

Raises:

RuntimeError

if the shell was not connected to a socket by a call to connect or socket



76
77
78
79
80
81
82
83
84
85
# File 'lib/ether_shell/shell_dsl.rb', line 76

def out(packet_data)
  raise "Not connected. did you forget to call connect?" unless @_socket
  bytes = EtherShell::ShellDsl.parse_packet_data packet_data
  
  
  print "Sending #{bytes.unpack('H*').first}... " if @_verbose
  @_socket.send bytes
  print "OK\n" if @_verbose
  EtherShell::ShellDsl.nothing
end

#socket(high_socket) ⇒ Object

Connects this shell to a pre-created socket

Args:

high_socket

socket that behaves like an EtherShell::HighSocket



51
52
53
54
55
56
# File 'lib/ether_shell/shell_dsl.rb', line 51

def socket(high_socket)
  raise "Already connected. did you forget to call disconnect?" if @_socket
  @_socket = high_socket
  print "Connected directly to socket\n" if @_verbose
  EtherShell::ShellDsl.nothing
end

#verbose(true_or_false = true) ⇒ Object

Enables or disables the console output in out and expect.

Args:

true_or_false

if true, out and expect will produce console output



62
63
64
65
# File 'lib/ether_shell/shell_dsl.rb', line 62

def verbose(true_or_false = true)
  @_verbose = true_or_false
  EtherShell::ShellDsl.nothing
end