Implement a secure ICS protocol targeting LoRa Node151 microcontroller for controlling irrigation.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

92 lines
2.4 KiB

  1. import asyncio
  2. import random
  3. import socket
  4. import struct
  5. import unittest
  6. from lora import timeout
  7. # This function based upon code from:
  8. # https://gist.github.com/petrdvor/e802bec72e78ace061ab9d4469418fae#file-async-multicast-receiver-server-py-L54-L72
  9. def make_multisock(maddr):
  10. # family, type, proto, ??, addr)
  11. addrinfo = socket.getaddrinfo(*maddr, type=socket.SOCK_DGRAM)[0]
  12. sock = socket.socket(*addrinfo[:2])
  13. sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
  14. sock.bind(maddr)
  15. group_bin = socket.inet_pton(addrinfo[0], addrinfo[4][0])
  16. mreq = group_bin + struct.pack('=I', socket.INADDR_ANY)
  17. sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
  18. return sock
  19. class StupidProtocol(object):
  20. def __init__(self):
  21. self.transport = None
  22. def connection_made(self, transport):
  23. # Note: the connection_made call seems to be sync. This
  24. # isn't documented, and I don't know how to force a test
  25. # if it isn't.
  26. self.transport = transport
  27. class ReceiverProtocol(StupidProtocol):
  28. def __init__(self):
  29. super().__init__()
  30. self._q = asyncio.Queue()
  31. def datagram_received(self, data, addr):
  32. self._q.put_nowait((data, addr))
  33. async def recv(self):
  34. return await self._q.get()
  35. async def create_multicast_receiver(maddr):
  36. sock = make_multisock(maddr)
  37. loop = asyncio.get_running_loop()
  38. transport, protocol = await loop.create_datagram_endpoint(
  39. lambda: ReceiverProtocol(),
  40. sock=sock)
  41. return protocol
  42. class TransmitterProtocol(StupidProtocol):
  43. async def send(self, msg):
  44. self.transport.sendto(msg)
  45. async def create_multicast_transmitter(maddr):
  46. loop = asyncio.get_running_loop()
  47. transport, protocol = await loop.create_datagram_endpoint(
  48. lambda: TransmitterProtocol(),
  49. remote_addr=maddr)
  50. return protocol
  51. class TestMulticast(unittest.IsolatedAsyncioTestCase):
  52. @timeout(2)
  53. async def test_multicast(self):
  54. # see: https://www.iana.org/assignments/multicast-addresses/multicast-addresses.xhtml#multicast-addresses-1
  55. maddr = ('224.0.0.%d' % random.randint(151, 250), 3485)
  56. l1 = await create_multicast_receiver(maddr)
  57. l2 = await create_multicast_receiver(maddr)
  58. t1 = await create_multicast_transmitter(maddr)
  59. print('tm:', repr(t1))
  60. msg = b'test message'
  61. await t1.send(msg)
  62. await t1.send(msg)
  63. self.assertEqual((await l1.recv())[0], msg)
  64. self.assertEqual((await l2.recv())[0], msg)
  65. self.assertEqual((await l1.recv())[0], msg)
  66. self.assertEqual((await l2.recv())[0], msg)