source: ReferenceDesigns/w3_802.11/python/wlan_exp/transport/transport_eth_ip_udp.py

Last change on this file was 6320, checked in by chunter, 5 years ago

1.8.0 release wlan-exp

File size: 13.6 KB
Line 
1# -*- coding: utf-8 -*-
2"""
3------------------------------------------------------------------------------
4Mango 802.11 Reference Design Experiments Framework
5    - Transport Ethernet IP/UDP base class
6------------------------------------------------------------------------------
7License:   Copyright 2019 Mango Communications, Inc. All rights reserved.
8           Use and distribution subject to terms in LICENSE.txt
9------------------------------------------------------------------------------
10
11This module provides the base class for Ethernet IP/UDP transports.
12
13Functions:
14    TransportEthUdp() -- Base class for Ethernet UDP transports
15    int_to_ip()       -- Convert 32 bit integer to 'w.x.y.z' IP address string
16    ip_to_int()       -- Convert 'w.x.y.z' IP address string to 32 bit integer
17    mac_addr_to_str() -- Convert 6 byte MAC address to 'uu:vv:ww:xx:yy:zz' string
18
19"""
20
21import re
22import time
23import errno
24import socket
25from socket import error as socket_error
26
27from . import message
28from . import exception as ex
29from . import transport as tp
30
31
32__all__ = ['TransportEthIpUdp']
33
34
35class TransportEthIpUdp(tp.Transport):
36    """Base Class for Ethernet IP/UDP Transport class.
37       
38    Attributes:
39        timeout        -- Maximum time spent waiting before retransmission
40        transport_type -- Unique type of the transport
41        sock           -- UDP socket
42        status         -- Status of the UDP socket
43        mtu            -- Maximum Ethernet payload size
44       
45        mac_address    -- Node's MAC address
46        ip_address     -- IP address of destination
47        unicast_port   -- Unicast port of destination
48        broadcast_port -- Broadcast port of destination
49        group_id       -- Group ID of the node attached to the transport
50        rx_buffer_size -- OS's receive buffer size (in bytes)       
51        tx_buffer_size -- OS's transmit buffer size (in bytes)       
52    """
53    timeout         = None
54    transport_type  = None
55    node_eth_device = None
56    sock            = None
57    status          = None
58    mtu             = None
59    mac_address     = None
60    ip_address      = None
61    unicast_port    = None
62    broadcast_port  = None
63    group_id        = None
64    rx_buffer_size  = None
65    tx_buffer_size  = None
66   
67    def __init__(self):
68        self.hdr = message.TransportHeader()
69        self.status = 0
70        self.timeout = 1
71        self.mtu = 1000 # sane default - overwritten during node init
72       
73        self.check_setup()
74
75    def __del__(self):
76        """Closes any open sockets"""
77        self.transport_close()
78
79    def __repr__(self):
80        """Return transport IP address and Ethernet MAC address"""
81        msg  = "Eth UDP Transport: {0} ".format(self.ip_address)
82        msg += "({0})".format(self.mac_address)
83        return msg
84
85    def check_setup(self):
86        """Check the setup of the transport."""
87        pass
88
89    def set_ip_address(self, value):     self.ip_address = value
90    def set_mac_address(self, value):    self.mac_address = value
91    def set_unicast_port(self, value):   self.unicast_port = value
92    def set_broadcast_port(self, value): self.broadcast_port = value
93    def set_src_id(self, value):         self.hdr.set_src_id(value)
94    def set_dest_id(self, value):        self.hdr.set_dest_id(value)
95
96    def get_ip_address(self):            return self.ip_address
97    def get_mac_address(self):           return self.mac_address
98    def get_unicast_port(self):          return self.unicast_port
99    def get_broadcast_port(self):        return self.broadcast_port
100    def get_src_id(self):                return self.hdr.get_src_id()
101    def get_dest_id(self):               return self.hdr.get_dest_id()
102
103
104    def transport_open(self, tx_buf_size=None, rx_buf_size=None):
105        """Opens an Ethernet IP/UDP socket.""" 
106        # AF_INET: IP, SOCK_DGRAM: UDP
107        self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
108       
109        self.sock.settimeout(self.timeout)
110        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
111        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
112       
113        if tx_buf_size is not None:
114            try:
115                self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, tx_buf_size)
116            except socket_error as serr:
117                # Cannot set the buffer size on some hardware
118                if serr.errno != errno.ENOBUFS:
119                    raise serr
120
121        if rx_buf_size is not None:
122            try:
123                self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, rx_buf_size)
124            except socket_error as serr:
125                # Cannot set the buffer size on some hardware
126                if serr.errno != errno.ENOBUFS:
127                    raise serr
128
129        self.tx_buffer_size = self.sock.getsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF)
130        self.rx_buffer_size = self.sock.getsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF)
131        self.status = 1
132       
133        if (self.tx_buffer_size != tx_buf_size):
134            msg  = "OS reduced send buffer size from "
135            msg += "{0} to {1}".format(self.tx_buffer_size, tx_buf_size) 
136            print(msg)
137           
138        if (self.rx_buffer_size != rx_buf_size):
139            msg  = "OS reduced receive buffer size from "
140            msg += "{0} to {1}".format(self.rx_buffer_size, rx_buf_size) 
141            print(msg)
142
143
144    def transport_close(self):
145        """Closes an Ethernet IP/UDP socket."""
146        if self.sock:
147            try:
148                self.sock.close()
149            except socket.error as err:
150                print("Error closing socket:  {0}".format(err))
151
152        self.status = 0
153
154
155    # -------------------------------------------------------------------------
156    # Commands that must be implemented by child classes
157    # -------------------------------------------------------------------------
158    def send(self, payload, robust=None):
159        """Send a message over the transport."""
160        raise NotImplementedError
161
162    def receive(self, timeout=None):
163        """Return a response from the transport."""
164        raise NotImplementedError
165
166
167    # -------------------------------------------------------------------------
168    # Commands for the Transport
169    # -------------------------------------------------------------------------
170    def ping(self, node, output=False):
171        """Issues a ping command to the given node.
172       
173        Will optionally print the result of the ping.
174        """
175        from . import cmds
176
177        start_time = time.clock()
178        node.send_cmd(cmds.TransportPing())
179        end_time = time.clock()
180       
181        if output:
182            ms_time = (end_time - start_time) * 1000
183            print("Reply from {0}:  time = {1:.3f} ms".format(self.ip_address, 
184                                                              ms_time))
185
186    def add_node_group_id(self, node, group):
187        from . import cmds
188
189        node.send_cmd(cmds.TransportAddNodeGroupId(group))
190
191   
192    def clear_node_group_id(self, node, group):
193        from . import cmds
194
195        node.send_cmd(cmds.TransportClearNodeGroupId(group))
196
197    # -------------------------------------------------------------------------
198    # Misc methods for the Transport
199    # -------------------------------------------------------------------------
200    def int_to_ip(self, ip_address):    return int_to_ip(ip_address)
201
202    def ip_to_int(self, ip_address):    return ip_to_int(ip_address)
203
204    def mac_addr_to_str(self, mac_address):  return mac_addr_to_str(mac_address)
205       
206    def str_to_mac_addr(self, mac_address):  return str_to_mac_addr(mac_address)
207
208    def __str__(self):
209        """Pretty print the Transport parameters"""
210        msg = ""
211        if self.mac_address is not None:
212            msg += "{0} Transport ({1}):\n".format(self.node_eth_device, transport_type_to_str(self.transport_type))
213            msg += "    IP address    :  {0}\n".format(self.ip_address)
214            msg += "    MAC address   :  {0}\n".format(self.mac_addr_to_str(self.mac_address)) 
215            msg += "    MTU           :  {0}\n".format(self.mtu)
216            msg += "    Unicast port  :  {0}\n".format(self.unicast_port)
217            msg += "    Broadcast port:  {0}\n".format(self.broadcast_port)
218            msg += "    Timeout       :  {0}\n".format(self.timeout)
219            msg += "    Rx Buffer Size:  {0}\n".format(self.rx_buffer_size)
220            msg += "    Tx Buffer Size:  {0}\n".format(self.tx_buffer_size)
221            msg += "    Group ID      :  {0}\n".format(self.group_id)
222        return msg
223       
224# End Class
225
226
227
228# -------------------------------------------------------------------------
229# Global methods for the Transport
230# -------------------------------------------------------------------------
231
232def int_to_ip(ip_address):
233    """Convert an integer to IP address string (dotted notation)."""
234    ret_val = ""
235   
236    if ip_address is not None:
237        ret_val += "{0:d}.".format((ip_address >> 24) & 0xFF)
238        ret_val += "{0:d}.".format((ip_address >> 16) & 0xFF)
239        ret_val += "{0:d}.".format((ip_address >>  8) & 0xFF)
240        ret_val += "{0:d}".format(ip_address & 0xFF)
241       
242    return ret_val
243
244# End def
245
246
247def ip_to_int(ip_address):
248    """Convert IP address string (dotted notation) to an integer."""
249    ret_val = 0
250   
251    if ip_address is not None:
252        #expr = re.compile('\.')
253        #tmp = [int(n) for n in expr.split(ip_addr)]       
254        tmp = [int(n) for n in ip_address.split('.')]       
255        ret_val = (tmp[3]) + (tmp[2] * 2**8) + (tmp[1] * 2**16) + (tmp[0] * 2**24)
256       
257    return ret_val
258
259# End def
260
261
262def mac_addr_to_str(mac_address):
263    """Convert an integer to a colon separated MAC address string."""
264    ret_val = ""
265   
266    if mac_address is not None:
267
268        # Force the input address to a Python int
269        #   This handles the case of a numpy uint64 input, which kills the >> operator
270        if(type(mac_address) is not int):
271            mac_address = int(mac_address)
272
273        ret_val += "{0:02x}:".format((mac_address >> 40) & 0xFF)
274        ret_val += "{0:02x}:".format((mac_address >> 32) & 0xFF)
275        ret_val += "{0:02x}:".format((mac_address >> 24) & 0xFF)
276        ret_val += "{0:02x}:".format((mac_address >> 16) & 0xFF)
277        ret_val += "{0:02x}:".format((mac_address >>  8) & 0xFF)
278        ret_val += "{0:02x}".format(mac_address & 0xFF)
279
280    return ret_val
281
282# End def
283
284
285def str_to_mac_addr(mac_address):
286    """Convert a MAC Address (colon separated) to an integer."""
287    ret_val = 0
288   
289    if mac_address is not None:
290       
291        expr = re.compile(':')
292        tmp = [int('0x{0}'.format(n), base=16) for n in expr.split(mac_address)]
293        ret_val = (tmp[5]) + (tmp[4] * 2**8) + (tmp[3] * 2**16) + (tmp[2] * 2**24) + (tmp[1] * 2**32) + (tmp[0] * 2**40)
294
295        # Alternate Method:
296        #
297        # ret_val = mac_address
298        # ret_val = ''.join('{0:02X}:'.format(ord(x)) for x in ret_val)[:-1]
299        # ret_val = '0x' + ret_val.replace(':', '')
300        # mac_addr_int = int(ret_val, 0)
301       
302    return ret_val
303
304# End def
305
306
307def mac_addr_to_byte_str(mac_address):
308    """Convert an integer to a MAC address byte string."""
309    ret_val = b''
310   
311    if mac_address is not None:
312        import sys
313   
314        # Fix to support Python 2.x and 3.x
315        if sys.version[0]=="2":
316            ret_val   = ""
317            int_array = [((mac_address >> ((6 - i - 1) * 8)) % 256) for i in range(6)]
318            for value in int_array:
319                ret_val += "{0}".format(chr(value))
320            ret_val = bytes(ret_val)
321        elif sys.version[0]=="3":
322            ret_val = bytes([((mac_address >> ((6 - i - 1) * 8)) % 256) for i in range(6)])
323        else:
324            msg  = "WARNING:  Cannot convert MAC Address to byte string:\n"
325            msg += "    Unsupported Python version."
326            print(msg)
327       
328    return ret_val
329
330# End def
331
332
333def byte_str_to_mac_addr(mac_address):
334    """Convert a MAC Address (byte string) to an integer."""
335    ret_val = 0
336   
337    if mac_address is not None:
338        import sys
339       
340        # Fix to support Python 2.x and 3.x
341        if sys.version[0]=="2":
342            ret_val = sum([ord(b) << (8 * i) for i, b in enumerate(mac_address[::-1])])
343        elif sys.version[0]=="3":
344            ret_val = sum([b << (8 * i) for i, b in enumerate(mac_address[::-1])])
345        else:
346            msg  = "WARNING:  Cannot convert byte string to MAC Address:\n"
347            msg += "    Unsupported Python version."
348            print(msg)
349       
350    return ret_val
351
352# End def
353
354
355def buffer_to_str(buffer):
356    """Convert buffer of bytes to a formatted string of byte values."""
357    ret_val = "    "
358   
359    for i, byte in enumerate(buffer):
360        try:
361            ret_val += "0x{0:02X} ".format(ord(byte))
362        except TypeError:
363            ret_val += "0x{0:02X} ".format(byte)
364           
365        if (((i % 16) == 0) and (i != len(buffer))):
366            ret_val += "\n    "
367           
368    ret_val += "\n"
369
370    return ret_val
371   
372# End def
373
374
375def transport_type_to_str(transport_type):
376    if (transport_type == tp.TRANSPORT_TYPE_IP_UDP):
377        return "IP/UDP"
378    else:
379        return "Type Undefined"
380   
381# End def
382
383
384def node_eth_device_to_str(eth_device):
385    if (eth_device == 0):
386        return 'ETH A'
387    elif (eth_device == 1):
388        return 'ETH B'
389    else:
390        return 'ETH Undefined'
391
392# End def
393
Note: See TracBrowser for help on using the repository browser.