Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
2319 lines (1694 sloc) 81.3 KB
import random
import string
import struct
import socket
import json
import yaml
import binascii
import base64
import inspect
import copy
import collections
from scapy.all import *
from ..utils.common import *
from ..common.trex_types import *
from ..common.trex_exceptions import TRexError
from .trex_stl_packet_builder_interface import CTrexPktBuilderInterface
class CTRexPacketBuildException(Exception):
"""
This is the general Packet Building error exception class.
"""
def __init__(self, code, message):
self.code = code
self.message = message
def __str__(self):
return self.__repr__()
def __repr__(self):
return u"[errcode:%r] %r" % (self.code, self.message)
################################################################################################
class CTRexScriptsBase(object):
"""
VM Script base class
"""
def clone (self):
return copy.deepcopy(self)
class CTRexScFieldRangeBase(CTRexScriptsBase):
FILED_TYPES = ['inc', 'dec', 'rand']
def __init__(self, field_name,
field_type
):
super(CTRexScFieldRangeBase, self).__init__()
self.field_name =field_name
self.field_type =field_type
if self.field_type not in CTRexScFieldRangeBase.FILED_TYPES :
raise CTRexPacketBuildException(-12, 'Field type should be in %s' % FILED_TYPES);
class CTRexScFieldRangeValue(CTRexScFieldRangeBase):
"""
Range of field values
"""
def __init__(self, field_name,
field_type,
min_value,
max_value
):
super(CTRexScFieldRangeValue, self).__init__(field_name,field_type)
self.min_value =min_value;
self.max_value =max_value;
if min_value > max_value:
raise CTRexPacketBuildException(-12, 'Invalid range: min is greater than max.');
if min_value == max_value:
raise CTRexPacketBuildException(-13, "Invalid range: min value is equal to max value.");
class CTRexScIpv4SimpleRange(CTRexScFieldRangeBase):
"""
Range of ipv4 ip
"""
def __init__(self, field_name, field_type, min_ip, max_ip):
super(CTRexScIpv4SimpleRange, self).__init__(field_name,field_type)
self.min_ip = min_ip
self.max_ip = max_ip
mmin=ipv4_str_to_num (is_valid_ipv4_ret(min_ip))
mmax=ipv4_str_to_num (is_valid_ipv4_ret(max_ip))
if mmin > mmax :
raise CTRexPacketBuildException(-11, 'CTRexScIpv4SimpleRange m_min ip is bigger than max');
class CTRexScIpv4TupleGen(CTRexScriptsBase):
"""
Range tuple
"""
FLAGS_ULIMIT_FLOWS =1
def __init__(self, min_ipv4, max_ipv4, num_flows=100000, min_port=1025, max_port=65535, flags=0):
super(CTRexScIpv4TupleGen, self).__init__()
self.min_ip = min_ipv4
self.max_ip = max_ipv4
mmin=ipv4_str_to_num (is_valid_ipv4_ret(min_ipv4))
mmax=ipv4_str_to_num (is_valid_ipv4_ret(max_ipv4))
if mmin > mmax :
raise CTRexPacketBuildException(-11, 'CTRexScIpv4SimpleRange m_min ip is bigger than max');
self.num_flows=num_flows;
self.min_port =min_port
self.max_port =max_port
self.flags = flags
class CTRexScTrimPacketSize(CTRexScriptsBase):
"""
Trim packet size. Field type is CTRexScFieldRangeBase.FILED_TYPES = ["inc","dec","rand"]
"""
def __init__(self,field_type="rand",min_pkt_size=None, max_pkt_size=None):
super(CTRexScTrimPacketSize, self).__init__()
self.field_type = field_type
self.min_pkt_size = min_pkt_size
self.max_pkt_size = max_pkt_size
if max_pkt_size != None and min_pkt_size !=None :
if min_pkt_size == max_pkt_size:
raise CTRexPacketBuildException(-11, 'CTRexScTrimPacketSize min_pkt_size is the same as max_pkt_size ');
if min_pkt_size > max_pkt_size:
raise CTRexPacketBuildException(-11, 'CTRexScTrimPacketSize min_pkt_size is bigger than max_pkt_size ');
class STLScVmRaw(CTRexScriptsBase):
"""
Raw instructions
"""
def __init__(self,list_of_commands=None, cache_size=None):
"""
Include a list of a basic instructions objects.
:parameters:
list_of_commands : list
list of instructions
cache_size : uint16_t
In case it is bigger than zero, FE results will be cached - this will speedup of the program at the cost of limiting the number of possible packets to the number of cache. The cache size is limited to the pool size
The following example splits the generated traffic by "ip_src" variable.
.. code-block:: python
# Split by
# TCP SYN
base_pkt = Ether()/IP(dst="48.0.0.1")/TCP(dport=80,flags="S")
# vm
vm = STLScVmRaw( [ STLVmFlowVar(name="ip_src",
min_value="16.0.0.0",
max_value="16.0.0.254",
size=4, op="inc"),
STLVmWrFlowVar(fv_name="ip_src", pkt_offset= "IP.src" ),
STLVmFixIpv4(offset = "IP"), # fix checksum
]
cache_size = 1000
)
"""
super(STLScVmRaw, self).__init__()
self.cache_size = cache_size
if list_of_commands==None:
self.commands =[]
else:
self.commands = list_of_commands
def add_cmd (self,cmd):
self.commands.append(cmd)
################################################################################################
# VM raw instructions
################################################################################################
class CTRexVmInsBase(object):
"""
Instruction base
"""
def __init__(self, ins_type):
self.type = ins_type
validate_type('ins_type', ins_type, basestring)
class CTRexVmInsFixIpv4(CTRexVmInsBase):
def __init__(self, offset):
super(CTRexVmInsFixIpv4, self).__init__("fix_checksum_ipv4")
self.pkt_offset = offset
validate_type('offset', offset, int)
class CTRexVmInsFixHwCs(CTRexVmInsBase):
L4_TYPE_UDP = 11
L4_TYPE_TCP = 13
L4_TYPE_IP = 17
def __init__(self, l2_len,l3_len,l4_type):
super(CTRexVmInsFixHwCs, self).__init__("fix_checksum_hw")
self.l2_len = l2_len
validate_type('l2_len', l2_len, int)
self.l3_len = l3_len
validate_type('l3_len', l3_len, int)
self.l4_type = l4_type
validate_type('l4_type', l4_type, int)
class CTRexVmInsFlowVar(CTRexVmInsBase):
#TBD add more validation tests
OPERATIONS =['inc', 'dec', 'random']
VALID_SIZES =[1, 2, 4, 8]
def __init__(self, fv_name, size, op, init_value, min_value, max_value,step, value_list, split_to_cores, next_var):
super(CTRexVmInsFlowVar, self).__init__("flow_var")
self.name = fv_name
validate_type('fv_name', fv_name, basestring)
self.size = size
self.op = op
if next_var is not None:
validate_type('next_var', next_var, basestring)
if op == 'random':
raise CTRexPacketBuildException(-11,"If next_var is defined then op can't be random. Check %s " % fv_name)
if next_var == self.name:
raise CTRexPacketBuildException(-11,"Self loops are forbidden.")
self.next_var = next_var
validate_type('split_to_cores', split_to_cores, bool)
self.split_to_cores = split_to_cores
if value_list == None:
self.init_value = init_value
validate_type('init_value', init_value, int)
assert init_value >= 0, 'init_value (%s) is negative' % init_value
assert init_value <= max_value and init_value >= min_value, 'init_value (%s) must be between min_value (%s) and max_value (%s)' % (init_value, min_value, max_value)
self.min_value = min_value
validate_type('min_value', min_value, int)
assert min_value >= 0, 'min_value (%s) is negative' % min_value
self.max_value = max_value
validate_type('max_value', max_value, int)
assert max_value >= 0, 'max_value (%s) is negative' % max_value
else:
self.value_list = value_list
validate_type('value_list', value_list, list)
for value in value_list:
assert value >= 0, 'value_list (%s) is negative' % value
self.step=step
validate_type('step', step, int)
assert step > 0, 'step (%s) is equals 0 or negative' % step
class CTRexVmInsFlowVarRandLimit(CTRexVmInsBase):
#TBD add more validation tests
VALID_SIZES =[1, 2, 4, 8]
def __init__(self, fv_name, size, limit, seed, min_value, max_value, split_to_cores, next_var):
super(CTRexVmInsFlowVarRandLimit, self).__init__("flow_var_rand_limit")
self.name = fv_name;
validate_type('fv_name', fv_name, basestring)
self.size = size
self.limit=limit
validate_type('limit', limit, int)
assert limit > 0, 'limit (%s) is equals 0 or negative' % limit
self.seed=seed
validate_type('seed', seed, int)
self.min_value=min_value
validate_type('min_value', min_value, int)
assert min_value >= 0, 'min_value (%s) is negative' % min_value
self.max_value=max_value
validate_type('max_value', max_value, int)
assert max_value >= 0, 'max_value (%s) is negative' % max_value
validate_type('split_to_cores', split_to_cores, bool)
self.split_to_cores = split_to_cores
if next_var is not None:
validate_type('next_var', next_var, [basestring])
if next_var == self.name:
raise CTRexPacketBuildException(-11,"Self loops are forbidden.")
self.next_var = next_var
class CTRexVmInsWrFlowVar(CTRexVmInsBase):
def __init__(self, fv_name, pkt_offset, add_value=0, is_big_endian=True):
super(CTRexVmInsWrFlowVar, self).__init__("write_flow_var")
self.name = fv_name
validate_type('fv_name', fv_name, basestring)
self.pkt_offset = pkt_offset
validate_type('pkt_offset', pkt_offset, int)
self.add_value = add_value
validate_type('add_value', add_value, int)
self.is_big_endian = is_big_endian
validate_type('is_big_endian', is_big_endian, bool)
class CTRexVmInsWrMaskFlowVar(CTRexVmInsBase):
def __init__(self, fv_name, pkt_offset,pkt_cast_size,mask,shift,add_value, is_big_endian=True):
super(CTRexVmInsWrMaskFlowVar, self).__init__("write_mask_flow_var")
self.name = fv_name
validate_type('fv_name', fv_name, basestring)
self.pkt_offset = pkt_offset
validate_type('pkt_offset', pkt_offset, int)
self.pkt_cast_size = pkt_cast_size
validate_type('pkt_cast_size', pkt_cast_size, int)
self.mask = mask
validate_type('mask', mask, int)
self.shift = shift
validate_type('shift', shift, int)
self.add_value =add_value
validate_type('add_value', add_value, int)
self.is_big_endian = is_big_endian
validate_type('is_big_endian', is_big_endian, bool)
class CTRexVmInsTrimPktSize(CTRexVmInsBase):
def __init__(self,fv_name):
super(CTRexVmInsTrimPktSize, self).__init__("trim_pkt_size")
self.name = fv_name
validate_type('fv_name', fv_name, basestring)
class CTRexVmInsTupleGen(CTRexVmInsBase):
def __init__(self, fv_name, ip_min, ip_max, port_min, port_max, limit_flows, flags=0):
super(CTRexVmInsTupleGen, self).__init__("tuple_flow_var")
self.name =fv_name
validate_type('fv_name', fv_name, basestring)
self.ip_min = ip_min;
self.ip_max = ip_max;
self.port_min = port_min;
self.port_max = port_max;
self.limit_flows = limit_flows;
self.flags =flags;
################################################################################################
#
class CTRexVmEngine(object):
def __init__(self):
"""
Inlcude list of instructions.
"""
super(CTRexVmEngine, self).__init__()
self.ins=[]
self.cache_size = 0
# return as json
def get_json (self):
inst_array = [];
# dump it as dict
for obj in self.ins:
inst_array.append(obj.__dict__);
d={'instructions': inst_array};
if self.cache_size >0 :
d['cache']=self.cache_size
return d
def add_ins (self,ins):
#assert issubclass(ins, CTRexVmInsBase)
self.ins.append(ins);
def dump (self):
cnt=0;
for obj in self.ins:
print("ins",cnt)
cnt = cnt +1
print(obj.__dict__)
def dump_bjson (self):
print(json.dumps(self.get_json(), sort_keys=True, indent=4))
def dump_as_yaml (self):
print(yaml.dump(self.get_json(), default_flow_style=False))
################################################################################################
class CTRexScapyPktUtl(object):
def __init__(self, scapy_pkt):
assert isinstance(scapy_pkt, Packet)
self.pkt = scapy_pkt
def pkt_iter (self):
p=self.pkt;
while True:
yield p
p=p.payload
if p ==None or isinstance(p,NoPayload):
break;
def get_list_iter(self):
l=list(self.pkt_iter())
return l
def get_pkt_layers(self):
"""
Return string 'IP:UDP:TCP'
"""
l=self.get_list_iter ();
l1=map(lambda p: p.name,l );
return ":".join(l1);
def _layer_offset(self, name, cnt = 0):
"""
Return offset of layer. Example: 'IP',1 returns offfset of layer ip:1
"""
save_cnt=cnt
for pkt in self.pkt_iter ():
if name in (pkt.name, pkt.__class__.__name__):
if cnt==0:
return (pkt, pkt._offset)
else:
cnt=cnt -1
raise CTRexPacketBuildException(-11,("no layer %s-%d" % (name, save_cnt)));
def layer_offset(self, name, cnt = 0):
"""
Return offset of layer. Example: 'IP',1 returns offfset of layer ip:1
"""
save_cnt=cnt
for pkt in self.pkt_iter ():
if pkt.name == name:
if cnt==0:
return pkt._offset
else:
cnt=cnt -1
raise CTRexPacketBuildException(-11,("no layer %s-%d" % (name, save_cnt)));
def get_field_offet(self, layer, layer_cnt, field_name):
"""
Return offset of layer. Example: 'IP',1 returns offfset of layer ip:1
"""
t=self._layer_offset(layer,layer_cnt);
l_offset=t[1];
layer_pkt=t[0]
#layer_pkt.dump_fields_offsets ()
for f in layer_pkt.fields_desc:
if f.name == field_name:
return (l_offset+f._offset,f.get_size_bytes ());
raise CTRexPacketBuildException(-11, "No layer %s-%d." % (field_name, layer_cnt))
def get_field_by_offset(self, offset):
'''
Try to convert numeric offset to layer name, layer index, field name.
The opposite of get_field_offet()
Return None if not successful.
'''
offset_int = int(offset)
assert offset_int == offset
assert offset_int >= 0
# look for layer
layer_cnt = {}
for pkt in self.pkt_iter():
layer_name = pkt.__class__.__name__
if pkt._offset > offset_int:
break
if layer_name in layer_cnt:
layer_cnt[layer_name] += 1
else:
layer_cnt[layer_name] = 0
last_name = layer_name
last_count = layer_cnt[layer_name]
last_layer = pkt
# look for field inside layer
for f in last_layer.fields_desc:
if last_layer._offset + f._offset == offset_int:
return (last_name, last_count, f.name)
def get_layer_offet_by_str(self, layer_des):
"""
Return layer offset by string.
:parameters:
IP:0
IP:1
return offset
"""
l1=layer_des.split(":")
layer=""
layer_cnt=0;
if len(l1)==1:
layer=l1[0];
else:
layer=l1[0];
layer_cnt=int(l1[1]);
return self.layer_offset(layer, layer_cnt)
def get_field_offet_by_str(self, field_des):
"""
Return field_des (offset,size) layer:cnt.field
Example:
802|1Q.vlan get 802.1Q->valn replace | with .
IP.src
IP:0.src (first IP.src like IP.src)
Example: IP:1.src for internal IP
Return (offset, size) as tuple.
"""
s=field_des.split(".");
if len(s)!=2:
raise CTRexPacketBuildException(-11, ("Field desription should be layer:cnt.field Example: IP.src or IP:1.src"));
layer_ex = s[0].replace("|",".")
field = s[1]
l1=layer_ex.split(":")
layer=""
layer_cnt=0;
if len(l1)==1:
layer=l1[0];
else:
layer=l1[0];
layer_cnt=int(l1[1]);
return self.get_field_offet(layer,layer_cnt,field)
def has_IPv4 (self):
return self.pkt.has_layer("IP");
def has_IPv6 (self):
return self.pkt.has_layer("IPv6");
def has_UDP (self):
return self.pkt.has_layer("UDP");
################################################################################################
class CTRexVmDescBase(object):
"""
Instruction base
"""
def __init__(self):
pass;
def get_obj(self):
return self;
def get_json(self):
return self.get_obj().__dict__
def dump_bjson(self):
print(json.dumps(self.get_json(), sort_keys=True, indent=4))
def dump_as_yaml(self):
print(yaml.dump(self.get_json(), default_flow_style=False))
def get_var_ref (self):
'''
Virtual function returns a ref var name.
'''
return None
def get_var_name(self):
'''
Virtual function returns the varible name if it exists.
'''
return None
def get_next_var_name(self):
'''
Virtual function that returns the name of the next variable name if it exists.
'''
return None
def compile(self,parent):
'''
Virtual function to take parent that has function name_to_offset.
'''
pass
def valid_fv_size (size):
if size not in CTRexVmInsFlowVar.VALID_SIZES:
raise CTRexPacketBuildException(-11,("Flow var has invalid size %d ") % size );
def valid_fv_ops (op):
if op not in CTRexVmInsFlowVar.OPERATIONS:
raise CTRexPacketBuildException(-11,("Flow var has invalid op %s ") % op );
def get_max_by_size (size):
d={
1:((1<<8) -1),
2:((1<<16)-1),
4:((1<<32)-1),
8:0xffffffffffffffff
};
return d[size]
def convert_val (val):
if is_integer(val):
return val
if type(val) == str:
return ipv4_str_to_num (is_valid_ipv4_ret(val))
raise CTRexPacketBuildException(-11,("init val invalid %s ") % val );
def convert_val_list(val):
if not isinstance(val, (list,)):
raise CTRexPacketBuildException(-11, ("init val invalid(not list) %s ") % val);
for index in range(0,len(val)):
if is_integer(val[index]):
continue
elif type(val[index]) == str:
val[index] = ipv4_str_to_num (is_valid_ipv4_ret(val[index]))
else:
raise CTRexPacketBuildException(-11, ("init val[%s] invalid %s ") % (index, val[index]));
return val
def check_for_int (val):
validate_type('val', val, int)
def valid_fv_var_sizes(size, vars):
for val in vars:
if val > get_max_by_size(size):
raise CTRexPacketBuildException(-11, ("requested value %d with size %d is bigger than %s") % (val, size, hex(get_max_by_size(size))));
def valid_fv_init(init_value, min_value, max_value):
if init_value > max_value or init_value < min_value:
raise CTRexPacketBuildException(-11, ("init_value must be between min_value %d and max_value %d" % (min_value, max_value)));
def valid_fv_min_max(min_value, max_value):
if min_value > max_value :
raise CTRexPacketBuildException(-11, ("max %d is lower than min %d ") % (max_value, min_value));
def valid_fv_cycle(min_value, max_value, step, op):
pad = (max_value - min_value + 1) % step
if pad != 0:
step_pad = step - pad
if op == 'inc':
if (get_max_by_size(8) - max_value) < step_pad:
raise CTRexPacketBuildException(-11,("could not pad range to be a true cycle - '(max_value - min_value + 1) % step' should be zero"));
elif op == 'dec':
if min_value < step_pad:
raise CTRexPacketBuildException(-11,("could not pad range to be a true cycle - '(max_value - min_value + 1) % step' should be zero"));
class STLVmFlowVar(CTRexVmDescBase):
def __init__(self, name, init_value=None, min_value=0, max_value=255, size=4, step=1, op="inc", value_list=None, split_to_cores=True, next_var=None):
"""
Flow variable instruction. Allocates a variable on a stream context. The size argument determines the variable size.
The operation can be inc, dec, and random.
For increment and decrement operations, can set the "step" size.
For all operations, can set initialization value, minimum and maximum value.
:parameters:
name : string
Name of the stream variable
init_value : int
Init value of the variable. If not specified, it will be min_value
min_value : int
Min value
max_value : int
Max value
size : int
Number of bytes of the variable. Possible values: 1,2,4,8 for uint8_t, uint16_t, uint32_t, uint64_t
step : int
Step in case of "inc" or "dec" operations
op : string
Possible values: "inc", "dec", "random"
value_list : int
List of values instead of init_value, min_value and max_value
split_to_cores : bool
Default value is True. If the value is True then each core updates the variable by step * num of cores.
If split_to_cores = False then each core updates the variable by step.
next_var : string or None
In case it isn't None it is the name of the next variable. The next variable will perform its operation only when this variable
completes a full wrap around.
.. code-block:: python
# Example1
# input
STLVmFlowVar(min_value=0, max_value=3, size=1,op="inc")
# output 0,1,2,3,0,1,2,3 ..
# input
STLVmFlowVar(min_value=0, max_value=3, size=1,op="dec")
# output 0,3,2,1,0,3,2,1 ..
# input
STLVmFlowVar(min_value=0, max_value=3, size=1,op="random")
# output 1,1,2,3,1,2,1,0 ..
# input
STLVmFlowVar(min_value=0, max_value=10, size=1,op="inc",step=3)
# output 0,3,6,9,0,3,6,9,0..
# Example2
# input value_list
STLVmFlowVar(value_list=["16.0.0.1","16.0.0.2","16.0.0.3","16.0.0.4"], op="inc"))
# output 268435457,268435458,268435459,268435460,268435457,268435458,268435459,268435460,268435457..
# input value_list
STLVmFlowVar(value_list=[9,0,4,7,5], op="dec"))
# output 5,7,4,0,9,5,7,4,0,9,5,7..
"""
super(STLVmFlowVar, self).__init__()
self.name = name
validate_type('name', name, basestring)
self.size =size
valid_fv_size(size)
self.op =op
valid_fv_ops (op)
if next_var is not None:
validate_type('next_var', next_var, [basestring])
if op == 'random':
raise CTRexPacketBuildException(-11,"If next_var is defined then op can't be random. Check %s " % self.name)
if next_var == self.name:
raise CTRexPacketBuildException(-11,"Self loops are forbidden.")
self.next_var = next_var
self.previous = None
validate_type('split_to_cores', split_to_cores, bool)
self.split_to_cores = split_to_cores
self.step = convert_val (step)
if self.step <= 0:
raise CTRexPacketBuildException(-11,("step %d must be more than 0") % (self.step))
if value_list == None:
# choose default value for init val
if init_value == None :
init_value = max_value if op == "dec" else min_value
self.init_value = convert_val(init_value)
self.min_value = convert_val(min_value)
self.max_value = convert_val(max_value)
self.value_list = None
valid_fv_var_sizes(self.size, [self.min_value, self.max_value, self.init_value, self.step])
valid_fv_min_max(self.min_value, self.max_value)
valid_fv_init(self.init_value, self.min_value, self.max_value)
valid_fv_cycle(self.min_value, self.max_value, self.step, self.op)
else:
if len(value_list) > get_max_by_size(2):
raise CTRexPacketBuildException(-11,("value_list size %d bigger than %d") % (len(value_list), get_max_by_size(2)));
self.init_value = None
self.min_value = None
self.max_value = None
self.value_list = convert_val_list(value_list)
valid_fv_var_sizes(self.size, self.value_list)
def get_obj (self):
return CTRexVmInsFlowVar(self.name,self.size,self.op,self.init_value,self.min_value,self.max_value,self.step,self.value_list,
self.split_to_cores, self.next_var)
def get_var_name(self):
return [self.name]
def get_next_var_name(self):
return self.next_var
def get_previous_var_name(self):
return self.previous
class STLVmFlowVarRepeatableRandom(CTRexVmDescBase):
def __init__(self, name, size=4, limit=100, seed=None, min_value=0, max_value=None, split_to_cores=True, next_var=None):
"""
Flow variable instruction for repeatable random with limit number of generating numbers. Allocates memory on a stream context.
The size argument determines the variable size. Could be 1,2,4 or 8
1. The maximum number of distinct values will 'limit'. There could be a case of repetition
2. The values will be repeated after 'limit' number of values.
:parameters:
name : string
Name of the stream variable
size : int
Number of bytes of the variable. Possible values: 1,2,4,8 for uint8_t, uint16_t, uint32_t, uint64_t
limit : int
The number of distinct repetable random number
seed : int
For deterministic result, you can set this to a uint16_t number
min_value : int
Min value
max_value : int
Max value
split_to_cores : bool
Default value is True. If the value is True then each core updates the variable by step * num of cores.
If split_to_cores = False then each core updates the variable by step.
next_var : string or None
In case it isn't None it is the name of the next variable. The next variable will perform its operation only when this variable
completes a full wrap around.
.. code-block:: python
# Example1
# input , 1 byte or random with limit of 5
STLVmFlowVarRepeatableRandom("var1",size=1,limit=5)
# output 255,1,7,129,8, ==> repeat 255,1,7,129,8
STLVmFlowVarRepeatableRandom("var1",size=4,limit=100,min_value=0x12345678, max_value=0x32345678)
"""
super(STLVmFlowVarRepeatableRandom, self).__init__()
self.name = name;
validate_type('name', name, basestring)
self.size =size
valid_fv_size(size)
self.limit =limit
validate_type('split_to_cores', split_to_cores, bool)
self.split_to_cores = split_to_cores
if next_var is not None:
validate_type('next_var', next_var, [basestring])
if next_var == self.name:
raise CTRexPacketBuildException(-11,"Self loops are forbidden.")
self.next_var = next_var
self.previous = None
if seed == None:
self.seed = random.randint(1, 32000)
else:
self.seed = seed
if min_value == None:
self.min_value = convert_val (0);
else:
self.min_value = convert_val (min_value);
if max_value == None :
self.max_value = get_max_by_size (self.size)
else:
self.max_value = convert_val (max_value)
valid_fv_var_sizes(self.size, [self.min_value, self.max_value])
valid_fv_min_max(self.min_value, self.max_value)
if self.limit <= 0:
raise CTRexPacketBuildException(-11,("limit %d must be more than 0") % (self.limit));
def get_obj (self):
return CTRexVmInsFlowVarRandLimit(self.name, self.size, self.limit, self.seed, self.min_value, self.max_value, self.split_to_cores, self.next_var);
def get_var_name(self):
return [self.name]
def get_next_var_name(self):
return self.next_var
def get_previous_var_name(self):
return self.previous
class STLVmFixChecksumHw(CTRexVmDescBase):
def __init__(self, l3_offset,l4_offset,l4_type):
"""
Fix IPv4 header checksum and/or TCP/UDP checksum using hardware assist.
Use this if the packet header has changed or data payload has changed as it is necessary to fix the checksums.
This instruction works on NICS that support this hardware offload.
For fixing only IPv4 header checksum use STLVmFixIpv4. This instruction should be used if both L4 and L3 need to be fixed.
example for supported packets
Ether()/IPv4
SomeTunnel()/IPv4
Ether()/(IPv4|IPv6)/(UDP|TCP)
Ether()/(IPv4|IPv6)/(UDP|TCP)
SomeTunnel()/(IPv4|IPv6)/(UDP|TCP)
SomeTunnel()/(IPv4|IPv6)/(UDP|TCP)
:parameters:
l3_offset : offset in bytes
**IPv4/IPv6 header** offset from packet start. It is **not** the offset of the checksum field itself.
in could be string in case of scapy packet. format IP[:[id]]
l4_offset : offset in bytes to UDP/TCP header or IPv4 payload. in case of IPv4 checksum CTRexVmInsFixHwCs.L4_TYPE_IP could be set to zero to be auto calculated by the server
l4_type : [CTRexVmInsFixHwCs.L4_TYPE_UDP or CTRexVmInsFixHwCs.L4_TYPE_TCP or CTRexVmInsFixHwCs.L4_TYPE_IP]
see full example stl/syn_attack_fix_cs_hw.py
.. code-block:: python
# Example2
pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)
# by offset
STLVmFixChecksumHw(l3_offset=14,l4_offset=14+20,l4_type=CTRexVmInsFixHwCs.L4_TYPE_UDP)
# in case of scapy packet can be defined by header name
STLVmFixChecksumHw(l3_offset="IP",l4_offset="UDP",l4_type=CTRexVmInsFixHwCs.L4_TYPE_UDP)
# string for second "IP" header in the packet is IP:1
STLVmFixChecksumHw(offset="IP:1")
"""
super(STLVmFixChecksumHw, self).__init__()
self.l3_offset = l3_offset; # could be a name of offset
self.l4_offset = l4_offset; # could be a name of offset
self.l4_type = l4_type
self.l2_len = 0
def get_obj (self):
return CTRexVmInsFixHwCs(self.l2_len,self.l3_len,self.l4_type);
def compile(self,parent):
if type(self.l3_offset)==str:
self.l2_len = parent._pkt_layer_offset(self.l3_offset);
else:
self.l2_len = self.l3_offset
if type(self.l4_offset)==str:
self.l4_offset = parent._pkt_layer_offset(self.l4_offset);
if (self.l4_type != CTRexVmInsFixHwCs.L4_TYPE_IP):
assert self.l4_offset >= self.l2_len+8, 'l4_offset should be higher than l3_offset offset'
self.l3_len = self.l4_offset - self.l2_len;
else:
if self.l4_offset!=0:
assert self.l4_offset >= self.l2_len+8, 'l4_offset should be higher than l3_offset offset'
self.l3_len = self.l4_offset - self.l2_len;
else:
self.l3_len=0
class STLVmFixIpv4(CTRexVmDescBase):
def __init__(self, offset):
"""
Fix IPv4 header checksum. Use this if the packet header has changed and it is necessary to change the checksum.
:parameters:
offset : uint16_t or string
**IPv4 header** offset from packet start. It is **not** the offset of the checksum field itself.
in could be string in case of scapy packet. format IP[:[id]]
.. code-block:: python
# Example2
pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)
# by offset
STLVmFixIpv4(offset=14)
# in case of scapy packet can be defined by header name
STLVmFixIpv4(offset="IP")
# string for second "IP" header in the packet is IP:1
STLVmFixIpv4(offset="IP:1")
"""
super(STLVmFixIpv4, self).__init__()
self.offset = offset; # could be a name of offset
def get_obj (self):
return CTRexVmInsFixIpv4(self.offset);
def compile(self,parent):
if type(self.offset)==str:
self.offset = parent._pkt_layer_offset(self.offset);
class STLVmWrFlowVar(CTRexVmDescBase):
def __init__(self, fv_name, pkt_offset, offset_fixup=0, add_val=0, is_big=True):
"""
Write a stream variable into a packet field.
The write position is determined by the packet offset + offset fixup. The size of the write is determined by the stream variable.
Example: Offset 10, fixup 0, variable size 4. This function writes at 10, 11, 12, and 13.
For inromation about chaning the write size, offset, or fixup, see the `STLVmWrMaskFlowVar` command.
The Field name/offset can be given by name in the following format: ``header[:id].field``.
:parameters:
fv_name : string
Stream variable to write to a packet offset.
pkt_offset : string or int
Name of the field or offset in bytes from packet start.
offset_fixup : int
Number of bytes to move forward. If negative, move backward.
add_value : int
Value to add to the stream variable before writing it to the packet field. Can be used as a constant offset.
is_big : bool
How to write the variable to the the packet. True=big-endian, False=little-endian
.. code-block:: python
# Example3
pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)
# write to ip.src offset
STLVmWrFlowVar (fv_name="tuple", pkt_offset= "IP.src" )
# packet offset is varible
STLVmWrFlowVar (fv_name="tuple", pkt_offset= 26 )
# add l3_len_fix before writing fv_rand into IP.len field
STLVmWrFlowVar(fv_name="fv_rand", pkt_offset= "IP.len", add_val=l3_len_fix)
"""
super(STLVmWrFlowVar, self).__init__()
self.fv_name =fv_name
validate_type('fv_name', fv_name, basestring)
self.offset_fixup =offset_fixup
validate_type('offset_fixup', offset_fixup, int)
self.pkt_offset =pkt_offset
self.add_val =add_val
validate_type('add_val', add_val, int)
self.is_big =is_big;
validate_type('is_big', is_big, bool)
def get_var_ref (self):
return self.fv_name
def get_obj (self):
return CTRexVmInsWrFlowVar(self.fv_name,self.pkt_offset+self.offset_fixup,self.add_val,self.is_big)
def compile(self,parent):
if type(self.pkt_offset)==str:
t=parent._name_to_offset(self.pkt_offset)
self.pkt_offset = t[0]
class STLVmWrMaskFlowVar(CTRexVmDescBase):
def __init__(self, fv_name, pkt_offset, pkt_cast_size=1, mask=0xff, shift=0, add_value=0, offset_fixup=0, is_big=True):
"""
Write a stream variable into a packet field with some operations.
Using this instruction, the variable size and the field can have different sizes.
Pseudocode of this code::
uint32_t val=(cast_to_size)rd_from_variable("name") # read flow-var
val+=m_add_value # add value
if (m_shift>0) { # shift
val=val<<m_shift
}else{
if (m_shift<0) {
val=val>>(-m_shift)
}
}
pkt_val=rd_from_pkt(pkt_offset) # RMW to the packet
pkt_val = (pkt_val & ~m_mask) | (val & m_mask)
wr_to_pkt(pkt_offset,pkt_val)
:parameters:
fv_name : string
The stream variable name to write to a packet field
pkt_cast_size : uint8_t
The size in bytes of the packet field
mask : uint32_t
The mask of the field. 1 means to write. 0 don't care
shift : uint8_t
How many bits to shift
pkt_offset : string or int
the name of the field or offset in byte from packet start.
offset_fixup : int
how many bytes to go forward. In case of a negative value go backward
add_val : int
value to add to stream variable before writing it to packet field. can be used as a constant offset
is_big : bool
how to write the variable to the the packet. is it big-edian or little edian
Example 1 - Cast from uint16_t (var) to uint8_t (pkt)::
base_pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)
vm = STLScVmRaw( [ STLVmFlowVar(name="mac_src",
min_value=1,
max_value=30,
size=2,
op="dec",step=1),
STLVmWrMaskFlowVar(fv_name="mac_src",
pkt_offset= 11,
pkt_cast_size=1,
mask=0xff) # mask command ->write it as one byte
]
)
pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)
Example 2 - Change MSB of uint16_t variable::
vm = STLScVmRaw( [ STLVmFlowVar(name="mac_src",
min_value=1,
max_value=30,
size=2, op="dec",step=1),
STLVmWrMaskFlowVar(fv_name="mac_src",
pkt_offset= 10,
pkt_cast_size=2,
mask=0xff00,
shift=8) # take the var shift it 8 (x256) write only to LSB
]
)
Example 3 - Every 2 packets, change the MAC (shift right)::
vm = STLScVmRaw( [ STLVmFlowVar(name="mac_src",
min_value=1,
max_value=30,
size=2, op="dec",step=1),
STLVmWrMaskFlowVar(fv_name="mac_src",
pkt_offset= 10,
pkt_cast_size=1,
mask=0x1,
shift=-1) # take var mac_src>>1 and write the LSB every two packet there should be a change
]
)
"""
super(STLVmWrMaskFlowVar, self).__init__()
self.fv_name =fv_name
validate_type('fv_name', fv_name, basestring)
self.offset_fixup =offset_fixup
validate_type('offset_fixup', offset_fixup, int)
self.pkt_offset =pkt_offset
self.pkt_cast_size =pkt_cast_size
validate_type('pkt_cast_size', pkt_cast_size, int)
if pkt_cast_size not in [1,2,4]:
raise CTRexPacketBuildException(-10,"not valid cast size");
self.mask = mask
validate_type('mask', mask, int)
self.shift = shift
validate_type('shift', shift, int)
self.add_value = add_value
validate_type('add_value', add_value, int)
self.is_big =is_big;
validate_type('is_big', is_big, bool)
def get_var_ref (self):
return self.fv_name
def get_obj (self):
return CTRexVmInsWrMaskFlowVar(self.fv_name,self.pkt_offset+self.offset_fixup,self.pkt_cast_size,self.mask,self.shift,self.add_value,self.is_big)
def compile(self,parent):
if type(self.pkt_offset)==str:
t=parent._name_to_offset(self.pkt_offset)
self.pkt_offset = t[0]
class STLVmTrimPktSize(CTRexVmDescBase):
def __init__(self,fv_name):
"""
Trim the packet size by the stream variable size. This instruction only changes the total packet size, and does not repair the fields to match the new size.
:parameters:
fv_name : string
Stream variable name. The value of this variable is the new total packet size.
For Example::
def create_stream (self):
# pkt
p_l2 = Ether();
p_l3 = IP(src="16.0.0.1",dst="48.0.0.1")
p_l4 = UDP(dport=12,sport=1025)
pyld_size = max(0, self.max_pkt_size_l3 - len(p_l3/p_l4));
base_pkt = p_l2/p_l3/p_l4/('\x55'*(pyld_size))
l3_len_fix =-(len(p_l2));
l4_len_fix =-(len(p_l2/p_l3));
# vm
vm = STLScVmRaw( [ STLVmFlowVar(name="fv_rand", min_value=64,
max_value=len(base_pkt),
size=2, op="inc"),
STLVmTrimPktSize("fv_rand"), # change total packet size <<<
STLVmWrFlowVar(fv_name="fv_rand",
pkt_offset= "IP.len",
add_val=l3_len_fix), # fix ip len
STLVmFixIpv4(offset = "IP"), # fix checksum
STLVmWrFlowVar(fv_name="fv_rand",
pkt_offset= "UDP.len",
add_val=l4_len_fix) # fix udp len
]
)
pkt = STLPktBuilder(pkt = base_pkt,
vm = vm)
return STLStream(packet = pkt,
mode = STLTXCont())
"""
super(STLVmTrimPktSize, self).__init__()
self.fv_name = fv_name
validate_type('fv_name', fv_name, basestring)
def get_var_ref (self):
return self.fv_name
def get_obj (self):
return CTRexVmInsTrimPktSize(self.fv_name)
class STLVmTupleGen(CTRexVmDescBase):
def __init__(self,name, ip_min="0.0.0.1", ip_max="0.0.0.10", port_min=1025, port_max=65535, limit_flows=100000, flags=0):
"""
Generate a struct with two variables: ``var_name.ip`` as uint32_t and ``var_name.port`` as uint16_t
The variables are dependent. When the ip variable value reaches its maximum, the port is incremented.
For:
* ip_min = 10.0.0.1
* ip_max = 10.0.0.5
* port_min = 1025
* port_max = 1028
* limit_flows = 10
The result:
+------------+------------+-----------+
| ip | port | flow_id |
+============+============+===========+
| 10.0.0.1 | 1025 | 1 |
+------------+------------+-----------+
| 10.0.0.2 | 1025 | 2 |
+------------+------------+-----------+
| 10.0.0.3 | 1025 | 3 |
+------------+------------+-----------+
| 10.0.0.4 | 1025 | 4 |
+------------+------------+-----------+
| 10.0.0.5 | 1025 | 5 |
+------------+------------+-----------+
| 10.0.0.1 | 1026 | 6 |
+------------+------------+-----------+
| 10.0.0.2 | 1026 | 7 |
+------------+------------+-----------+
| 10.0.0.3 | 1026 | 8 |
+------------+------------+-----------+
| 10.0.0.4 | 1026 | 9 |
+------------+------------+-----------+
| 10.0.0.5 | 1026 | 10 |
+------------+------------+-----------+
| 10.0.0.1 | 1025 | 1 |
+------------+------------+-----------+
:parameters:
name : string
Name of the stream struct.
ip_min : string or int
Min value of the ip value. Number or IPv4 format.
ip_max : string or int
Max value of the ip value. Number or IPv4 format.
port_min : int
Min value of port variable.
port_max : int
Max value of port variable.
limit_flows : int
Limit of number of flows.
flags : int
0 - noop
1 - ignore port min and max.
.. code-block:: python
# Example5
def create_stream (self):
# pkt
p_l2 = Ether();
p_l3 = IP(src="16.0.0.1",dst="48.0.0.1")
p_l4 = UDP(dport=12,sport=1025)
pyld_size = max(0, self.max_pkt_size_l3 - len(p_l3/p_l4));
base_pkt = p_l2/p_l3/p_l4/('\x55'*(pyld_size))
l3_len_fix =-(len(p_l2));
l4_len_fix =-(len(p_l2/p_l3));
# vm
vm = STLScVmRaw( [ STLVmFlowVar(name="fv_rand", min_value=64,
max_value=len(base_pkt),
size=2, op="inc"),
STLVmTrimPktSize("fv_rand"), # change total packet size <<<
STLVmWrFlowVar(fv_name="fv_rand",
pkt_offset= "IP.len",
add_val=l3_len_fix), # fix ip len
STLVmFixIpv4(offset = "IP"), # fix checksum
STLVmWrFlowVar(fv_name="fv_rand",
pkt_offset= "UDP.len",
add_val=l4_len_fix) # fix udp len
]
)
pkt = STLPktBuilder(pkt = base_pkt,
vm = vm)
return STLStream(packet = pkt,
mode = STLTXCont())
"""
super(STLVmTupleGen, self).__init__()
self.name = name
validate_type('name', name, basestring)
self.ip_min = convert_val(ip_min);
self.ip_max = convert_val(ip_max);
self.port_min = port_min;
check_for_int (port_min)
self.port_max = port_max;
check_for_int(port_max)
self.limit_flows = limit_flows;
check_for_int(limit_flows)
self.flags =flags;
check_for_int(flags)
def get_var_name(self):
return [self.name+".ip",self.name+".port"]
def get_obj (self):
return CTRexVmInsTupleGen(self.name, self.ip_min, self.ip_max, self.port_min, self.port_max, self.limit_flows, self.flags);
################################################################################################
class STLPktBuilder(CTrexPktBuilderInterface):
def __init__(self, pkt = None, pkt_buffer = None, vm = None, path_relative_to_profile = False, build_raw = False, remove_fcs = True):
"""
This class defines a method for building a template packet and Field Engine using the Scapy package.
Using this class the user can also define how TRex will handle the packet by specifying the Field engine settings.
The pkt can be a Scapy pkt or pcap file name.
If using a pcap file, and path_relative_to_profile is True, then the function loads the pcap file from a path relative to the profile.
.. code-block:: python
# Example6
# packet is scapy
STLPktBuilder( pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)/(10*'x') )
# packet is taken from pcap file relative to python
STLPktBuilder( pkt ="stl/yaml/udp_64B_no_crc.pcap")
# packet is taken from pcap file relative to profile file
STLPktBuilder( pkt ="stl/yaml/udp_64B_no_crc.pcap",
path_relative_to_profile = True )
vm = STLScVmRaw( [ STLVmTupleGen ( ip_min="16.0.0.1", ip_max="16.0.0.2",
port_min=1025, port_max=65535,
name="tuple"), # define tuple gen
STLVmWrFlowVar (fv_name="tuple.ip", pkt_offset= "IP.src" ), # write ip to packet IP.src
STLVmFixIpv4(offset = "IP"), # fix checksum
STLVmWrFlowVar (fv_name="tuple.port", pkt_offset= "UDP.sport" ) #write udp.port
]
)
base_pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)
pad = max(0, size - len(base_pkt)) * 'x'
STLPktBuilder(pkt = base_pkt/pad, vm= vm)
:parameters:
pkt : string,
Scapy object or pcap filename.
pkt_buffer : bytes
Packet as buffer.
vm : list or base on :class:`trex.stl.trex_stl_packet_builder_scapy.STLScVmRaw`
List of instructions to manipulate packet fields.
path_relative_to_profile : bool
If pkt is a pcap file, determines whether to load it relative to profile file.
build_raw : bool
If a buffer is specified (by pkt_buffer), determines whether to build Scapy. Useful in cases where it is necessary to take the offset from Scapy.
remove_fcs : bool
If a buffer is specified (by pkt_buffer), determines whether to remove FCS.
"""
super(STLPktBuilder, self).__init__()
validate_type('pkt', pkt, (type(None), str, Packet))
validate_type('pkt_buffer', pkt_buffer, (type(None), bytes))
self.pkt = None # as input
self.pkt_raw = None # from raw pcap file
self.vm_scripts = [] # list of high level instructions
self.vm_low_level = None
self.is_pkt_built = False
self.metadata=""
self.path_relative_to_profile = path_relative_to_profile
self.remove_fcs = remove_fcs
self.is_binary_source = pkt_buffer != None
if pkt != None and pkt_buffer != None:
raise CTRexPacketBuildException(-15, "Packet builder cannot be provided with both pkt and pkt_buffer.")
# process packet
if pkt != None:
self.set_packet(pkt)
elif pkt_buffer != None:
self.set_pkt_as_str(pkt_buffer)
# process VM
if vm != None:
if not isinstance(vm, (STLScVmRaw, list)):
raise CTRexPacketBuildException(-14, "Bad value for variable vm.")
self.add_command(vm if isinstance(vm, STLScVmRaw) else STLScVmRaw(vm))
# raw source build to see MAC presence/ fields offset by name in VM
if build_raw and self.pkt_raw and not self.pkt:
self.__lazy_build_packet()
# if we have packet and VM - compile now
if (self.pkt or self.pkt_raw) and (self.vm_scripts):
self.compile()
def dump_vm_data_as_yaml(self):
print(yaml.dump(self.get_vm_data(), default_flow_style=False))
def get_vm_data(self):
"""
Dumps the instructions
:parameters:
None
:return:
+ json object of instructions
:raises:
+ :exc:`AssertionError`, in case VM is not compiled (is None).
"""
assert self.vm_low_level is not None, 'vm_low_level is None, please use compile()'
return self.vm_low_level.get_json()
def dump_pkt(self, encode = True):
"""
Dumps the packet as a decimal array of bytes (each item x gets value in range 0-255)
:parameters:
encode : bool
Encode using base64. (disable for debug)
Default: **True**
:return:
+ packet representation as array of bytes
:raises:
+ :exc:`AssertionError`, in case packet is empty.
"""
pkt_buf = self._get_pkt_as_str()
return {'binary': base64.b64encode(pkt_buf).decode() if encode else pkt_buf,
'meta': self.metadata}
def dump_pkt_to_pcap(self, file_path):
wrpcap(file_path, self._get_pkt_as_str())
def add_command (self, script):
self.vm_scripts.append(script.clone());
def dump_scripts (self):
self.vm_low_level.dump_as_yaml()
def dump_as_hex (self):
pkt_buf = self._get_pkt_as_str()
print(hexdump(pkt_buf))
def pkt_layers_desc (self):
"""
Return layer description in this format: IP:TCP:Pyload
"""
pkt_buf = self._get_pkt_as_str()
return self.pkt_layers_desc_from_buffer(pkt_buf)
@staticmethod
def pkt_layers_desc_from_buffer (pkt_buf):
scapy_pkt = Ether(pkt_buf);
pkt_utl = CTRexScapyPktUtl(scapy_pkt);
return pkt_utl.get_pkt_layers()
def set_pkt_as_str (self, pkt_buffer):
validate_type('pkt_buffer', pkt_buffer, bytes)
self.pkt_raw = pkt_buffer
def set_pcap_file (self, pcap_file):
"""
Load raw pcap file into a buffer. Loads only the first packet.
:parameters:
pcap_file : file_name
:raises:
+ :exc:`AssertionError`, if packet is empty.
"""
f_path = self._get_pcap_file_path (pcap_file)
p=RawPcapReader(f_path)
was_set = False
for pkt in p:
was_set=True;
self.pkt_raw = pkt[0]
break
if not was_set :
raise CTRexPacketBuildException(-14, "Empty pcap file {0}".format(f_path))
def to_pkt_dump(self):
p = self.pkt
if p and isinstance(p, Packet):
p.show2();
hexdump(p);
return;
p = self.pkt_raw;
if p:
scapy_pkt = Ether(p);
scapy_pkt.show2();
hexdump(p);
def set_packet (self, pkt):
"""
Scapy packet
Example::
pkt =Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)/IP()/('x'*10)
"""
if isinstance(pkt, Packet):
self.pkt = pkt;
else:
if isinstance(pkt, str):
self.set_pcap_file(pkt)
else:
raise CTRexPacketBuildException(-14, "bad packet" )
def is_default_src_mac (self):
if self.is_binary_source:
return True
p = self.pkt
if isinstance(p, Packet):
if isinstance(p,Ether):
if 'src' in p.fields :
return False
return True
def is_default_dst_mac (self):
if self.is_binary_source:
return True
p = self.pkt
if isinstance(p, Packet):
if isinstance(p,Ether):
if 'dst' in p.fields :
return False
return True
def compile (self):
if self.pkt == None and self.pkt_raw == None:
raise CTRexPacketBuildException(-14, "Packet is empty")
self.vm_low_level = CTRexVmEngine()
# compile the VM
for sc in self.vm_scripts:
if isinstance(sc, STLScVmRaw):
self._compile_raw(sc)
def get_pkt_len (self):
if self.pkt:
return len(self.pkt)
elif self.pkt_raw:
return len(self.pkt_raw)
else:
raise CTRexPacketBuildException(-14, "Packet is empty")
def to_json (self):
''' write packet builder to JSON format '''
return {'packet': self.dump_pkt(), 'vm': self.get_vm_data()}
@staticmethod
def from_json (json_data):
''' given a JSON, construct a valid packet builder object '''
try:
# packet
pkt_buffer = base64.b64decode(json_data['packet']['binary'])
# VM
vm = json_data['vm']
# create VM object
vm_obj = STLVM()
# set cache size
if 'cache' in vm:
vm_obj.set_cached(vm['cache'])
# fetch instructions
vm_instr = json_data['vm']['instructions']
if not type(vm_instr) == list:
raise TRexError("from_json: bad type {0} for 'VM' field".format(type(vm_instr)))
# iterate over instructions
for instr in vm_instr:
# flow var
if instr['type'] == 'flow_var':
vm_obj.var(name = instr['name'],
op = instr['op'],
min_value = instr.get('min_value',0),
max_value = instr.get('max_value',0),
value_list = instr.get('value_list',None),
size = instr['size'],
step = instr['step'],
split_to_cores = instr.get('split_to_cores', True),
next_var = instr.get('next_var', None))
# write flow var
elif instr['type'] == 'write_flow_var':
vm_obj.write(fv_name = instr['name'],
pkt_offset = instr['pkt_offset'],
add_val = instr['add_value'],
byte_order = 'big' if instr['is_big_endian'] else 'little')
# IPv4 checksum fix
elif instr['type'] == 'fix_checksum_ipv4':
vm_obj.fix_chksum(offset = instr['pkt_offset'])
# HW checksum fix
elif instr['type'] == 'fix_checksum_hw':
vm_obj.fix_chksum_hw(l3_offset = instr['l2_len'],
l4_offset = instr['l2_len'] + instr['l3_len'],
l4_type = instr['l4_type'])
# tuple flow var
elif instr['type'] == 'tuple_flow_var':
vm_obj.tuple_var(name = instr['name'],
ip_min = instr['ip_min'],
ip_max = instr['ip_max'],
port_min = instr['port_min'],
port_max = instr['port_max'],
limit_flows = instr['limit_flows'])
# trim
elif instr['type'] == 'trim_pkt_size':
vm_obj.trim(fv_name = instr['name'])
# write masked flow var
elif instr['type'] == 'write_mask_flow_var':
vm_obj.write_mask(fv_name = instr['name'],
pkt_offset = instr['pkt_offset'],
pkt_cast_size = instr['pkt_cast_size'],
mask = instr['mask'],
shift = instr['shift'],
add_val = instr['add_value'],
byte_order = 'big' if instr['is_big_endian'] else 'little')
elif instr['type'] == 'flow_var_rand_limit':
vm_obj.repeatable_random_var(fv_name = instr['name'],
size = instr['size'],
limit = instr['limit'],
seed = instr['seed'],
min_value = instr['min_value'],
max_value = instr['max_value'],
split_to_cores = instr.get('split_to_cores', True),
next_var = instr.get('next_var', None))
else:
print(instr)
raise TRexError("from_json: unknown VM instruction type '{0}'".format(instr['type']))
except binascii.Error:
raise TRexError("from_json: bad packet format")
except KeyError as e:
raise TRexError("from_json: missing field {0} from JSON".format(e))
return STLPktBuilder(pkt_buffer = pkt_buffer, vm = vm_obj)
####################################################
# private
def compute_previous(self, flow_variables):
for var_name, var in flow_variables.items():
next_var_name = var.get_next_var_name()
if next_var_name:
next_var = flow_variables[next_var_name]
if next_var.get_previous_var_name() and next_var.get_previous_var_name() != var_name:
raise CTRexPacketBuildException(-11,"Variable %s is pointed by two vars." % next_var_name )
next_var.previous = var_name
next_var.split_to_cores = False
def order_flow_vars(self, flow_variables):
ordered_flow_vars = []
for var in flow_variables.values():
if var.get_previous_var_name() is None: #it means that this var is the head of a chain
head = var #start inserting the whole chain in the list
while (head.get_next_var_name()):
ordered_flow_vars.append(head)
head = flow_variables[head.get_next_var_name()] # update the head to the next var
# last var in a chain
ordered_flow_vars.append(head)
if len(flow_variables) != len(ordered_flow_vars):
raise CTRexPacketBuildException(-11,"Loops are forbidden for dependent variables")
return ordered_flow_vars
def _get_pcap_file_path (self,pcap_file_name):
f_path = pcap_file_name
if os.path.isabs(pcap_file_name):
f_path = pcap_file_name
else:
if self.path_relative_to_profile:
p = self._get_path_relative_to_profile () # loader
if p :
f_path=os.path.abspath(os.path.join(os.path.dirname(p),pcap_file_name))
return f_path
def _get_path_relative_to_profile (self):
p = inspect.stack()
for obj in p:
if obj[3]=='get_streams':
return obj[1]
return None
def _compile_raw (self,obj):
# make sure we have varibles once
vars={}
flow_vars = collections.OrderedDict() #dictionary of flow vars
# add vars to vars dict
for desc in obj.commands:
var_names = desc.get_var_name()
if var_names :
for var_name in var_names:
if var_name in vars:
raise CTRexPacketBuildException(-11,("Variable %s defined twice ") % (var_name) )
else:
vars[var_name]=1
# check that all write command variables exist
for desc in obj.commands:
var_name = desc.get_var_ref()
if var_name :
if var_name not in vars:
raise CTRexPacketBuildException(-11,("Variable %s does not exist ") % (var_name) )
# create dictionary of flow variables
for desc in obj.commands:
if type(desc) in (STLVmFlowVar,STLVmFlowVarRepeatableRandom):
flow_vars[desc.get_var_name()[0]] = desc
# check that all next_var exist
for var_name, var in flow_vars.items():
next_var_name = var.get_next_var_name()
if next_var_name:
if next_var_name not in flow_vars:
raise CTRexPacketBuildException(-11,"Variable %s does not exist " % next_var_name )
self.compute_previous(flow_vars)
ordered_commands = self.order_flow_vars(flow_vars)
ordered_commands += [cmd for cmd in obj.commands if type(cmd) not in (STLVmFlowVar,STLVmFlowVarRepeatableRandom)]
for desc in ordered_commands:
desc.compile(self)
self.vm_low_level.add_ins(desc.get_obj())
#set cache size
if obj.cache_size :
validate_type('obj.cache_size', obj.cache_size, int)
self.vm_low_level.cache_size = obj.cache_size
# lazy packet build only on demand
def __lazy_build_packet (self):
# alrady built ? bail out
if self.is_pkt_built:
return
# for buffer, promote to a scapy packet
if self.pkt_raw:
self.pkt = Ether(self.pkt_raw)
self.pkt_raw = None
# regular scapy packet
elif not self.pkt:
# should not reach here
raise CTRexPacketBuildException(-11, 'Empty packet')
if self.remove_fcs and self.pkt.lastlayer().name == 'Padding':
self.pkt.lastlayer().underlayer.remove_payload()
self.pkt.build()
self.is_pkt_built = True
def _pkt_layer_offset (self,layer_name):
self.__lazy_build_packet()
p_utl=CTRexScapyPktUtl(self.pkt);
return p_utl.get_layer_offet_by_str(layer_name)
def _name_to_offset(self,field_name):
self.__lazy_build_packet()
p_utl=CTRexScapyPktUtl(self.pkt);
return p_utl.get_field_offet_by_str(field_name)
def _get_pkt_as_str(self):
if self.pkt:
return bytes(self.pkt)
if self.pkt_raw:
return self.pkt_raw
raise CTRexPacketBuildException(-11, 'Empty packet');
def _add_tuple_gen(self,tuple_gen):
pass;
def STLIPRange (src = None,
dst = None,
fix_chksum = True):
vm = []
if src:
vm += [
STLVmFlowVar(name="src", min_value = src['start'], max_value = src['end'], size = 4, op = "inc", step = src['step']),
STLVmWrFlowVar(fv_name="src",pkt_offset= "IP.src")
]
if dst:
vm += [
STLVmFlowVar(name="dst", min_value = dst['start'], max_value = dst['end'], size = 4, op = "inc", step = dst['step']),
STLVmWrFlowVar(fv_name="dst",pkt_offset= "IP.dst")
]
if fix_chksum:
vm.append( STLVmFixIpv4(offset = "IP"))
return vm
class STLVM(STLScVmRaw):
'''
Defines a VM/Field Engine object
Describes the interaction on each packet
'''
def __init__ (self):
STLScVmRaw.__init__(self)
def set_cached (self, cache_size):
"""
set VM as cached with a cache size
"""
self.cache_size = cache_size
def var (self, name, min_value, max_value, size, op, step = 1, split_to_cores = True, next_var = None, **k):
"""
Defines a flow variable.
Allocates a variable on a stream context. The size argument determines the variable size.
The operation can be inc, dec, and random.
For increment and decrement operations, can set the "step" size.
For all operations, can set initialization value, minimum and maximum value.
:parameters:
name : string
Name of the stream variable
min_value : int
Min value
max_value : int
Max value
size : int
Number of bytes of the variable. Possible values: 1,2,4,8 for uint8_t, uint16_t, uint32_t, uint64_t
step : int
Step in case of "inc" or "dec" operations
op : string
Possible values: "inc", "dec", "random"
split_to_cores : bool
Default value is True. If the value is True then each core updates the variable by step * num of cores.
If split_to_cores = False then each core updates the variable by step.
"""
self.add_cmd(STLVmFlowVar(name = name, min_value = min_value, max_value = max_value, size = size, op = op, step = step,
split_to_cores = split_to_cores, next_var = next_var, **k))
def write (self, fv_name, pkt_offset, offset_fixup = 0, add_val = 0, byte_order = 'big'):
"""
Write a previously defined varaible to the packet.
The write position is determined by the packet offset + offset fixup. The size of the write is determined by the stream variable.
Example: Offset 10, fixup 0, variable size 4. This function writes at 10, 11, 12, and 13.
For inromation about chaning the write size, offset, or fixup, see the `STLVmWrMaskFlowVar` command.
The Field name/offset can be given by name in the following format: ``header[:id].field``.
:parameters:
fv_name : string
Stream variable to write to a packet offset.
pkt_offset : string or int
Name of the field or offset in bytes from packet start.
offset_fixup : int
Number of bytes to move forward. If negative, move backward.
add_val : int
Value to add to the stream variable before writing it to the packet field. Can be used as a constant offset.
is_big : bool
How to write the variable to the the packet. True=big-endian, False=little-endian
"""
self.add_cmd(STLVmWrFlowVar(fv_name = fv_name,
pkt_offset = pkt_offset,
offset_fixup = offset_fixup,
add_val = add_val,
is_big = (byte_order == 'big')))
def tuple_var (self, name, ip_min, ip_max, port_min, port_max, limit_flows = 0, **k):
"""
Generate a struct with two variables: ``var_name.ip`` as uint32_t and ``var_name.port`` as uint16_t
The variables are dependent. When the ip variable value reaches its maximum, the port is incremented.
For:
* ip_min = 10.0.0.1
* ip_max = 10.0.0.5
* port_min = 1025
* port_max = 1028
* limit_flows = 10
The result:
+------------+------------+-----------+
| ip | port | flow_id |
+============+============+===========+
| 10.0.0.1 | 1025 | 1 |
+------------+------------+-----------+
| 10.0.0.2 | 1025 | 2 |
+------------+------------+-----------+
| 10.0.0.3 | 1025 | 3 |
+------------+------------+-----------+
| 10.0.0.4 | 1025 | 4 |
+------------+------------+-----------+
| 10.0.0.5 | 1025 | 5 |
+------------+------------+-----------+
| 10.0.0.1 | 1026 | 6 |
+------------+------------+-----------+
| 10.0.0.2 | 1026 | 7 |
+------------+------------+-----------+
| 10.0.0.3 | 1026 | 8 |
+------------+------------+-----------+
| 10.0.0.4 | 1026 | 9 |
+------------+------------+-----------+
| 10.0.0.5 | 1026 | 10 |
+------------+------------+-----------+
| 10.0.0.1 | 1025 | 1 |
+------------+------------+-----------+
:parameters:
name : string
Name of the stream struct.
ip_min : string or int
Min value of the ip value. Number or IPv4 format.
ip_max : string or int
Max value of the ip value. Number or IPv4 format.
port_min : int
Min value of port variable.
port_max : int
Max value of port variable.
limit_flows : int
Limit of number of flows.
"""
self.add_cmd(STLVmTupleGen(name = name,
ip_min = ip_min,
ip_max = ip_max,
port_min = port_min,
port_max = port_max,
limit_flows = limit_flows, **k))
def fix_chksum (self, offset = "IP"):
"""
Fix IPv4 header checksum. Use this if the packet header has changed and it is necessary to change the checksum.
:parameters:
offset : uint16_t or string
**IPv4 header** offset from packet start. It is **not** the offset of the checksum field itself.
in could be string in case of scapy packet. format IP[:[id]]
"""
self.add_cmd(STLVmFixIpv4(offset))
def fix_chksum_hw (self, l3_offset, l4_offset, l4_type):
"""
Fix IPv4 header checksum and/or TCP/UDP checksum using hardware assist.
Use this if the packet header has changed or data payload has changed as it is necessary to fix the checksums.
This instruction works on NICS that support this hardware offload.
For fixing only IPv4 header checksum use STLVmFixIpv4. This instruction should be used if both L4 and L3 need to be fixed.
:parameters:
l3_offset : offset in bytes
**IPv4/IPv6 header** offset from packet start. It is **not** the offset of the checksum field itself.
in could be string in case of scapy packet. format IP[:[id]]
l4_offset : offset in bytes to UDP/TCP header or IPv4 payload. in case of IPv4 checksum CTRexVmInsFixHwCs.L4_TYPE_IP could be set to zero to be auto calculated by the server
l4_type : [CTRexVmInsFixHwCs.L4_TYPE_UDP or CTRexVmInsFixHwCs.L4_TYPE_TCP or CTRexVmInsFixHwCs.L4_TYPE_IP]
see full example stl/syn_attack_fix_cs_hw.py
"""
self.add_cmd(STLVmFixChecksumHw(l3_offset = l3_offset,
l4_offset = l4_offset,
l4_type = l4_type))
def trim (self, fv_name):
"""
Trim the packet size by the stream variable size. This instruction only changes the total packet size, and does not repair the fields to match the new size.
:parameters:
fv_name : string
Stream variable name. The value of this variable is the new total packet size.
"""
self.add_cmd(STLVmTrimPktSize(fv_name = fv_name))
def write_mask (self, fv_name, pkt_offset, pkt_cast_size, mask, shift = 0, offset_fixup = 0, add_val = 0, byte_order = 'big'):
"""
Write a stream variable into a packet field with some operations.
Using this instruction, the variable size and the field can have different sizes.
:parameters:
fv_name : string
The stream variable name to write to a packet field
pkt_offset : string or int
the name of the field or offset in byte from packet start.
pkt_cast_size : uint8_t
The size in bytes of the packet field
mask : uint32_t
The mask of the field. 1 means to write. 0 don't care
shift : uint8_t
How many bits to shift
offset_fixup : int
how many bytes to go forward. In case of a negative value go backward
add_val : int
value to add to stream variable before writing it to packet field. can be used as a constant offset
is_big : bool
how to write the variable to the the packet. is it big-edian or little edian
"""
self.add_cmd(STLVmWrMaskFlowVar(fv_name = fv_name,
pkt_offset = pkt_offset,
pkt_cast_size = pkt_cast_size,
mask = mask,
shift = shift,
add_value = add_val,
offset_fixup = offset_fixup,
is_big = (byte_order == 'big')))
def repeatable_random_var (self, fv_name, size, limit, seed = None, min_value = None, max_value = None, split_to_cores = True, next_var = None):
"""
Flow variable instruction for repeatable random with limit number of generating numbers. Allocates memory on a stream context.
The size argument determines the variable size. Could be 1,2,4 or 8
1. The maximum number of distinct values will 'limit'. There could be a case of repetition
2. The values will be repeated after 'limit' number of values.
:parameters:
name : string
Name of the stream variable
size : int
Number of bytes of the variable. Possible values: 1,2,4,8 for uint8_t, uint16_t, uint32_t, uint64_t
limit : int
The number of distinct repetable random number
seed : int
For deterministic result, you can set this to a uint16_t number
min_value : int
Min value
max_value : int
Max value
split_to_cores : bool
Default value is True. If the value is True then each core updates the variable by step * num of cores.
If split_to_cores = False then each core updates the variable by step.
next_var : string or None
In case it isn't None it is the name of the next variable. The next variable will perform its operation only when this variable
completes a full wrap around.
"""
self.add_cmd(STLVmFlowVarRepeatableRandom(name = fv_name,
size = size,
limit = limit,
seed = seed,
min_value = min_value,
max_value = max_value,
split_to_cores= split_to_cores,
next_var = next_var))
You can’t perform that action at this time.