CLI: Initial Commit for Python Bindings
This commit is contained in:
parent
2e8e7c0440
commit
35472a6ac7
@ -1,6 +1,7 @@
|
|||||||
syntax: glob
|
syntax: glob
|
||||||
|
|
||||||
# generated object files
|
# generated object files
|
||||||
|
*.pyc
|
||||||
*.o
|
*.o
|
||||||
*.a
|
*.a
|
||||||
*.exe
|
*.exe
|
||||||
@ -20,6 +21,7 @@ Makefile*
|
|||||||
# protobuf generated files
|
# protobuf generated files
|
||||||
*.pb.h
|
*.pb.h
|
||||||
*.pb.cc
|
*.pb.cc
|
||||||
|
*_pb2.py
|
||||||
|
|
||||||
# ostinato generated files
|
# ostinato generated files
|
||||||
version.cpp
|
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.*
|
QMAKE_DISTCLEAN += object_script.*
|
||||||
|
|
||||||
|
#binding.depends = compiler_protobuf_py_make_all
|
||||||
|
#QMAKE_EXTRA_TARGETS += binding
|
||||||
|
|
||||||
include(../protobuf.pri)
|
include(../protobuf.pri)
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
package OstProto;
|
package OstProto;
|
||||||
option cc_generic_services = true;
|
option cc_generic_services = true;
|
||||||
|
option py_generic_services = true;
|
||||||
|
|
||||||
message StreamId {
|
message StreamId {
|
||||||
required uint32 id = 1;
|
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.name = protobuf header
|
||||||
protobuf_decl.input = PROTOS
|
protobuf_decl.input = PROTOS
|
||||||
protobuf_decl.output = ${QMAKE_FILE_BASE}.pb.h
|
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
|
protobuf_decl.variable_out = GENERATED_FILES
|
||||||
QMAKE_EXTRA_COMPILERS += protobuf_decl
|
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.commands = $$escape_expand(\n)
|
||||||
protobuf_impl.variable_out = GENERATED_SOURCES
|
protobuf_impl.variable_out = GENERATED_SOURCES
|
||||||
QMAKE_EXTRA_COMPILERS += protobuf_impl
|
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