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