-
Notifications
You must be signed in to change notification settings - Fork 36
/
Copy pathaddressManager.py
271 lines (246 loc) · 13 KB
/
addressManager.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
#
# Address Manager
#
# Keeps, provides and finds the MAC and IPv6 addresses
#
import subprocess
import os
import sys
import ipaddress
from helpers import * # prettyMac etc
from configmodule import getConfigValue, getConfigValueBool
MAC_LAPTOP = [0xdc, 0x0e, 0xa1, 0x11, 0x67, 0x08 ] # Win10 laptop
#MAC_RANDOM = [0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff ]
#MAC_ALPI = [0x0A, 0x19, 0x4A, 0x39, 0xD6, 0x98 ] # alpitronics
#MAC_TPLINK_E4 = [0x98, 0x48, 0x27, 0x5A, 0x3C, 0xE4 ] # TPlink PLC adaptor
#MAC_TPLINK_E6 = [0x98, 0x48, 0x27, 0x5A, 0x3C, 0xE6 ] # TPlink PLC adaptor
#MAC_DEVOLO_26 = [0xBC, 0xF2, 0xAF, 0x0B, 0x8E, 0x26 ] # Devolo PLC adaptor
class addressManager():
def __init__(self):
self.localIpv6Addresses=[]
self.findLocalMacAddress()
self.findLinkLocalIpv6Address()
self.pevIp=""
self.SeccIp=""
self.SeccTcpPort = 15118 # just a default. Will be overwritten during SDP if we are pev.
self.evseMacIsUpdated = False
self.evseMac = [0, 0, 0, 0, 0, 0]
pass
def findLinkLocalIpv6Address(self):
# For the IPv4 address, this works:
# print(socket.gethostbyname(socket.gethostname())) # Result: The ipv4 address of the ethernet adaptor
# But this does not help, we need the IPv6 address.
#
# On windows we can use command line tool "ipconfig".
# The link-local addresses always start with fe80::, so we just parse the output of ipconfig line-by-line.
# If we have multiple interfaces (e.g. ethernet and WLAN), it will find multiple link-local-addresses.
#
# On Linux, we use "ip addr". From its output we can directly see the ethernet IPv6 and MAC, even if
# there are multiple other interfaces (e.g. WLAN). Pitfall: As long as there is nothing connected to the
# ethernet, the Raspberry will not tell a link-local IPv6 in the command "ip addr". This means, we must
# make sure that the modem is connected and powered, when the script is starting.
ba = bytearray(6)
foundAddresses = []
if os.name == 'nt':
# on Windows
result = subprocess.run(["ipconfig.exe"], capture_output=True, text=True, encoding="ansi")
if (len(result.stderr)>0):
print(result.stderr)
else:
lines = result.stdout.split("\n")
for line in lines:
if (line.find("IPv6")>0):
k = line.find(" fe80::")
if (k>0):
foundAddresses.append(line[k+1:])
else:
# on Raspberry
# instead of the deprecated ifconfig, use "ip addr"
result = subprocess.run(["ip", "addr"], capture_output=True, text=True)
if (len(result.stderr)>0):
print(result.stderr)
else:
blInTheEthernetChapter = 0
lines = result.stdout.split("\n")
for line in lines:
# print(line);
if (line[0:1]!=" "): # if the line does not start with a blank, then it is a heading, e.g.
# 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
# 3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
# print("This is a heading")
sFind = ": " + getConfigValue("eth_interface") # e.g. "eth0"
if (line.find(sFind)>0):
# print("This is the heading for the ethernet.")
blInTheEthernetChapter = 1 # we are in the ethernet chapter
else:
blInTheEthernetChapter = 0 # we are not in the ethernet chapter
else:
# The line started with a blank. This means, it is no heading.
# In the best case, we find here something like
# inet6 fe80::181f:efdf:97e5:2191/64 scope link
if ((blInTheEthernetChapter==1) and (line.find(" inet6")>0) and (line.find(" scope link")>0)):
k = line.find(" fe80::") # the beginning of the IPv6
if (k>0):
sIpWithText = line[k+1:]
x = sIpWithText.find(" ") # the space is the end of the IPv6
sIp = sIpWithText[0:x]
x = sIp.find("/") # remove the /64 at the end
if (x>0):
sIp = sIp[0:x]
# print("[addressManager] IP=>" + sIp + "<")
foundAddresses.append(sIp)
# Also the ethernet MAC is visible here, something like
# link/ether b8:27:eb:27:33:53 brd ff:ff:ff:ff:ff:ff
if ((blInTheEthernetChapter==1) and (line.find(" link/ether")>0)):
# print(line)
k = line.find("link/ether ")
# print(k)
strMac = line[k+11:k+28]
# e.g. "b8:27:eb:12:34:56"
# print(strMac)
# Remove all ":"
strMac = strMac.replace(":", "")
# print(strMac)
if (len(strMac)!=12):
print("[addressManager] ERROR: invalid length of MAC string. Expected be 6 bytes, means 12 hex characters. Found " + str(len(strMac)))
else:
for i in range(0, 6):
sTwoChar = strMac[2*i : 2*i+2]
ba[i] = int(sTwoChar, 16)
self.localMac = ba
print("[addressManager] we have local MAC " + prettyMac(self.localMac) + ".")
print("[addressManager] Found " + str(len(foundAddresses)) + " link-local IPv6 addresses.")
for a in foundAddresses:
print(a)
self.localIpv6Addresses = foundAddresses
if (len(self.localIpv6Addresses)==0):
print("[addressManager] Error: No local Ipv6 address was found.")
self.localIpv6Address = "localhost"
cfg_exitIfNoLocalLinkAddressIsFound = getConfigValueBool("exit_if_no_local_link_address_is_found")
if (cfg_exitIfNoLocalLinkAddressIsFound!=0):
print("Exiting, because it does not make sense to continue without IPv6 address")
sys.exit(1)
else:
print("Error: It does not make sense to continue without IPv6 address, but exit_if_no_local_link_address_is_found says you want to continue");
else:
# at least one address was found. Take the first one (this may be the wrong adaptor).
self.localIpv6Address = self.localIpv6Addresses[0]
if (len(self.localIpv6Addresses)>1):
print("[addressManager] Warning: Multiple Ipv6 addresses were found. If you have multiple network adaptors, try to disconnect the not relevant ones.")
print("[addressManager] Using the first detected, " + self.localIpv6Addresses[0])
print("[addressManager] Local IPv6 is " + self.localIpv6Address)
def findLocalMacAddress(self):
# Find out the MAC address of the local ethernet interface.
# Todo: Find this out dynamically.
ba = bytearray(6)
if os.name == 'nt':
# on Windows
self.localMac = MAC_LAPTOP
print("[addressManager] we have local MAC " + prettyMac(self.localMac) + ". Todo: find this out dynamically.")
else:
# on raspberry
# nothing to do here. The MAC address is found together with the IPv6 address above.
pass
def setPevMac(self, pevMac):
# During the SLAC, the MAC of the PEV was found out. Store it, maybe we need it later.
self.pevMac = pevMac
print("[addressManager] pev has MAC " + prettyMac(self.pevMac))
def setEvseMac(self, evseMac):
# During the SLAC, the MAC of the EVSE was found out. Store it, maybe we need it later.
self.evseMac = evseMac
self.evseMacIsUpdated = True
print("[addressManager] evse has MAC " + prettyMac(self.evseMac))
def getEvseMacAsStringAndClearUpdateFlag(self):
self.evseMacIsUpdated = False
return prettyMac(self.evseMac)
def isEvseMacNew(self):
return self.evseMacIsUpdated
def setPevIp(self, pevIp):
# During SDP, the IPv6 of the PEV was found out. Store it, maybe we need it later.
if (type(pevIp)==type(bytearray([0]))):
# the parameter was a bytearray. We want a string, so convert it.
# print("this is a byte array")
if (len(pevIp)!=16):
print("[addressManager] ERROR: setPevIp: invalid ip address len " + str(len(pevIp)))
return
s = ""
for i in range(0, 16):
s = s + twoCharHex(pevIp[i])
if (((i % 2)==1) and (i!=15)):
s = s + ":"
self.pevIp = s.lower()
else:
# the parameter was a string, assumingly. Just take it over.
self.pevIp = pevIp
print("[addressManager] pev has IP " + self.pevIp)
def setSeccIp(self, SeccIp):
# During SDP, the IPv6 of the charger was found out. Store it, maybe we need it later.
if (type(SeccIp)==type(bytearray([0]))):
# the parameter was a bytearray. We want a string, so convert it.
# print("this is a byte array")
if (len(SeccIp)!=16):
print("[addressManager] ERROR: setSeccIp: invalid ip address len " + str(len(SeccIp)))
return
s = ""
for i in range(0, 16):
s = s + twoCharHex(SeccIp[i])
if (((i % 2)==1) and (i!=15)):
s = s + ":"
self.SeccIp = s.lower()
else:
# the parameter was a string, assumingly. Just take it over.
self.SeccIp = SeccIp
print("[addressManager] charger has IP " + self.SeccIp)
def getSeccIp(self):
# The IPv6 address of the charger. Type is String.
return self.SeccIp
def setSeccTcpPort(self, SeccTcpPort):
# During SDP, the TCP port of the charger was found out. Store it, we need it later.
self.SeccTcpPort = SeccTcpPort
print("[addressManager] charger has TCP port " + str(self.SeccTcpPort))
def getSeccTcpPort(self):
# The chargers TCP port, which it announced in the SDP response.
return self.SeccTcpPort
def getLocalMacAddress(self):
print("[addressManager] will give local MAC " + prettyMac(self.localMac))
return self.localMac;
def getLocalMacAsTwelfCharString(self):
# gives the own MAC as string of 12 hex characters, without : or spaces.
s = ""
for i in range(0, 6):
s = s + twoCharHex(self.localMac[i])
return s
def getLinkLocalIpv6Address(self, resulttype="string"):
if (resulttype=="string"):
return self.localIpv6Address;
if (resulttype=="bytearray"):
# The caller wants a byte array. We need to convert a string like
# "fe80::4c46:fea5:b6c9:25a9%3" into a byte array of 16 bytes.
# "fe80::4c46:fea5:b6c9:25a9"
# print("[addressManager] converting self.localIpv6Address into bytearray")
# Step1: remove the % and all behind:
s = self.localIpv6Address
s = s.partition('%')[0]
#print(s)
# Step 2: expand the ommited zeros
# Step 3: Fill in leading zeroes for each 16 bit field
# Step 4: Remove all ":"
s = ipaddress.IPv6Address(s).exploded.replace(':', '')
#print(s)
ba = bytearray(s, 'utf-8')
if (len(s)!=32):
print("[addressManager] ERROR: invalid length of IPv6 string. Expected be 16 bytes, means 32 hex characters. Found " + str(len(s)))
else:
for i in range(0, 16):
sTwoChar = s[2*i : 2*i+2]
ba[i] = int(sTwoChar, 16)
#print(sTwoChar)
#fe80::4c46:fea5:b6c9:25a9%3
return ba
if __name__ == "__main__":
print("Testing addressManager.py....")
am = addressManager()
#am.setPevIp(bytearray([0xfe, 0x80]))
am.setPevIp(bytearray([0xfe, 0x80, 0x00, 0x00, 0xfe, 0x80, 0x00, 0x00, 0xfe, 0x80, 0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]))
print("Test result: LinkLocalIpv6=" + am.getLinkLocalIpv6Address())
print("same as byte array: " + str(am.getLinkLocalIpv6Address(resulttype="bytearray")))