CLI: Initial Commit for Python Bindings
This commit is contained in:
parent
2e8e7c0440
commit
35472a6ac7
@ -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
0
binding/README.txt
Normal file
17
binding/__init__.py
Normal file
17
binding/__init__.py
Normal 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
48
binding/core.py
Normal 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
111
binding/example.py
Normal 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)
|
17
binding/protocols/__init__.py
Normal file
17
binding/protocols/__init__.py
Normal 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
63
binding/rpc.py
Normal 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
42
binding/setup.py
Normal 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': ''}
|
||||
)
|
@ -107,5 +107,8 @@ SOURCES += \
|
||||
|
||||
QMAKE_DISTCLEAN += object_script.*
|
||||
|
||||
#binding.depends = compiler_protobuf_py_make_all
|
||||
#QMAKE_EXTRA_TARGETS += binding
|
||||
|
||||
include(../protobuf.pri)
|
||||
|
||||
|
@ -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;
|
||||
|
11
protobuf.pri
11
protobuf.pri
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user