nodescan2.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. # -*- coding: utf-8 -*-
  2. """
  3. Valid-License-Identifier: GPL-2.0-only
  4. SPDX-URL: https://spdx.org/licenses/GPL-2.0-only.html
  5. (c) 2012 micha@librelight.de
  6. """
  7. import time
  8. import json
  9. import socket, struct
  10. import sys
  11. import os
  12. import _thread as thread
  13. import copy
  14. import random
  15. import traceback
  16. sys.stdout.write("\x1b]2;Nodescan\x07")
  17. def UDP_Socket(bind=False,ip='',port=6454):
  18. sock = False
  19. try:
  20. sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  21. sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
  22. if bind:
  23. sock.bind((ip, port))
  24. except socket.error as e:
  25. print("Socket 6454 ", "ERR: {0} ".format(e.args))
  26. sock = False
  27. return sock
  28. def ArtPoll(sock=None,ip="2.255.255.255",port=6454):
  29. print("ArtPoll",ip,port)
  30. if not sock:
  31. sock=UDP_Socket()
  32. #port=6454
  33. #ip="2.255.255.255"
  34. PKG=b'Art-Net\x00\x00 \x00\x0e\x06\x00'
  35. print("SEND:",[PKG])
  36. sock.sendto(PKG,(ip,port)) # ArtPol / ping
  37. sock.close()
  38. def convert_mac(MAC):
  39. #MAC = data[201:201+6]
  40. _MAC = []
  41. for x in MAC:
  42. #x = hex(ord(x))[2:]
  43. x = hex(x)[2:]
  44. x = x.rjust(2,"0")
  45. _MAC.append(x)
  46. _MAC = ":".join(_MAC)
  47. return _MAC
  48. def convert_bin(d):
  49. return bin(d)[2:].rjust(8,"0")
  50. def convert_ip(d):
  51. IP="[0,0,0,0]"
  52. _ip = []
  53. try:
  54. _ip.append( d[0] )
  55. _ip.append( d[1] )
  56. _ip.append( d[2] )
  57. _ip.append( d[3] )
  58. IP = str(_ip)
  59. except:pass
  60. return IP
  61. def convert_to_hex(x,d):
  62. out = b""
  63. try:
  64. a = struct.unpack(x, d)[0]
  65. #out = hex(a)
  66. out = "{0:#0{1}x}".format(a,6)
  67. except:
  68. pass
  69. return out
  70. def artnet_get_opcode(head):
  71. #print([head]) #[9:10])
  72. opcode=0x0000
  73. name ="unkown"
  74. try:
  75. opcode=hex(struct.unpack('<h', head[8:10])[0])
  76. except:
  77. print("opcode error",[head])
  78. if opcode == '0x5000':
  79. name = "ArtDMX"
  80. elif opcode == '0x2000':
  81. name = "ArtPoll"
  82. elif opcode == '0x2100':
  83. name = "ArtPollReplay"
  84. return (name,opcode)
  85. def ArtNet_decode_pollreplay(data):
  86. debug = 0
  87. node = {}
  88. if len(data) < 10: #min opcode
  89. return node
  90. opcode=convert_to_hex("<h",data[8:10])
  91. if opcode != '0x2100': #OpPollReplay
  92. return node
  93. if len(data) < 207: #Mal
  94. return node
  95. #print(data[174:174+4])
  96. #print("===================================================================-")
  97. #print("decode",data[:13])
  98. # UDP PACKAGE VALUE:INDEX:RAGE
  99. CONF = {}
  100. CONF["IP"] = [10,14+1]
  101. CONF["port"] = [14,15+1]
  102. CONF["version"] = [16,17+1]
  103. CONF["NetSwitch"] = [18]
  104. CONF["SubSwitch"] = [19]
  105. CONF["oem"] = [20,21+1]
  106. CONF["ubea"] = [22]
  107. CONF["status"] = [23]
  108. CONF["esta"] = [24,25+1]
  109. CONF["sname"] = [26,26+17]
  110. CONF["lname"] = [44,44+43]
  111. CONF["NodeReport"] = [108,108+20]
  112. CONF["NumPort"] = [173]
  113. CONF["PortTypes"] = [174,174+4]
  114. CONF["GoodInput"] = [178,178+4]
  115. CONF["GoodOutput"] = [182,182+4]
  116. CONF["SwIn"] = [186,186+4]
  117. CONF["SwOut"] = [190,190+4]
  118. #CONF["MSG"] = [108,108+40]
  119. CONF["MAC"] = [201,201+6]
  120. cleanup = ["sname","lname","MSG","NodeReport"]
  121. for k,v in CONF.items():
  122. val = b'undefined'
  123. if len(v) == 2:
  124. val = data[v[0]:v[1]]
  125. if k in cleanup:
  126. val = val.strip(b"\x00")
  127. val = val.decode(errors="ignore")
  128. if len(v) == 1:
  129. val = data[v[0]]
  130. node[k] = val
  131. # ================================
  132. node["MAC"] = convert_mac(node["MAC"])
  133. node["IP"] = convert_ip(node["IP"])
  134. node["status"] = convert_bin(node["status"])
  135. node["opcode"] = opcode
  136. unpack = {"port":b"<H"}
  137. for k,v in node.items():
  138. if k in unpack:
  139. up = unpack[k]
  140. #print(k,v,up)
  141. node[k] = struct.unpack(up,v)[0]
  142. to_hex = {"version":'>H',"oem":'>H',"esta":"<H"}
  143. for k,u in to_hex.items():
  144. if k in node:
  145. v=node[k]
  146. node[k] = convert_to_hex(u,v)
  147. #for k,v in node.items():
  148. # if type(node[k]) is bytes:
  149. # node[k] = v.decode(errors="ignore")
  150. return node
  151. def test():
  152. UDP_ArtPollReplay = b'Art-Net\x00\x00!\x02\x00\x00T6\x19\x03P\x00\x00\x11\x10\x00\x00RUAADN-01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00AVR-ArtNet DMX NODE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Node is ready\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x80\x00\x00\x00\x08\x00\x00\x00\x82\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00p\xb3\xd5\xfa\xff\xfa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
  153. NODE_OLD = {'IP': '[2, 0, 0, 84]', 'port': (6454,), 'version': '\x03P', 'NetSwitch': 0, 'SubSwitch': 0, 'oem': '\x11\x10', 'ubea': 0, 'status': 0, 'esta': 'RU', 'sname': 'AADN-01', 'lname': 'AVR-ArtNet DMX NODE', 'NumPort': 1, 'PortTypes': '\x00\x00\x00', 'GoodInput': '\x08\x00\x00\x00', 'GoodOutput': '\x00\x00\x00', 'SwIn': '\x07\x00\x00\x00', 'SwOut': '\x00\x00\x00\x00', 'MSG': 'Node is ready', 'MAC': '70:b3:d5:fa:ff:fa'}
  154. NODE = {'IP': '[2, 0, 0, 84]', 'port': 6454, 'version': '0x0350', 'NetSwitch': 0, 'SubSwitch': 0, 'oem': '0x1110', 'ubea': 0, 'status': '00000000', 'esta': '0x5552', 'sname': 'AADN-01', 'lname': 'AVR-ArtNet DMX NODE', 'NodeReport': 'Node is ready', 'NumPort': 1, 'PortTypes': b'\x80\x00\x00\x00', 'GoodInput': b'\x08\x00\x00\x00', 'GoodOutput': b'\x82\x00\x00\x00', 'SwIn': b'\x07\x00\x00\x00', 'SwOut': b'\x00\x00\x00\x00', 'MAC': '70:b3:d5:fa:ff:fa', 'opcode': '0x2100'}
  155. node = ArtNet_decode_pollreplay(UDP_ArtPollReplay)
  156. k_miss =[]
  157. if NODE != node:
  158. for k,v in node.items():
  159. if k in NODE:
  160. print(v==NODE[k],"- diff -",k,v, NODE[k])
  161. else:
  162. k_miss.append(k)
  163. print("MISSING KEY:",k_miss)
  164. print(node)
  165. print(NODE)
  166. assert NODE == node
  167. def os_get_MAC(_filter=["vmbr0","br0"]):
  168. print(sys._getframe().f_code.co_name)
  169. cmd = "ip -j -o l l"
  170. r=os.popen(cmd)
  171. out="fa:00:00:00:00:00"
  172. out={}
  173. if not r:
  174. return out
  175. try:
  176. txt = r.read()
  177. data = json.loads(txt)
  178. for i in data:
  179. #print(i)
  180. if "ifname" not in i:
  181. continue
  182. #if i["ifname"] in _filter:
  183. if 1:
  184. dev = "none"
  185. if "ifname" in i:
  186. dev = i["ifname"]
  187. #if "." in dev:
  188. # continue
  189. if dev not in out:
  190. out[dev] = []
  191. if "address" in i:
  192. out[dev].append( i["address"])
  193. except Exception as e:
  194. print(e,e.args[0])
  195. return out
  196. def os_list_routing():
  197. print(sys._getframe().f_code.co_name)
  198. cmd="ip -j -o r l"
  199. r=os.popen(cmd)
  200. out={}
  201. if not r:
  202. return ips
  203. txt=r.read()
  204. jdata = json.loads(txt)
  205. for data in jdata:
  206. #print(data)
  207. dev = "none"
  208. if "dev" in data:
  209. dev = data["dev"]
  210. if dev not in out:
  211. out[dev] = []
  212. if "dst" in data:
  213. out[dev].append(data["dst"])
  214. return out
  215. def os_list_ip():
  216. print(sys._getframe().f_code.co_name)
  217. ips = {}
  218. cmd="ip -o -j -4 a l "
  219. r=os.popen(cmd)
  220. if not r:
  221. return ips
  222. txt=r.read()
  223. jdata = json.loads(txt)
  224. for data in jdata:
  225. #print(data)
  226. if "addr_info" in data:
  227. infos = data["addr_info"]
  228. for info in infos:
  229. dev = "None"
  230. if "dev" in info:
  231. dev = info["dev"]
  232. if "local" in info:
  233. ip = info["local"]
  234. if "prefixlen" in info:
  235. ip += "/"+str(info["prefixlen"])
  236. #if "." in dev:
  237. # continue
  238. if dev not in ips:
  239. ips[dev] = []
  240. ips[dev].append(ip)
  241. #print(" ",[dev,ip])
  242. return ips
  243. #ips = os_list_ip() #example
  244. #get_mask(ips)
  245. def get_mask(IP):
  246. print(sys._getframe().f_code.co_name)
  247. import ipaddress
  248. #mask=ipaddress.IPv4Network(IP+'/8',False)
  249. #print(mask,mask.netmask)
  250. #mask=ipaddress.IPv4Network(IP+'/24',False)
  251. mask=ipaddress.IPv4Network(IP,False)
  252. #print(IP,mask.netmask)
  253. return mask.netmask
  254. def ArtPollReply(sock=None):
  255. print(sys._getframe().f_code.co_name)
  256. if not sock:
  257. sock=UDP_Socket()
  258. port = 6454
  259. content = []
  260. content2=[]
  261. for c in content:
  262. if type(c) is not bytes:
  263. c= bytes(c,"ascii")
  264. content2.append(c)
  265. content = b''.join(content2)
  266. fill = 240-len(content)-1
  267. print()
  268. if fill > 0:
  269. content = content + b"\x00"*fill
  270. def create_ip(IP,content):
  271. #CONF["IP"] = [10,14+1]
  272. x=[]
  273. j=10
  274. for i in IP.split("."):
  275. i = 33
  276. _ip = struct.pack("B",int(i))
  277. # content = content[:10+j]+ _ip + content[10+j:]
  278. content = inject(j,_ip,content)
  279. j+=1
  280. x.append([_ip,i])
  281. print(IP,x)
  282. def patch(content,index,patch):
  283. _patch = patch[:]
  284. if type(_patch) != bytes:
  285. _patch=bytes(_patch,"ascii")
  286. content[index:index+len(_patch)] = _patch
  287. def pad(val,count,fill="\x00"):
  288. return val.ljust(count,fill)[:count]
  289. def ip_to_byte(IP):
  290. out=[]
  291. for i in IP.split("."):
  292. out.append( int(i) )
  293. return bytes(out)
  294. print()
  295. #IP="2.0.0.255"
  296. #create_ip(IP,content)
  297. #sock.sendto(content, (IP, port))
  298. # =======================================
  299. content = [0]*(282-42) # new empty PKG bytes([0,0,0,..])
  300. patch(content,0, b"Art-Net\x00")
  301. patch(content,8, b"\x00\x21") # revers 0x2100 protocol
  302. hostname=os.popen("hostname").read().strip()
  303. #hostname+="0123456789012345678901234567890123456789012345678901234567890"
  304. sname=pad(hostname,18) # Short Name, len == 30
  305. patch(content,26,sname)
  306. lname=pad("LibreLight "+hostname,64) # Long Name, len == 64
  307. patch(content,26+18,lname)
  308. Report="LibreLight is ready CPU:40%"
  309. #Report+="12345678901234567890123456789012345123456789012345678901234567890"
  310. Report=pad(Report,64)
  311. patch(content,26+28+54,Report)
  312. ips=os_list_ip()
  313. mac= os_get_MAC()
  314. if "vmbr0" in mac:
  315. mac = mac["vmbr0"][0]
  316. print()
  317. print([mac])
  318. print()
  319. mac=mac.replace(":","")
  320. print([mac])
  321. if len(mac) == 12:
  322. mac=bytes.fromhex(mac)
  323. #content[201:] = mac
  324. patch(content,201,mac)
  325. #print([mac])
  326. print("send" ,[content])
  327. for IP in ["192.168.2.255","2.0.0.255","10.10.10.255"]:
  328. content2 = content[:]
  329. _IP = ip_to_byte(IP)
  330. patch(content2,10,_IP)
  331. content2 = bytes(content2)
  332. sock.sendto(content2, (IP, port))
  333. print("send" ,[content])
  334. def main():
  335. print("start main()")
  336. sock = UDP_Socket(bind=True,ip='',port=6454)
  337. print("-- loop --")
  338. while sock:
  339. data, addr = sock.recvfrom(300)
  340. opcode=""
  341. if len(data) >= 10:
  342. opcode=convert_to_hex("<h",data[8:10])
  343. if opcode != '0x2100': #OpPollReplay
  344. continue
  345. print("-",addr,len(data),opcode)
  346. print()
  347. nodes = ArtNet_decode_pollreplay(data)
  348. for k,v in nodes.items():
  349. print("-",[k,v])
  350. print("end main()")
  351. def print_help():
  352. print("help -h --help ")
  353. print(" --main")
  354. print(" --ArtPoll")
  355. print(" --ArtPollReplay")
  356. if __name__ == "__main__":
  357. test()
  358. if "-h" in sys.argv or "--help" in sys.argv:
  359. print_help()
  360. elif "--ArtPoll" in sys.argv:
  361. ArtPoll()
  362. elif "--ArtPollReplay" in sys.argv:
  363. ArtPollReply()
  364. elif "-MAC" in sys.argv:
  365. mac= os_get_MAC()
  366. for k,v in mac.items():
  367. print("-",k,v)
  368. print()
  369. for k,v in os_list_ip().items():
  370. print("-",k,v)
  371. print()
  372. for k,v in os_list_routing().items():
  373. print("-",k,v)
  374. print()
  375. print( get_mask("192.168.2.2/24"))
  376. print( get_mask("192.168.2.2/25"))
  377. print( get_mask("192.168.2.2/8"))
  378. elif "--main" in sys.argv:
  379. main()
  380. else:
  381. print_help()