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.
 
 
 
 
 
 

102 lines
2.5 KiB

  1. import asyncio
  2. import random
  3. import socket
  4. import struct
  5. import unittest
  6. from util import *
  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 close(self):
  23. return self.transport.close()
  24. def connection_lost(self, exc):
  25. self.transport = None
  26. def connection_made(self, transport):
  27. # Note: the connection_made call seems to be sync. This
  28. # isn't documented, and I don't know how to force a test
  29. # if it isn't.
  30. self.transport = transport
  31. class ReceiverProtocol(StupidProtocol):
  32. def __init__(self):
  33. super().__init__()
  34. self._q = asyncio.Queue()
  35. def datagram_received(self, data, addr):
  36. self._q.put_nowait((data, addr))
  37. async def recv(self):
  38. return await self._q.get()
  39. async def create_multicast_receiver(maddr):
  40. sock = make_multisock(maddr)
  41. loop = asyncio.get_running_loop()
  42. transport, protocol = await loop.create_datagram_endpoint(
  43. lambda: ReceiverProtocol(),
  44. sock=sock)
  45. return protocol
  46. class TransmitterProtocol(StupidProtocol):
  47. async def send(self, msg):
  48. self.transport.sendto(msg)
  49. async def create_multicast_transmitter(maddr):
  50. loop = asyncio.get_running_loop()
  51. transport, protocol = await loop.create_datagram_endpoint(
  52. lambda: TransmitterProtocol(),
  53. remote_addr=maddr)
  54. return protocol
  55. class TestMulticast(unittest.IsolatedAsyncioTestCase):
  56. @timeout(2)
  57. async def test_multicast(self):
  58. # see: https://www.iana.org/assignments/multicast-addresses/multicast-addresses.xhtml#multicast-addresses-1
  59. maddr = ('224.0.0.199', 3485)
  60. l1 = await create_multicast_receiver(maddr)
  61. l2 = await create_multicast_receiver(maddr)
  62. t1 = await create_multicast_transmitter(maddr)
  63. print('tm:', repr(t1))
  64. msg = b'test message'
  65. await t1.send(msg)
  66. await t1.send(msg)
  67. self.assertEqual((await l1.recv())[0], msg)
  68. self.assertEqual((await l2.recv())[0], msg)
  69. self.assertEqual((await l1.recv())[0], msg)
  70. self.assertEqual((await l2.recv())[0], msg)
  71. t1.close()
  72. l1.close()
  73. l2.close()