CLI: Initial Commit for Python Bindings

This commit is contained in:
Srivats P. 2014-06-03 07:14:54 +05:30
parent 2e8e7c0440
commit 35472a6ac7
11 changed files with 314 additions and 1 deletions

View File

@ -1,6 +1,7 @@
syntax: glob
# generated object files
*.pyc
*.o
*.a
*.exe
@ -20,6 +21,7 @@ Makefile*
# protobuf generated files
*.pb.h
*.pb.cc
*_pb2.py
# ostinato generated files
version.cpp

0
binding/README.txt Normal file
View File

17
binding/__init__.py Normal file
View File

@ -0,0 +1,17 @@
# Copyright (C) 2014 Srivats P.
#
# This file is part of "Ostinato"
#
# This is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>

48
binding/core.py Normal file
View File

@ -0,0 +1,48 @@
# Copyright (C) 2014 Srivats P.
#
# This file is part of "Ostinato"
#
# This is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
from rpc import OstinatoRpcChannel, OstinatoRpcController
import protocols.protocol_pb2 as ost_pb
class DroneProxy(object):
def __init__(self, host_name, port_number=7878):
self.host = host_name
self.port = port_number
self.channel = OstinatoRpcChannel()
self.stub = ost_pb.OstService_Stub(self.channel)
for method in self.stub.GetDescriptor().methods:
fn = lambda request, method_name=method.name: \
self.callRpcMethod(method_name, request)
self.__dict__[method.name] = fn
def hostName(self):
return self.host
def portNumber(self):
return self.port
def connect(self):
self.channel.connect(self.host, self.port)
def callRpcMethod(self, method_name, request):
controller = OstinatoRpcController()
ost_pb.OstService_Stub.__dict__[method_name](
self.stub, controller, request, None)
return controller.response

111
binding/example.py Normal file
View File

@ -0,0 +1,111 @@
# standard modules
import sys
import time
import logging
# ostinato modules - prepend 'ostinato.' to the module names when using
# an installed package i.e ostinato.core and ostinato.protocols.xxx
from core import ost_pb, DroneProxy
from protocols.mac_pb2 import mac
from protocols.ip4_pb2 import ip4, Ip4
host_name = '127.0.0.1'
tx_port_number = 1
rx_port_number = 1
# setup logging
log = logging.getLogger(__name__)
logging.basicConfig(level=logging.DEBUG)
drone = DroneProxy(host_name)
try:
# connect to drone
log.info('connecting to drone(%s:%d)'
% (drone.hostName(), drone.portNumber()))
drone.connect()
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;
# verify tx and rx ports exist
log.info('verifying tx_port %d' % tx_port.port_id[0].id)
port_config_list = drone.getPortConfig(tx_port)
log.info('-->' + port_config_list.__str__())
if len(port_config_list.port) <= 0:
log.error('invalid tx_port'
+ tx_port_number)
sys.exit(1)
log.info('verifying rx_port %d' % rx_port.port_id[0].id)
port_config_list = drone.getPortConfig(rx_port)
log.info('-->' + port_config_list.__str__())
if len(port_config_list.port) <= 0:
log.error('invalid rx_port'
+ rx_port_number)
sys.exit(1)
# add a stream
stream_id = ost_pb.StreamIdList()
stream_id.port_id.CopyFrom(tx_port.port_id[0])
stream_id.stream_id.add().id = 1
log.info('adding tx_stream %d' % stream_id.stream_id[0].id)
drone.addStream(stream_id)
# configure the stream
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 = 1
s.control.num_packets = 5
p = s.protocol.add()
p.protocol_id.id = ost_pb.Protocol.kMacFieldNumber
p.Extensions[mac].dst_mac = 0x001122334455
p.Extensions[mac].src_mac = 0x00aabbccddee
p = s.protocol.add()
p.protocol_id.id = ost_pb.Protocol.kEth2FieldNumber
p = s.protocol.add()
p.protocol_id.id = ost_pb.Protocol.kIp4FieldNumber
p.Extensions[ip4].src_ip = 0x01020304
p.Extensions[ip4].dst_ip = 0x05060708
p.Extensions[ip4].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
log.info('configuring tx_stream %d' % stream_id.stream_id[0].id)
drone.modifyStream(stream_cfg)
# clear tx/rx stats
log.info('clearing tx/rx stats')
drone.clearStats(tx_port)
drone.clearStats(rx_port)
# start transmit
log.info('starting transmit')
drone.startTx(tx_port)
# wait for transmit to finish
log.info('waiting for transmit to finish ...')
time.sleep(7)
# get tx/rx stats
log.info('retreiving stats')
tx_stats = drone.getStats(tx_port)
rx_stats = drone.getStats(rx_port)
log.info('--> (tx_stats)' + tx_stats.__str__())
log.info('--> (rx_stats)' + rx_stats.__str__())
log.info('tx pkts = %d, rx pkts = %d' %
(tx_stats.port_stats[0].tx_pkts, rx_stats.port_stats[0].rx_pkts))
except Exception, ex:
log.exception(ex)
sys.exit(1)

