I am working with a fork of scapy (a Python packet manipulation tool) called scapy-com. This implements 802.15.4 and Zigbee parsing/manipulation, amongst other protocols.
A quirk of the Zigbee protcol is found in the network level security header. Initially, the security level (which defines the encryption and length of message integrity code) is set correctly, but is then set to 0 (no encryption) before it is sent. From the spec:
The security level sub-field of the security control field shall be
over-written by the 3-bit all-zero string '000'
The spec can be found here. The relevant section is "4.3.1.1 Security Processing of Outgoing Frames".
This means that packet captures indicate that no encryption or message integrity code is in use. The security level must be communicated out-of-band.
scapy-com doesn't deal with this. It naively parses the security level and sets the length of the MIC to 0. The code that does this is:
def util_mic_len(pkt):
''' Calculate the length of the attribute value field '''
# NWK security level 0 seems to implicitly be same as 5
if ( pkt.nwk_seclevel == 0 ): # no encryption, no mic
return 0
elif ( pkt.nwk_seclevel == 1 ): # MIC-32
return 4
elif ( pkt.nwk_seclevel == 2 ): # MIC-64
return 8
elif ( pkt.nwk_seclevel == 3 ): # MIC-128
return 16
elif ( pkt.nwk_seclevel == 4 ): # ENC
return 0
elif ( pkt.nwk_seclevel == 5 ): # ENC-MIC-32
return 4
elif ( pkt.nwk_seclevel == 6 ): # ENC-MIC-64
return 8
elif ( pkt.nwk_seclevel == 7 ): # ENC-MIC-128
return 16
else:
return 0
The project that uses scapy-com attempts to deal with this by setting the security level to 5:
#TODO: Investigate and issue a different fix:
# https://code.google.com/p/killerbee/issues/detail?id=30
# This function destroys the packet, therefore work on a copy - #cutaway
pkt = pkt.copy() #this is hack to fix the below line
pkt.nwk_seclevel=5 #the issue appears to be when this is set
mic = pkt.mic
However, this doesn't work - the message integrity code has already been set. I have worked around this by simply altering the util_mic_len function to set the mic length correctly.
The question is, how should the Zigbee parser be changed so that altering the nwk_seclevel after the initial dissection causes the mic length to be updated?
I can see two solutions:
Change the scapy-com code so that changing nwk_seclevel automatically changes the mic length.
Re-dissect the packets from outside scapy-com as they are changed.
The issue with 1 is I have no idea about how to go about it.
The issue with 2 is that I have some idea but can't get it to work - I can't work out how to call dissect on a packet after it has been loaded. Calling pkt.dissect(pkt) seems to not work and looks odd.
What is the best or recommended solution here?
Fixing scapy sounds right solution.
scapy-com is quite old. Zigbee specific code in scapy-com is 1244 lines of code, which in large part are enumerations and field lists. So, it should not be too hard to migrate it to scapy-python3. If you would assist in migrating it to scapy-python3 http://github.com/phaethon/scapy , I could help with fixing the issue.
The project you are referring to is KillerBee and I had this exact problem with decryption. I simply "fixed" the code thusly:
from struct import pack
f = pkt.getlayer(ZigbeeSecurityHeader).fields
pkt.nwk_seclevel = 5
nwk_mic = pkt.mic
nwk_encrypted = f['data'][:-6]
ext_source = f['ext_source']
nwk_sec_ctrl_byte = str(pkt.getlayer(ZigbeeSecurityHeader))[0]
nwk_nonce = struct.pack('Q',ext_source) + struct.pack('I',f['fc']) + nwk_sec_ctrl_byte
nwk_crop_size = 4 + 2 + len(pkt.getlayer(ZigbeeSecurityHeader).fields['data']) # The length of the encrypted data, mic and FCS
# the Security Control Field flags have to be adjusted before this is calculated, so we store their original values so we can reset them later
zigbeeData = pkt.getlayer(ZigbeeNWK).do_build()
zigbeeData = zigbeeData[:-nwk_crop_size]
(nwk_payload, nwk_micCheck) = zigbee_crypt.decrypt_ccm(nkey, nwk_nonce, nwk_mic, nwk_encrypted, zigbeeData)
Related
I'm working with the ipaddress module in Python and trying to figure out a way of calculating the next available subnet (of either the same prefix or a different prefix) that doesn't overlap the existing subnet (the new subnet MUST be greater than the old one).
Lets say I start with network:
from ipaddress import IPv4Network
# From 10.90.1.0 to 10.90.1.31
main_net = IPv4Network("10.90.1.0/27")
I know the next available address is going to be 10.90.1.32, I can even figure this out quite easily by doing:
next_ip = main_net.broadcast_address + 1
# will output 10.90.1.32
print(next_ip)
If I wanted to find the next /27, I just create a new network like so:
# From 10.90.1.32 to 10.90.1.63
new_net = IPv4Network(f"{next_ip}/27")
This is all very straightforward so far, but now what if the next subnet I am looking for is a /26 or a /28 - how can I find the next minimum start IP address for either of these cases in Python?
I have explored using the supernet method, for example I could do something like this:
# Will print 10.90.1.0/26
print(main_net.supernet(new_prefix=27))
The problem with this method is that it will print 10.90.1.0/26 which overlaps the existing 10.90.1.0/27 network, I could make a loop and get it to keep generated the next /26 until they stop overlapping, but it seems inefficient to me. Surely, there is a better way?
Thanks to the help of Ron Maupin's helpful comment leading to a useful guide, I have managed to make a function that does this. I still want to test it a bit more but believe it is correct:
def calculate_next_ip_network(ip_bytes, current_prefix, next_prefix):
next_prefix_mask = (~((1 << (32 - next_prefix)) - 1)) & 0xFFFFFFFF
if next_prefix <= current_prefix:
bit_shift = 32 - next_prefix
else:
bit_shift = 32 - current_prefix
new_ip = (((next_prefix_mask & ip_bytes) >> bit_shift) + 1) << bit_shift
return bytes([new_ip >> i & 0xFF for i in (24, 16, 8, 0)])
Usage:
nt = IPv4Network("10.90.1.56/29")
current_prefix = nt.prefixlen
next_prefix = 25
ip_bytes = int.from_bytes(nt.network_address.packed, byteorder="big")
next_ip = calculate_next_ip_network(ip_bytes, current_prefix, next_prefix)
print(IPv4Address(next_ip))
# Should print "10.90.1.128"
I'm working with a PCF8574 and I have a set of configurations that I need to send to the device. I have communication working and I'm using an I2C write_byte command. In the table below if a cell doesn't have a value then it's state should not be changed.
I know it's a bit manipulation "game" to get the combination of operators correct but I'm not having luck so far finding the right combination to yield what I want.
As an example, when the PCF8574 is powered up it's default state is 0b11111111. If I want to change to state 'B' (0bxxx10x1x, where x means don't change) what function could I create which would set the part to correctly be 0b11110111?
My current test solution isn't the way I think it should be implemented but it seems to work:
from smbus2 import SMBus
i2c_port_num = 1
pcfAddress = 0x38
pcfBits = 'xxx00xxx'
curSetting = i2cBus.read_byte(pcfAddress)
def checkBit(newSetting, bits):
for i in range(0, len(bits)):
if(bits[i]) != 'x':
print("position", i, "is", bits[i])
if int(bits[i]) == 0:
newSetting &=~(1<<i)
elif int(bits[i]) == 1:
newSetting |= (1<<i)
return(newSetting)
changeState = checkBit(curSetting, pcfBits)
i2cBus.write_byte(pcfAddress, changeState)
I’m really sorry if I’m asking a question that’s been already answered but I couldn’t find an answer.
I’m writing a code that would allow me to connect a translate of several controllers into a blendWeighted node input channels. The amount of the controllers may vary depending on the selection. I’m struggling with the part where they need to connect to a single blendWeighted node input. Could someone tell me how I could connect every new controller to the next input channel of the blendWeighted node?
I’m sorry if my code is a bit childlike, I’m still learning ^^;
sel = mc.ls(sl=True, fl=True)
drvConnect = []
for i in sel:
name = i.split('_Crv')[0]
dGP = mc.createNode('transform', n='%s_Drv'%name, p=i)
drvConnect.append(dGP)
sh = mc.listRelatives(i, shapes=True)[0]
blendX = mc.createNode('blendWeighted', n='%s_X'%name)
blendY = mc.createNode('blendWeighted', n='%s_Y'%name)
blendZ = mc.createNode('blendWeighted', n='%s_Z'%name)
mc.connectAttr(dGP + '.translateX', blendX +'.input')
mc.connectAttr(dGP + '.translateY', blendY +'.input')
mc.connectAttr(dGP + '.translateZ', blendZ +'.input')
I assume you only want to create a single blendWeighted node. If that's the case, consider that the blendWeighted node's input attribute is an array attribute, so if you want to connect multiple items to it you would need to specify the target index.
For example, to connect the three translate outputs of a node to the first three inputs of the blend node you would use something like this:
mc.connectAttr('ctrl.translateX', 'blend.input[0]')
mc.connectAttr('ctrl.translateY', 'blend.input[1]')
mc.connectAttr('ctrl.translateZ', 'blend.input[2]')
(Maya will take care of creating the items in the array)
In our case you could simply keep a counter of the added items while you loop through the selection and the transform components (just a guide - not tested):
sel = mc.ls(sl=True, fl=True)
drvConnect = []
blendNode = mc.createNode('blendWeighted', n='blend_main')
for i in sel:
name = i.split('_Crv')[0]
dGP = mc.createNode('transform', n='%s_Drv'%name, p=i)
drvConnect.append(dGP)
for comp in ('X', 'Y', 'Z'):
mc.connectAttr(
'{}.translate{}'.format(dGP, comp),
'{}.input[{}]'.format(blendNode, blendIndex))
blendIndex += 1
I'm using a raspberry to send some sensor data over to a SOAP webservice. RPi gets the data from the serial port. The format is 'DATE, VALUE1, VALUE2, VALUE3, VALUE4\r\n'. The webservice request looks like this
<sensors>
<Sensor>
<SensorId>int</SensorId>
<NodeId>int</NodeId>
<SensorTypeId>int</SensorTypeId>
<Value>double</Value>
<Status>string</Status>
<Date>dateTime</Date>
<Deleted>boolean</Deleted>
<Updated>boolean</Updated>
<RemoteId>int</RemoteId>
<DateOfLastUpdate>dateTime</DateOfLastUpdate>
<UserId>int</UserId>
<ErrorMessage>string</ErrorMessage>
</Sensor>
<Sensor>
<SensorId>int</SensorId>
<NodeId>int</NodeId>
<SensorTypeId>int</SensorTypeId>
<Value>double</Value>
<Status>string</Status>
<Date>dateTime</Date>
<Deleted>boolean</Deleted>
<Updated>boolean</Updated>
<RemoteId>int</RemoteId>
<DateOfLastUpdate>dateTime</DateOfLastUpdate>
<UserId>int</UserId>
<ErrorMessage>string</ErrorMessage>
</Sensor>
</sensors>
<username>string</username>
<password>string</password>
<UniqueClientID>string</UniqueClientID>
<project>string</project>
Each line I get from the serial port has 4 sensor values and the datetime these values were logged. So I need to create 4 objects with the SUDS library method client.factory.create() for each line I parse from the serial, add the values to each attribute and append() the objects to the Array of Sensor objects that the webservice accepts as its first parameter. The problem is I can't find a way to dynamically create the objects, enter the attributes' values, and append them to the big array. I'm going to parse probably 600lines from the serial port, so 4x600=2400 will need to be created. Hard-coding object names like this
while True:
serial_str = port.readline()
if serial_str:
string_list = serial_str.split(',')
date = 'T'.join( [ string_list[i] for i in [0, 1] ] )
temperature = string_list[2]
humidity = string_list[3]
rain = string_list[4]
wind = string_list[5].replace("\r\n","")
Sensor_obj1 = client.factory.create('Sensor')
Sensor_obj1.SensorId = -1
Sensor_obj1.NodeId = 1
Sensor_obj1.SensorTypeId = 2
Sensor_obj1.Value = temperature
Sensor_obj1.Status = ''
Sensor_obj1.Date = date
Sensor_obj1.Deleted = 0
Sensor_obj1.Updated = 0
Sensor_obj1.RemoteId = 0
Sensor_obj1.DateOfLastUpdate = datetime.now().strftime('%Y-%m-%dT%H:%M:%S')datetime.now()
Sensor_obj1.UserId = 0
Sensor_obj1.ErrorMessage = ''
Sensor_list_obj.Sensor.append(Sensor_obj1)
Sensor_obj2 = client.factory.create('Sensor')
...
would work if I only had 1 line to send, but even then its a bad programming style. I just got started with python 2.x, so I would appreciate if someone would point me to the right direction here. Thanks in advance
I am not very familiar with the problem at hand, but I would guess that you can add more parameters to the call to client.factory.create('Sensor') call. Does it accept, **kwargs, so that you can call:
client.factory.create('Sensor', SensorID=-1, NodeId=1, ...)
If not, you can always abstract the initialization by:
Creating a function that does all the asignments and hides away the 'gory' details. This is elegant enough, and probably you shouldn't try anything more advanced if you have just started programming in python.
Create a class Sensor, that inherits from the one of suds. Unfortunately, I don't know enough about the class of objects 'client.factory.create'.
Also, I think I would create a list of sensors (or dictionary, if that makes sense in your code), so that you append all the sensors to that structure, otherwise your program will be impossible to use if you have three or five sensors instead of 4.
I am currently working on a robot that has to traverse a maze.
For the robot I am using a TMC222 Stepper controller and the software is coded in Python.
I am in need of a function which can tell me when the motors are busy so that the robot will seize all other activity while the motors are running.
My idea is to check the current position on the motors and compare it to the target position, but i haven't gotten it to work yet.
My current attempt:
def isRunning(self):
print("IS RUNNING TEST")
fullstatus=self.getFullStatus2()
#print("FULL STATUS: " + str(fullstatus[0]) + " 2 " + str(fullstatus[1]))
actLeft=fullstatus[0][1]<<8 | fullstatus[0][2]<<0
actRight=fullstatus[1][1]<<8 | fullstatus[1][2]<<0
tarLeft=fullstatus[0][3]<<8 | fullstatus[0][4]<<0
tarRight=fullstatus[1][3]<<8 | fullstatus[1][4]<<0
value = (actLeft==tarLeft) and (actRight==tarRight)
value = not value
# print("isbusy="+str(value))
print 'ActPos = ' + str(actLeft)
print 'TarPos = ' + str(tarLeft)
return value
It would be helpful to see your getFullStatus2() code as well, since it's unclear to me how you're getting a multidimensional output.
In general, you can form a 16-bit "word" from two 8-bit bytes just as you have it:
Word = HB << 8 | LB << 0
Where HB and LB are the high (bits 15-8) and low (bits 7-0) bytes.
That being said, there are multiple ways to detect motor stall. The ideal way would be an external pressure switch that closed when it hit a wall. Another would be to monitor the motor's current, when the motor faces resistance (when accelerating or in stall), the current will rise.
Since it looks like neither of these are possible, I'd use still a different approach, monitoring the motor's position (presumably from some sort of encoder) over time.
Lets say you have a function get_position() that returns an unsigned 16-bit integer. You should be able to write something like:
class MotorPosition(object):
def __init__(self):
readings = []
def poll(self):
p = get_position()
self.readings.append(readings)
# If the list is now too long, remove the oldest entries
if len(self.readings) > 5:
self.readings.pop(0)
def get_deltas():
deltas = []
for x,y in zip(self.readings[1:4], self.readings[0:3]):
d = x - y
# Wraparound detection
if (d < -THRESHOLD): d += 65536
elif(d > THRESHOLD): d -= 65536
deltas.append(d)
return deltas
def get_average_delta():
deltas = self.get_deltas()
return sum(deltas) / float(len(deltas))
Note that this assumes you're polling the encoder fast enough and with consistent frequency.
You could then monitor the average delta (from get_average_delta()) and if it drops below some value, you consider the motor stalled.
Assumptions:
This is the datasheet for the controller you're using
Your I²C code is working correctly