Communicating with HomePlugAV Devices using Python

I've got a couple of pairs of ON Networks' PL 500 HomePlugAV Powerline Adapters and have been playing around with them to see how they compare to the Computrend 902 devices I played around with 5 years ago.

I'm still playing around with the kit, but thought I'd document a very basic example of how to send commands to the devices using Python - the instructions should work for any kit based on Qualcomm's INT6x00 and AR7x00 chipsets (mine use the AR7420/QCA7420) - we'll be changing one of the encryption keys (the NMK) that the devices use

 

Pre-Requisites

As all management communication is via ethernet frames, we'll need to craft packets using a raw socket. The easiest way to do this is to use scapy.

As we're opening a raw socket, you'll need to run the script as root.

 

Setting the Encryption Key

As a simple example, we're going to set the Network Management Key (NMK). It forms a good example as we're not expecting a response so can focus on building the request rather than also having to listen for the response.

#!/usr/bin/env python
#
# Copyright (C) 2014 B Tasker
#
#

import sys
# sys.path.append('Scapy') # Uncomment this if you've got Scapy in a subdirectory rather than installed system wide


from scapy.all import *
from scapy.utils import rdpcap
import fcntl, socket, struct


iface='eth0' # Which interface should we use



# Function from http://stackoverflow.com/questions/159137/getting-mac-address
def getHwAddr(ifname):
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    info = fcntl.ioctl(s.fileno(), 0x8927,  struct.pack('256s', ifname[:15]))
    return ''.join(['%02x:' % ord(char) for char in info[18:24]])[:-1]



# We'll set the encryption key (NMK) to 'HomePlugAV' - 50D3E4933F855B7040784DF815AA8DB7
#
# To generate a NMK, use hpavkey from open-plc-utils (https://github.com/qca/open-plc-utils)
#
# hpavkey -M HomePlugAV
# 50D3E4933F855B7040784DF815AA8DB7
#
# hpavkey -M StrongPassword
# 5A11F2E2B1FDA8ABFADA70B4B1B8C674


payload='00:50:a0:00:b0:52:01:50:d3:e4:93:3f:85:5b:70:40:78:4d:f8:15:aa:8d:b7:0f:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00'
data_list = payload.split(":")

# Break down of payload used above
#
# '00' - MAC Management header (Version: 1) - they're zero indexed
# '50:a0' - Request is AxA050 (Encryption key set request)
# 'b0:52' - OUI
# '01' - EKS (in this case - Unknown 0x01)
# '50:d3:e4:93:3f:85:5b:70:40:78:4d:f8:15:aa:8d:b7' - Desired Crypto key (the NMK)
# '0f' - Payload encryption key select (0x0f)
# '00:00:00:00:00:00' - Destination Address
# '00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00' - DAK (empty in this case)



# Build and send the packet
p = Ether()
p.src=getHwAddr(iface)
p.dst='00:B0:52:00:00:01'; # Only the nearest HomeplugAV device will respond
p.type=0x88e1; # HomeplugAV management frame
p.oui='00b052'
data=''.join(data_list).decode('hex')
b = p/data
ls(b)
sendp(b,count=1,iface=iface)

If you run the script, the key on your local powerline device should be set to 'HomePlugAV'. Only the local device will have changed as we've used the MAC 00:B0:52:00:00:01. The reason we've done this is that it's far more complicated to change the NMK on a remote powerline device (so the example script would have been much, much longer).

If you watch with tcpdump, you should see something similar to the following

tcpdump -i eth0 ether dst host '00:B0:52:00:00:01'

17:37:46.776878 00:1e:a0:cf:87:18 (oui Unknown) > 00:b0:52:00:00:01 (oui Unknown), ethertype Unknown (0x88e1), length 60: 
	0x0000:  0050 a000 b052 0150 d3e4 933f 855b 7040  .P...R.P...?.[p@
	0x0010:  784d f815 aa8d b70f 0000 0000 0000 0000  xM..............
	0x0020:  0000 0000 0000 0000 0000 0000 0000       ..............

The key should also have changed on the local powerline device.