View File

@ -0,0 +1,17 @@
# Copyright (C) 2014 Srivats P.
#
# This file is part of "Ostinato"
#
# This is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>

63
binding/rpc.py Normal file
View File

@ -0,0 +1,63 @@
# Copyright (C) 2014 Srivats P.
#
# This file is part of "Ostinato"
#
# This is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
from google.protobuf.service import RpcChannel
from google.protobuf.service import RpcController
import socket
import struct
class OstinatoRpcController(RpcController):
def __init__(self):
super(OstinatoRpcController, self).__init__()
class OstinatoRpcChannel(RpcChannel):
def __init__(self):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
def connect(self, host, port):
self.sock.connect((host, port))
def CallMethod(self, method, controller, request, response_class, done):
OST_PB_MSG_HDR_SIZE = 8
OST_PB_MSG_TYPE_REQUEST = 1
req = request.SerializeToString()
self.sock.sendall(struct.pack('>HHI',
OST_PB_MSG_TYPE_REQUEST, method.index, len(req)) + req)
hdr = ''
while len(hdr) < OST_PB_MSG_HDR_SIZE:
chunk = self.sock.recv(OST_PB_MSG_HDR_SIZE - len(hdr))
if chunk == '':
raise RuntimeError("socket connection broken")
hdr = hdr + chunk
(type, method, resp_len) = struct.unpack('>HHI', hdr)
resp = ''
while len(resp) < resp_len:
chunk = self.sock.recv(resp_len - len(resp))
if chunk == '':
raise RuntimeError("socket connection broken")
resp = resp + chunk
response = response_class()
response.ParseFromString(resp)
controller.response = response

42
binding/setup.py Normal file
View File

@ -0,0 +1,42 @@
# Copyright (C) 2014 Srivats P.
#
# This file is part of "Ostinato"
#
# This is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
import os
import shutil
import sys
from setuptools import setup
def read(fname):
return open(os.path.join(os.path.dirname(__file__), fname)).read()
if sys.argv[1] == 'clean_sdist':
shutil.rmtree('dist', ignore_errors = True)
shutil.rmtree('ostinato.egg-info', ignore_errors = True)
sys.exit(0)
setup(name = 'ostinato',
version = 'FIXME',
author = 'Srivats P',
author_email = 'pstavirs@gmail.com',
license = "GPLv3+",
url = 'http://ostinato.org',
description = 'Ostinato is a network packet and traffic generator and analyzer. It aims to be "Wireshark in Reverse" and become complementary to Wireshark. It features custom packet crafting via a GUI or a script',
long_description = read('README.txt'),
install_requires = ['google.protobuf>=2.3'],
packages=['ostinato', 'ostinato.protocols'],
package_dir={'ostinato': ''}
)

View File

@ -107,5 +107,8 @@ SOURCES += \
QMAKE_DISTCLEAN += object_script.*
#binding.depends = compiler_protobuf_py_make_all
#QMAKE_EXTRA_TARGETS += binding
include(../protobuf.pri)

View File

@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package OstProto;
option cc_generic_services = true;
option py_generic_services = true;
message StreamId {
required uint32 id = 1;

View File

@ -20,7 +20,8 @@ for(p, PROTOPATH):PROTOPATHS += --proto_path=$${p}
protobuf_decl.name = protobuf header
protobuf_decl.input = PROTOS
protobuf_decl.output = ${QMAKE_FILE_BASE}.pb.h
protobuf_decl.commands = protoc --cpp_out="." $${PROTOPATHS} ${QMAKE_FILE_NAME}
#protobuf_decl.commands = protoc --cpp_out="." $${PROTOPATHS} ${QMAKE_FILE_NAME}
protobuf_decl.commands = protoc --cpp_out="." --python_out="../binding/protocols" $${PROTOPATHS} ${QMAKE_FILE_NAME}
protobuf_decl.variable_out = GENERATED_FILES
QMAKE_EXTRA_COMPILERS += protobuf_decl
@ -31,3 +32,11 @@ protobuf_impl.depends = ${QMAKE_FILE_BASE}.pb.h
protobuf_impl.commands = $$escape_expand(\n)
protobuf_impl.variable_out = GENERATED_SOURCES
QMAKE_EXTRA_COMPILERS += protobuf_impl
protobuf_py.name = protobuf python binding
protobuf_py.input = PROTOS
protobuf_py.output = ../binding/protocols/${QMAKE_FILE_BASE}_pb2.py
protobuf_py.commands = $$escape_expand(\n)
#protobuf_py.commands = protoc --python_out="../binding/protocols" $${PROTOPATHS} ${QMAKE_FILE_NAME}
protobuf_py.variable_out = GENERATED_FILES
QMAKE_EXTRA_COMPILERS += protobuf_py