Tutorial¶
In this tutorial we will walk through the ostinato example.py
script that we used to verify the installation.
We start with importing the required modules and classes -
import os
import time
from ostinato.core import ost_pb, DroneProxy
from ostinato.protocols.mac_pb2 import mac, Mac
from ostinato.protocols.ip4_pb2 import ip4, Ip4
Setup some variables and create a DroneProxy
object that will act as a proxy to communicate with drone -
host_name = '127.0.0.1'
tx_port_number = 0
rx_port_number = 0
drone = DroneProxy(host_name)
Before we can instruct drone to do anything, we need to connect to drone. Once connected, we can invoke operations on drone. We query drone for the list of ports and then retrieve configuration for all the ports
drone.connect()
port_id_list = drone.getPortIdList()
port_config_list = drone.getPortConfig(port_id_list)
print('Port List')
print('---------')
for port in port_config_list.port:
print('%d.%s (%s)' % (port.port_id.id, port.name, port.description))
Several of the DroneProxy
's methods, including the ones for transmit and capture, take a list of ports to operate on, so we set these up next.
tx_port = ost_pb.PortIdList()
tx_port.port_id.add().id = tx_port_number;
rx_port = ost_pb.PortIdList()
rx_port.port_id.add().id = rx_port_number;
Configuring a stream on a port is a two-step process - add + configure.
First we assign a stream id and then add it -
stream_id = ost_pb.StreamIdList()
stream_id.port_id.CopyFrom(tx_port.port_id[0])
stream_id.stream_id.add().id = 1
drone.addStream(stream_id)
This just adds a 'default' stream. We then configure the stream using the same stream id as what we assigned earlier -
stream_cfg = ost_pb.StreamConfigList()
stream_cfg.port_id.CopyFrom(tx_port.port_id[0])
s = stream_cfg.stream.add()
s.stream_id.id = stream_id.stream_id[0].id
s.core.is_enabled = True
s.control.num_packets = 5
NOTE: By default when you add a stream, it is disabled. You need to set
Stream.core.is_enabled
to True to enable it.
After configuring basic stream parameters such as number of packets, we configure the protocols in the stream. Adding a protocol to a stream is a 3-step process.
- First we add a protocol with
protocol.add()
- Then we specify the specific protocol to use by assigning
protocol_id.id
to one of the variousost_pb.Protocol.kXXXFieldNumber
- Finally we configure that protocol's fields - the protocol's fields are accessed using the
Protocol.Extensions[XXX]
dictionary - If a protocol field is not set explicitly, a default value will be used in the generated packet
After setting up the stream parameters, we call modifyStream
to configure the default stream installed by addStream()
with the values given here -
# setup stream protocols as mac:eth2:ip4:udp:payload
p = s.protocol.add()
p.protocol_id.id = ost_pb.Protocol.kMacFieldNumber
p.Extensions[mac].dst_mac_mode = Mac.e_mm_fixed
p.Extensions[mac].src_mac_mode = Mac.e_mm_fixed
p.Extensions[mac].dst_mac = 0x001122334455 # 00:11:22:33:44:55
p.Extensions[mac].src_mac = 0x00aabbccddee # 00:aa:bb:cc:dd:ee
p = s.protocol.add()
p.protocol_id.id = ost_pb.Protocol.kEth2FieldNumber
p = s.protocol.add()
p.protocol_id.id = ost_pb.Protocol.kIp4FieldNumber
# reduce typing by creating a shorter reference to p.Extensions[ip4]
ip = p.Extensions[ip4]
ip.src_ip = 0x01020304 # 1.2.3.4
ip.dst_ip = 0x05060708 # 5.6.7.8
ip.dst_ip_mode = Ip4.e_im_inc_host
s.protocol.add().protocol_id.id = ost_pb.Protocol.kUdpFieldNumber
s.protocol.add().protocol_id.id = ost_pb.Protocol.kPayloadFieldNumber
drone.modifyStream(stream_cfg)
Before we start transmitting the stream, we build the generated packets and clear the port statistics -
build_cfg = ost_pb.BuildConfig()
build_cfg.port_id.CopyFrom(tx_port.port_id[0])
ack = drone.build(build_cfg)
if (ack.status): # error?
log.error('packets prebuild error: %s' % ack.notes)
drone.clearStats(tx_port)
drone.clearStats(rx_port)
Now we can start transmitting the stream that we configured. Since we want to capture the transmitted packets when they appear on the rx port we ensure that we start capture before we start transmit and do the reverse while stopping.
drone.startCapture(rx_port)
drone.startTransmit(tx_port)
# wait for transmit to finish
time.sleep(7)
drone.stopTransmit(tx_port)
drone.stopCapture(rx_port)
We verify the transmit and receive by retreiving statistics
tx_stats = drone.getStats(tx_port)
rx_stats = drone.getStats(rx_port)
We retrieve the captured packets, save it in a temporary pcap file and use tshark to dump them
buff = drone.getCaptureBuffer(rx_port.port_id[0])
drone.saveCaptureBuffer(buff, 'capture.pcap')
os.system('tshark -r capture.pcap')
os.remove('capture.pcap')
Finally, we cleanup by deleting the stream that we configured and disconnect from drone
drone.deleteStream(stream_id)
drone.disconnect()
Download/View the full example.py
Auto resolve Mac addresses¶
The above example uses a stream with fixed and known values of Mac addresses. You could instead configure to it to resolve (make sure you assign source IP correctly so that ARP resolution succeeds) -
# setup stream protocols as mac:eth2:ip4:udp:payload
p = s.protocol.add()
p.protocol_id.id = ost_pb.Protocol.kMacFieldNumber
p.Extensions[mac].dst_mac_mode = Mac.e_mm_resolve
p.Extensions[mac].src_mac_mode = Mac.e_mm_resolve
p = s.protocol.add()
p.protocol_id.id = ost_pb.Protocol.kIp4FieldNumber
# get a reference to the IP fields using p.Extensions[ip4]
ip = p.Extensions[ip4]
ip.src_ip = 0xc0a8010a # 192.168.1.10
ip.dst_ip = 0xc0a80114 # 192.168.1.20
s.protocol.add().protocol_id.id = ost_pb.Protocol.kUdpFieldNumber
s.protocol.add().protocol_id.id = ost_pb.Protocol.kPayloadFieldNumber
drone.modifyStream(stream_cfg)
To actually resolve the mac addresses, call resolveDeviceNeighbors()
before build()
-
drone.resolveDeviceNeighbors(tx_port)
build_cfg = ost_pb.BuildConfig()
build_cfg.port_id.CopyFrom(tx_port.port_id[0])
drone.build()