Commit c91831c6 authored by Federico Sismondi's avatar Federico Sismondi

Merge branch 'develop' into 'master'

Develop

See merge request !9
parents e2d021a5 a5e92f55
......@@ -2,7 +2,12 @@
# Created by https://www.gitignore.io/api/python
# testing tool
tmp/*.pcap
tmp/*.json
data/results/*.pcap
data/results/*.json
data/dumps/*.pcap
data/dumps/*.json
### Python ###
......
......@@ -8,6 +8,8 @@ RUN apt-get -y install python3-pip
RUN apt-get -y install python-pip
RUN apt-get -y install supervisor
RUN apt-get -y install tcpdump
# for CoAP Server Californium (delete this when we start supporting IUT resources selection)
RUN apt-get -y install default-jre
ADD . /coap_testing_tool
ENV PATH="/coap_testing_tool:$PATH"
......@@ -27,8 +29,6 @@ RUN pip3 install -r coap_testing_tool/packet_router/requirements.txt
RUN pip3 install -r coap_testing_tool/sniffer/requirements.txt
RUN pip3 install -r coap_testing_tool/webserver/requirements.txt
# for CoAP Server Californium (delete this when we start supporting IUT resources selection)
RUN apt-get -y install default-jre
#RUN groupadd -g 500 coap && useradd -u 500 -g 500 coap
#USER coap
......
......@@ -47,11 +47,11 @@ if(env.JOB_NAME =~ 'coap_testing_tool/'){
}
stage("Testing Tool components unit-testing"){
stage("unittesting components"){
gitlabCommitStatus("Testing Tool's components unit-testing"){
sh '''
echo $AMQP_URL
pwd
python3 -m pytest coap_testing_tool/test_coordinator/tests/tests.py
python3 -m pytest coap_testing_tool/packet_router/tests/tests.py
python3 -m pytest coap_testing_tool/extended_test_descriptions/tests/tests.py
......@@ -59,15 +59,28 @@ if(env.JOB_NAME =~ 'coap_testing_tool/'){
}
}
stage("Test submodules"){
stage("unittesting submodules"){
gitlabCommitStatus("Testing Tool's components unit-testing"){
sh '''
echo $AMQP_URL
cd coap_testing_tool/test_analysis_tool
pwd
python3 -m pytest tests/test_core --ignore=tests/test_core/test_dissector/test_dissector_6lowpan.py
'''
}
}
stage("Functional API smoke tests"){
gitlabCommitStatus("Functional API smoke tests"){
sh '''
echo $AMQP_URL
sudo -E supervisord -c supervisor.conf
sleep 15
pwd
python3 -m pytest tests/test_api.py -vv
'''
}
}
}
}
......@@ -139,7 +152,6 @@ if(env.JOB_NAME =~ 'coap_testing_tool_docker_build/'){
}
}
if(env.JOB_NAME =~ 'coap_testing_tool_ansible_playbook/'){
node('sudo'){
......
......@@ -91,13 +91,35 @@ Second, **build** the testing tool, from inside coap_testing_tool dir run:
docker build -t finterop-coap .
```
If build fails due to a "Failed to fetch http://archive.ubuntu ...."
then:
```
docker build -t finterop-coap . --no-cache
```
Finally, **run** it, from inside coap_testing_tool run:
```
docker run -it --env AMQP_EXCHANGE='default' --env AMQP_URL='amqp://someUser:somePassword@server/amqp_vhost' --privileged finterop-coap supervisord --nodaemon --configuration supervisor.conf
```
alternatively, you can:
```
docker run -it --env AMQP_EXCHANGE=default --env AMQP_URL='amqp://someUser:somePassword@server/amqp_vhost' --privileged finterop-coap bash
root@bab3b2220510:/coap_testing_tool# supervisord -c supervisor.conf
root@bab3b2220510:/coap_testing_tool# supervisorctl -c supervisor.conf
agent RUNNING pid 28, uptime 0:00:02
automated-iut STARTING
bootstrap-agent-TT RUNNING pid 19, uptime 0:00:02
packet-router RUNNING pid 24, uptime 0:00:02
packet-sniffer RUNNING pid 18, uptime 0:00:02
tat RUNNING pid 17, uptime 0:00:02
test-coordinator RUNNING pid 26, uptime 0:00:02
supervisor>
```
Run the CLI & Agent and you are ready to launch CoAP tests from your PC!
#### Opt 2 & 3 - Build CoAP testing tool with ansible
......
......@@ -4,7 +4,7 @@ import os
import json
from urllib.parse import urlparse
__version__ = (0, 0, 4)
__version__ = (0, 0, 5)
project_dir = os.path.abspath(os.path.join(os.path.realpath(__file__), os.pardir))
......
Subproject commit f1904121ef66c830fde991a7945e334a155adfa8
Subproject commit 2c2d9f740e11eb53804768ac7ea568b2c346ec43
......@@ -31,7 +31,7 @@ class PacketRouter(threading.Thread):
AGENT_2_ID = AGENT_NAMES[1]
AGENT_TT_ID = AGENT_TT_ID
def __init__(self, conn, routing_table):
def __init__(self, conn, routing_table = None):
threading.Thread.__init__(self)
logger.info("Imported agent names of the test session: %s" %str(AGENT_NAMES))
......@@ -71,7 +71,7 @@ class PacketRouter(threading.Thread):
self.channel = self.connection.channel()
queue_name = 'data_packets_queue@%s' % COMPONENT_ID
self.channel.queue_declare(queue=queue_name)
self.channel.queue_declare(queue=queue_name, auto_delete = True )
self.channel.queue_bind(exchange=AMQP_EXCHANGE,
queue=queue_name,
......
......@@ -25,7 +25,7 @@ class PacketRouterTestCase(unittest.TestCase):
time.sleep(1)
# create and bind queue
self.channel.queue_declare(queue=self.queue_name)
self.channel.queue_declare(queue=self.queue_name, auto_delete=True)
self.channel.queue_bind(exchange=AMQP_EXCHANGE,
queue=self.queue_name,
routing_key='data.tun.#')
......
This diff is collapsed.
test_analysis_tool @ 6bd88d5c
Subproject commit f847ec23ba844e7bd4da8e3ef4ee454e44075ead
Subproject commit 6bd88d5cbe6834184ff132252b49f882b684099f
......@@ -10,6 +10,9 @@ from coap_testing_tool.utils.rmq_handler import RabbitMQHandler, JsonFormatter
COMPONENT_ID = 'test_coordinator'
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)
# init logging to stnd output and log files
logger = logging.getLogger(__name__)
......@@ -24,6 +27,9 @@ rabbitmq_handler.setFormatter(json_formatter)
logger.addHandler(rabbitmq_handler)
logger.setLevel(logging.DEBUG)
# make pika logger less verbose
logging.getLogger('pika').setLevel(logging.INFO)
TT_check_list = [
'dissection',
'analysis',
......@@ -50,7 +56,7 @@ if __name__ == '__main__':
logger.info('Setting up AMQP connection..')
# setup AMQP connection
connection = pika.BlockingConnection(pika.URLParameters(AMQP_URL))
channel = connection.channel()
except pika.exceptions.ConnectionClosed as cc:
logger.error(' AMQP cannot be established, is message broker up? \n More: %s' %traceback.format_exc())
sys.exit(1)
......@@ -189,7 +195,7 @@ if __name__ == '__main__':
'_type': 'testcoordination.error',
}),
exchange = AMQP_EXCHANGE,
routing_key ='control.testcoordination.error',
routing_key ='session.error',
properties=pika.BasicProperties(
content_type='application/json',
)
......
# -*- coding: utf-8 -*-
import base64
# !/usr/bin/env python3
import pika
import json
import uuid
import time
import logging
from threading import Timer
from coap_testing_tool.utils.exceptions import TatError, SnifferError,CoordinatorError, AmqpMessageError
from coap_testing_tool import AMQP_URL, AMQP_EXCHANGE
from collections import OrderedDict
from coap_testing_tool.utils.event_bus_messages import *
def publish_message(channel, message):
""" Published which uses message object metadata
:param channel:
:param message:
:return:
"""
properties = pika.BasicProperties(**message.get_properties())
channel.basic_publish(
exchange=AMQP_EXCHANGE,
routing_key=message.routing_key,
properties=properties,
body=message.to_json(),
)
def amqp_reply(channel, props, response):
def amqp_request(channel, request_message: Message, component_id: str):
# NOTE: channel must be a pika channel
# check first that sender didnt forget about reply to and corr id
try:
reply_to = props.reply_to
correlation_id = props.correlation_id
logging.info("reply_to: %s type: %s"%(str(reply_to),str(type(reply_to))))
logging.info("corr_id: %s type: %s" % (str(correlation_id), str(type(correlation_id))))
except KeyError:
logging.error(msg='There is an error on the request, either reply_to or correlation_id not provided')
return
logging.debug('Sending reply through the bus: r_key: %s , corr_id: %s'%(reply_to,correlation_id))
assert request_message.reply_to
assert request_message.correlation_id
response = None
reply_queue_name = 'amqp_rpc_%s@%s' % (str(uuid.uuid4())[:8], component_id)
result = channel.queue_declare(queue=reply_queue_name, auto_delete=True)
callback_queue = result.method.queue
# bind and listen to reply_to topic
channel.queue_bind(
exchange=AMQP_EXCHANGE,
queue=callback_queue,
routing_key=request_message.reply_to
)
channel.basic_publish(
body=json.dumps(response, ensure_ascii=False),
routing_key=reply_to,
exchange=AMQP_EXCHANGE,
properties=pika.BasicProperties(
content_type='application/json',
correlation_id=correlation_id,
)
exchange=AMQP_EXCHANGE,
routing_key=request_message.routing_key,
properties=pika.BasicProperties(**request_message.get_properties()),
body=request_message.to_json(),
)
# timeout in seconds
AMQP_REPLY_TOUT = 10
class AmqpSynchronousCallClient:
def __init__(self, component_id ):
# setup blocking connection, do not reuse the conection from coord, it needs to be a new one
self.connection = pika.BlockingConnection(pika.URLParameters(AMQP_URL))
self.component_id = component_id
# this generates a blocking channel
self.channel = self.connection.channel()
self.reply_queue_name = 'service_responses@%s'%self.component_id
def on_response(self, ch, method, props, body):
ch.basic_ack(delivery_tag=method.delivery_tag)
if self.corr_id == props.correlation_id:
self.response = body
else:
self.response = None
def call(self, routing_key, body):
result = self.channel.queue_declare(queue = self.reply_queue_name)
self.callback_queue = result.method.queue
# by convention routing key of answer is routing_key + .reply
self.channel.queue_bind(exchange=AMQP_EXCHANGE,
queue=self.callback_queue,
routing_key=routing_key + '.reply')
self.channel.basic_consume(self.on_response,
no_ack=False,
queue=self.callback_queue)
self.response = None
self.corr_id = str(uuid.uuid4())
self.channel.basic_qos(prefetch_count=1)
self.channel.basic_publish(exchange=AMQP_EXCHANGE,
routing_key= routing_key,
properties=pika.BasicProperties(
reply_to= routing_key + '.reply',
correlation_id=self.corr_id,
content_type='application/json',
),
body=json.dumps(body),
)
self.timeout = False
def timeout():
self.timeout = True
t = Timer(AMQP_REPLY_TOUT, timeout)
t.start()
while self.response is None and not self.timeout:
time.sleep(1)
self.connection.process_data_events()
if self.timeout:
raise AmqpMessageError("Response timeout for request: \nrouting_key: %s,\nbody%s"
%(routing_key,json.dumps(body)))
else:
t.cancel()
# cleaning up
self.channel.queue_delete(self.reply_queue_name)
return json.loads(self.response.decode('utf-8'),object_pairs_hook=OrderedDict)
#this is just an example of usage where we ask the sniffer for a pcap capture and we save it disk after:
if __name__ == '__main__':
connection = pika.BlockingConnection(pika.URLParameters(AMQP_URL))
amqpRPCClient = AmqpSynchronousCallClient("dummy_component")
time.sleep(0.2)
retries_left = 5
while retries_left > 0:
time.sleep(0.5)
method, props, body = channel.basic_get(reply_queue_name)
if method:
channel.basic_ack(method.delivery_tag)
if hasattr(props, 'correlation_id') and props.correlation_id == request_message.correlation_id:
break
retries_left -= 1
if retries_left > 0:
body_dict = json.loads(body.decode('utf-8'), object_pairs_hook=OrderedDict)
response = MsgReply(request_message, **body_dict)
else:
raise TimeoutError(
"Response timeout! rkey: %s , request type: %s" % (
request_message.routing_key,
request_message._type
)
)
body = {'_type': 'sniffing.getcapture', 'testcase_id': 'testcase pepito'}
ret = amqpRPCClient.call("control.sniffing.service", body=body)
# clean up
channel.queue_delete(reply_queue_name)
out = ret['value']
filename = ret['filename']
return response
# save to file
with open("./"+filename, "wb") as file2:
file2.write(base64.b64decode(out))
if __name__ == '__main__':
connection = pika.BlockingConnection(pika.URLParameters(AMQP_URL))
m = MsgSniffingGetCapture()
r = amqp_request(connection.channel(), m, 'someImaginaryComponent')
print(repr(r))
This diff is collapsed.
......@@ -23,6 +23,12 @@
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'''
This library is provided to allow standard python logging
to output log data as JSON formatted strings
'''
import logging
import json
import re
......@@ -181,25 +187,39 @@ class RabbitMQHandler(logging.Handler):
"""
def __init__(self, url, name, exchange="default"):
logging.Handler.__init__(self)
self.connection = pika.BlockingConnection(pika.URLParameters(url))
self.url = url
self.connection = pika.BlockingConnection(pika.URLParameters(url+"?heartbeat=10"))
self.channel = self.connection.channel()
self.exchange = exchange
self.name = name
def emit(self, record):
routing_key = ".".join(["log", record.levelname.lower(), self.name])
self.channel.basic_publish(
try:
self.channel.basic_publish(
exchange=self.exchange,
routing_key=routing_key,
body=self.format(record),
properties = pika.BasicProperties(
content_type='application/json',
content_type='application/json'
)
)
except pika.exceptions.ConnectionClosed :
print("Log hanlder connection closed. Reconnecting..")
self.connection = pika.BlockingConnection(pika.URLParameters(self.url + "?heartbeat=10"))
self.channel = self.connection.channel()
self.channel.basic_publish(
exchange=self.exchange,
routing_key=routing_key,
body=self.format(record),
properties=pika.BasicProperties(
content_type='application/json'
)
)
def close(self):
if self.channel:
self.channel.close()
self.channel.close()
if __name__ == "__main__":
......@@ -208,20 +228,20 @@ if __name__ == "__main__":
json_formatter = JsonFormatter()
rabbitmq_handler.setFormatter(json_formatter)
logger = logging.getLogger(__name__)
logger.addHandler(rabbitmq_handler)
logger.setLevel(logging.DEBUG)
log = logging.getLogger(__name__)
log.addHandler(rabbitmq_handler)
log.setLevel(logging.DEBUG)
sh = logging.StreamHandler()
logger.addHandler(sh)
log.addHandler(sh)
while True:
logger.critical("This is a critical message")
log.critical("This is a critical message")
time.sleep(1)
logger.error("This is an error")
log.error("This is an error")
time.sleep(1)
logger.warning("This is a warning")
log.warning("This is a warning")
time.sleep(1)
logger.info("This is an info")
log.info("This is an info")
time.sleep(1)
logger.debug("This is a debug")
\ No newline at end of file
log.debug("This is a debug")
\ No newline at end of file
{
"_type": "testsuite.manifest",
"testing_tool":"CoAP Testing Tool",
"version":"0.0.4",
"version":"0.0.5",
"tests_type": ["interoperability","conformance"],
"protocols_under_test": ["CoAP", "CoAP_CORE", "CoAP_OBS"],
"protocols_info": [
......@@ -24,14 +24,14 @@
"agent_names": ["coap_client_agent", "coap_server_agent"],
"iut_roles": ["coap_client", "coap_server"],
"available_location_models:": ["loc_mod_A_single_user", "loc_mod_F_single_user"],
"profile_conformance_test":[
"ui_profile_conformance_test":[
{
"field_name": "Testing Tool Description",
"type": "text",
"value": [
"Reference-based interoperability (~conformance) Testing Tool for validating conformance to CoAP RFCs.",
"The testing tool uses as reference implementation (golden image) Californium implementation (CoAP client and CoAP server).",
"The implementation of the tests are based on ETSI test description [ETSI CoAP test description](http://www.etsi.org/plugtests/CoAP/Document/CoAP_TestDescriptions_v015.pdf"
"The implementation of the tests are based on ETSI test description [ETSI CoAP test description](http://www.etsi.org/plugtests/CoAP/Document/CoAP_TestDescriptions_v015.pdf)"
]
},
{
......@@ -40,20 +40,10 @@
"selection_values":["coap_client", "coap_server"],
"mandatory": true
},
{
"field_name": "User's IUT mode",
"type": "selection",
"selection_values":["automated_iut", "user_assisted"],
"user_info":[
"Automated IUT: Implementations under test which fully automated, bundled to F-Interop, and that are ready execute the tests without the need of a second user.",
"User Assisted (default): Implementations under test which needs to be driven by a User to perform the interoperability test. Particularly for handling certain the CONFIGs and executing the STIMULIs described in the test description"
],
"mandatory": false
},
{
"field_name": "Testing tool version",
"type": "selection",
"selection_values":["0.0.3","0.0.2","0.0.1"],
"selection_values":["0.0.5","0.0.4","0.0.3","0.0.2","0.0.1"],
"mandatory": false
},
{
......@@ -68,7 +58,7 @@
}
],
"profile_interoperability_test":[
"ui_profile_interoperability_test":[
{
"field_name": "Testing Tool Description",
"type": "text",
......@@ -102,12 +92,22 @@
"User Assisted: Implementations under test which needs to be driven by a User to perform the interoperability test. Particularly for handling certain the CONFIGs and executing the STIMULIs described in the test description"
],
"mandatory": true,
"field_hook": ""
"conditional_trigger": "if automated_iut then trigger(automated_iut_selection)"
},
{
"hook":"automated_iut_selection",
"field_name": "Resource selection for IUT 2 (automated)",
"type": "selection",
"resource_repository_query":["sth like select all resources which are coap and available and automated "],
"user_info":[
"Choose one automated IUT to test agains from the list of the available resources."
],
"mandatory": true
},
{
"field_name": "Testing tool version",
"type": "selection",
"selection_values":["0.0.3","0.0.2","0.0.1"],
"selection_values":["0.0.5","0.0.4","0.0.3","0.0.2","0.0.1"],
"mandatory": false
},
{
......
......@@ -43,7 +43,7 @@ stdout_logfile_backups = 5
[program:tat]
directory= ./coap_testing_tool/test_analysis_tool
command = sh -c "sleep 2;/usr/bin/python3 -m ttproto.tat_coap"
command = sh -c "sleep 2;/usr/bin/python3 -m ttproto.tat_coap -i amqp"
autorestart=false
stopsignal=INT
stopasgroup=true
......
PCAP_empty_base64 = '1MOyoQIABAAAAAAAAAAAAMgAAAAAAAAA'
PCAP_TC_COAP_01_base64 = '1MOyoQIABAAAAAAAAAAAAAAABAAAAAAAGfdPV8tZCAAtAAAALQAAAAIAAABFAAApcawAAEARAAB/AAABfwAAAdYxFj' \
'MAFf4oQgGqAWLatHRlc3TBAhn3T1fHrAgAXgAAAF4AAAACAAAARQAAWlLmAABAEQAAfwAAAX8AAAEWM9YxAEb+WWJF' \
'qgFi2sAhHpEC/1R5cGU6IDAgKENPTikKQ29kZTogMSAoR0VUKQpNSUQ6IDQzNTIxClRva2VuOiA2MmRh'
PCAP_TC_COAP_01_mingled_with_tcp_traffic_base64 = "1MOyoQIABAAAAAAAAAAAAAAABAAAAAAATMtfV23ABwA+AAAAPgAAAAIAAABFAAA6" \
"JxBAAEAGAAB/AAABfwAAAcgXEJOtwX4FlOsjDIAYN4z+LgAAAQEICjpvXc86b1YDX" \
"+UAAAAATMtfV5zABwA4AAAAOAAAAAIAAABFAAA0d5tAAEAGAAB/AAABfwAAARCTyB" \
"eU6yMMrcF+C4AQLz3+KAAAAQEICjpvXc86b13PTMtfV6zBBwA+AAAAPgAAAAIAAAB" \
"FAAA65M5AAEAGAAB/AAABfwAAARCTyBeU6yMMrcF+C4AYLz3+LgAAAQEICjpvXc86" \
"b13PAF8AAABiTMtfV9LBBwA4AAAAOAAAAAIAAABFAAA0uNdAAEAGAAB/AAABfwAAA" \
"cgXEJOtwX4LlOsjEoAQN4z+KAAAAQEICjpvXc86b13PTMtfV93BBwCaAAAAmgAAAA" \
"IAAABFAACWGL9AAEAGAAB/AAABfwAAARCTyBeU6yMSrcF+C4AYLz3+igAAAQEICjp" \
"vXc86b13PNjh8MTk0fDEzMDc3M3wwfDJ8MHwwfDB8NDUxOTU2NXwyNjA1Njc0MDh8" \
"OTQ4NzYzNDEzOHw5NjAwOHwxNDY1ODkwMDQwNjcyfDE0NjU5MDQ0NDA2NzJ8ZmFsc" \
"2V8ZmFsc2VMy19X48EHADgAAAA4AAAAAgAAAEUAADSeY0AAQAYAAH8AAAF/AAAByB" \
"cQk63BfguU6yN0gBA3if4oAAABAQgKOm9dzzpvXc9Oy19X0cEHAD4AAAA+AAAAAgA" \
"AAEUAADqwakAAQAYAAH8AAAF/AAAByBcQk63BfguU6yN0gBg3if4uAAABAQgKOm9l" \
"mDpvXc9f5QAAAABOy19XAMIHADgAAAA4AAAAAgAAAEUAADTafEAAQAYAAH8AAAF/A" \
"AABEJPIF5TrI3StwX4RgBAvPf4oAAABAQgKOm9lmDpvZZhOy19XOsMHAD4AAAA+AA" \
"AAAgAAAEUAADr8H0AAQAYAAH8AAAF/AAABEJPIF5TrI3StwX4RgBgvPf4uAAABAQg" \
"KOm9lmDpvZZgAXwAAAGJOy19XWMMHADgAAAA4AAAAAgAAAEUAADTBDEAAQAYAAH8A" \
"AAF/AAAByBcQk63BfhGU6yN6gBA3iP4oAAABAQgKOm9lmDpvZZhOy19XYcMHAJoAA" \
"ACaAAAAAgAAAEUAAJbB+kAAQAYAAH8AAAF/AAABEJPIF5TrI3qtwX4RgBgvPf6KAA" \
"ABAQgKOm9lmDpvZZg2OHwxOTR8MTMwNzczfDB8MnwwfDB8MHw0NTE5NTY1fDI2MDU" \
"2NzQwOHw5NDg3NjM0MTM4fDk2MDA4fDE0NjU4OTAwNDA2NzJ8MTQ2NTkwNDQ0MDY3" \
"MnxmYWxzZXxmYWxzZU7LX1dnwwcAOAAAADgAAAACAAAARQAANHkkQABABgAAfwAAA" \
"X8AAAHIFxCTrcF+EZTrI9yAEDeF/igAAAEBCAo6b2WYOm9lmE/LX1frjwoALQAAAC" \
"0AAAACAAAARQAAKRGCAABAEQAAfwAAAX8AAAHOzxYzABX+KEIBlnNi2rR0ZXN0wQJ" \
"Py19XAJQKAF4AAABeAAAAAgAAAEUAAFpVsAAAQBEAAH8AAAF/AAABFjPOzwBG/lli" \
"RZZzYtrAIR6RAv9UeXBlOiAwIChDT04pCkNvZGU6IDEgKEdFVCkKTUlEOiAzODUxN" \
"QpUb2tlbjogNjJkYVDLX1c2wwcAPgAAAD4AAAACAAAARQAAOryDQABABgAAfwAAAX" \
"8AAAHIFxCTrcF+EZTrI9yAGDeF/i4AAAEBCAo6b21hOm9lmF/lAAAAAFDLX1dfwwc" \
"AOAAAADgAAAACAAAARQAANNirQABABgAAfwAAAX8AAAEQk8gXlOsj3K3BfheAEC89" \
"/igAAAEBCAo6b21hOm9tYVDLX1dnxAcAPgAAAD4AAAACAAAARQAAOt5aQABABgAAf" \
"wAAAX8AAAEQk8gXlOsj3K3BfheAGC89/i4AAAEBCAo6b21hOm9tYQBfAAAAYlDLX1" \
"eIxAcAOAAAADgAAAACAAAARQAANKP+QABABgAAfwAAAX8AAAHIFxCTrcF+F5TrI+K" \
"AEDeF/igAAAEBCAo6b21hOm9tYVDLX1eRxAcAmgAAAJoAAAACAAAARQAAltRAQABA" \
"BgAAfwAAAX8AAAEQk8gXlOsj4q3BfheAGC89/ooAAAEBCAo6b21hOm9tYTY4fDE5N" \
"HwxMzA3NzN8MHwyfDB8MHwwfDQ1MTk1NjV8MjYwNTY3NDA4fDk0ODc2MzQxMzh8OT" \
"YwMDh8MTQ2NTg5MDA0MDY3MnwxNDY1OTA0NDQwNjcyfGZhbHNlfGZhbHNlUMtfV5z" \
"EBwA4AAAAOAAAAAIAAABFAAA0OFNAAEAGAAB/AAABfwAAAcgXEJOtwX4XlOskRIAQ" \
"N4L+KAAAAQEICjpvbWE6b21hUctfVxgFAwAuAAAALgAAAAIAAABFAAAqIpAAAEARAA" \
"B/AAABfwAAAc7PFjMAFv4pQgOWdGLatHRlc3T/OThRy19XeQcDACYAAAAmAAAAAgAA" \
"AEUAACL9+AAAQBEAAH8AAAF/AAABFjPOzwAO/iFiRJZ0YtpSy19XIcYHAD4AAAA+AA" \
"AAAgAAAEUAADr36kAAQAYAAH8AAAF/AAAByBcQk63BfheU6yREgBg3gv4uAAABAQgK" \
"Om91JDpvbWFf5QAAAABSy19XTMYHADgAAAA4AAAAAgAAAEUAADR4IkAAQAYAAH8AAA" \
"F/AAABEJPIF5TrJEStwX4dgBAvPf4oAAABAQgKOm91JDpvdSRSy19XYMcHAD4AAAA+" \
"AAAAAgAAAEUAADq+7EAAQAYAAH8AAAF/AAABEJPIF5TrJEStwX4dgBgvPf4uAAABAQ" \
"gKOm91JDpvdSQAXwAAAGJSy19XdscHADgAAAA4AAAAAgAAAEUAADQq8UAAQAYAAH8A" \
"AAF/AAAByBcQk63Bfh2U6yRKgBA3gv4oAAABAQgKOm91JDpvdSRSy19XgMcHAJoAAA" \
"CaAAAAAgAAAEUAAJaJ2EAAQAYAAH8AAAF/AAABEJPIF5TrJEqtwX4dgBgvPf6KAAAB" \
"AQgKOm91JDpvdSQ2OHwxOTR8MTMwNzczfDB8MnwwfDB8MHw0NTE5NTY1fDI2MDU2Nz" \
"QwOHw5NDg3NjM0MTM4fDk2MDA4fDE0NjU4OTAwNDA2NzJ8MTQ2NTkwNDQ0MDY3Mnxm" \
"YWxzZXxmYWxzZVLLX1eGxwcAOAAAADgAAAACAAAARQAANDU3QABABgAAfwAAAX8AAA" \
"HIFxCTrcF+HZTrJKyAEDd//igAAAEBCAo6b3UkOm91JA=="
PCAP_COAP_GET_OVER_TUN_INTERFACE_base64 = "1MOyoQIABAAAAAAAAAAAAMgAAABlAAAAqgl9WK8aBgA7AAAAOwAAAGADPxUAExFAu7sAAAAAAA" \
"AAAAAAAAAAAbu7AAAAAAAAAAAAAAAAAALXvBYzABNZUEABcGO0dGVzdMECqgl9WMcaBgCQAAAA" \
"kAAAAGAAAAAAaDr//oAAAAAAAAAAAAAAAAAAA7u7AAAAAAAAAAAAAAAAAAGJAAcTAAAAALu7AA" \
"AAAAAAAAAAAAAAAAK7uwAAAAAAAAAAAAAAAAACBAgAAAAAAABgAz8VABMRQLu7AAAAAAAAAAAA" \
"AAAAAAG7uwAAAAAAAAAAAAAAAAAC17wWMwATWVBAAXBjtHRlc6oJfVjSGgYAOwAAADsAAABgAz" \
"8VABMRP7u7AAAAAAAAAAAAAAAAAAG7uwAAAAAAAAAAAAAAAAAC17wWMwATWVBAAXBjtHRlc3TB" \
"Ag=="
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment