aboutsummaryrefslogtreecommitdiffstats
path: root/mpm/python/usrp_mpm/ethtable.py
blob: 4a656698787e523940574ad91adfc23bc13ce369 (plain)
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
#
# Copyright 2017-2018 Ettus Research, a National Instruments Company
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
"""
Ethernet dispatcher table control
"""

from builtins import str
from builtins import object
import netaddr
from usrp_mpm.mpmlog import get_logger
from usrp_mpm.sys_utils.uio import UIO
from usrp_mpm.sys_utils.net import get_mac_addr


class EthDispatcherTable(object):
    """
    Controls an Ethernet dispatcher table.
    """
    DEFAULT_VITA_PORT = (49153, 49154)
    # Address offsets:
    ETH_IP_OFFSET = 0x0000
    ETH_PORT_OFFSET = 0x0004
    FORWARD_ETH_BCAST_OFFSET = 0x0008
    BRIDGE_MAC_LO_OFFSET = 0x0010
    BRIDGE_MAC_HI_OFFSET = 0x0014
    BRIDGE_IP_OFFSET = 0x0018
    BRIDGE_PORT_OFFSET = 0x001c
    BRIDGE_ENABLE_OFFSET = 0x0020
    SID_IP_OFFSET = 0x1000
    SID_PORT_MAC_HI_OFFSET = 0x1400
    SID_MAC_LO_OFFSET = 0x1800
    LADDR_IP_OFFSET = 0x1D00
    LADDR_PORT_MAC_HI_OFFSET = 0x1E00
    LADDR_MAC_LO_OFFSET = 0x1F00


    def __init__(self, label):
        self.log = get_logger(label)
        self._regs = UIO(label=label, read_only=False)
        self.poke32 = self._regs.poke32
        self.peek32 = self._regs.peek32

    def set_bridge_mode(self, bridge_mode):
        " Enable/Disable Bridge Mode "
        self.log.trace("Bridge Mode {}".format(
            "Enabled" if bridge_mode else "Disabled"
        ))
        self.poke32(self.BRIDGE_ENABLE_OFFSET, int(bridge_mode))

    def set_bridge_mac_addr(self, mac_addr):
        """
        Set the bridge MAC address for this Ethernet dispatcher.
        Outgoing packets will have this MAC address.
        """
        self.log.debug("Setting bridge MAC address to `{}'".format(mac_addr))
        mac_addr_int = int(netaddr.EUI(mac_addr))
        self.log.trace("Writing to address 0x{:04X}: 0x{:04X}".format(
            self.BRIDGE_MAC_LO_OFFSET, mac_addr_int & 0xFFFFFFFF
        ))
        self.poke32(self.BRIDGE_MAC_LO_OFFSET, mac_addr_int & 0xFFFFFFFF)
        self.log.trace("Writing to address 0x{:04X}: 0x{:04X}".format(
            self.BRIDGE_MAC_HI_OFFSET, mac_addr_int >> 32
        ))
        self.poke32(self.BRIDGE_MAC_HI_OFFSET, mac_addr_int >> 32)

    def set_ipv4_addr(self, ip_addr, bridge_en=False):
        """
        Set the own IPv4 address for this Ethernet dispatcher.
        Outgoing packets will have this IP address.
        """
        if bridge_en:
            own_ip_offset = self.BRIDGE_IP_OFFSET
        else:
            own_ip_offset = self.ETH_IP_OFFSET
        self.log.debug("Setting my own IP address to `{}'".format(ip_addr))
        ip_addr_int = int(netaddr.IPAddress(ip_addr))
        with self._regs:
            self.poke32(own_ip_offset, ip_addr_int)

    def set_vita_port(self, port_value=None, port_idx=None, bridge_en=False):
        """
        Set the port that is used for incoming VITA traffic. This is used to
        distinguish traffic that goes to the FPGA from that going to the ARM.
        """
        port_idx = port_idx or 0
        port_value = port_value or self.DEFAULT_VITA_PORT[port_idx]
        assert port_idx in (0)                      #FIXME: Fix port_idx = 1
        if bridge_en:
            port_reg_addr = self.BRIDGE_PORT_OFFSET
        else:
            port_reg_addr = self.ETH_PORT_OFFSET
        with self._regs:
            self.poke32(port_reg_addr, port_value)

    def set_route(self, sid, ip_addr, udp_port, mac_addr=None):
        """
        Sets up routing in the Ethernet dispatcher. From sid, only the
        destination part is important. After this call, any CHDR packet
        reaching this Ethernet dispatcher will get routed to `ip_addr' and
        `udp_port'.

        It automatically looks up the MAC address of the destination unless a
        MAC address is given.

        sid -- Full SID, but only destination part matters.
        ip_addr -- IPv4 destination address. String format ("1.2.3.4").
        udp_addr -- Destination UDP port.
        mac_addr -- If given, this will replace an ARP lookup for the MAC
                    address. String format, ("aa:bb:cc:dd:ee:ff"), case does
                    not matter.
        """
        udp_port = int(udp_port)
        if mac_addr is None:
            mac_addr = get_mac_addr(ip_addr)
        if mac_addr is None:
            self.log.error(
                "Could not resolve a MAC address for IP address `{}'".format(ip_addr)
            )
        if sid.dst_addr == 0 or sid.dst_addr == 1:
            table_addr = sid.dst_ep
            ip_base_offset = self.SID_IP_OFFSET
            port_mac_hi_base_offset = self.SID_PORT_MAC_HI_OFFSET
            mac_lo_base_offset = self.SID_MAC_LO_OFFSET
            self.log.debug(
                "Routing SID `{sid}' (endpoint `{ep}') to IP address `{ip}', " \
                "MAC address `{mac}', port `{port}'".format(
                    sid=str(sid),
                    ep=table_addr,
                    ip=ip_addr,
                    mac=mac_addr,
                    port=udp_port
                )
            )
        else: #populate local addr eth dispatch table
            table_addr = sid.dst_addr
            ip_base_offset = self.LADDR_IP_OFFSET
            port_mac_hi_base_offset = self.LADDR_PORT_MAC_HI_OFFSET
            mac_lo_base_offset = self.LADDR_MAC_LO_OFFSET
            self.log.debug(
                "Routing SID `{sid}' (local addr`{addr}') to IP address `{ip}', " \
                "MAC address `{mac}', port `{port}'".format(
                    sid=str(sid),
                    addr=table_addr,
                    ip=ip_addr,
                    mac=mac_addr,
                    port=udp_port
                )
            )
        ip_addr_int = int(netaddr.IPAddress(ip_addr))
        mac_addr_int = int(netaddr.EUI(mac_addr))
        dst_offset = 4 * table_addr

        def poke_and_trace(addr, data):
            " Do a poke32() and log.trace() "
            self.log.trace("Writing to address 0x{:04X}: 0x{:04X}".format(
                addr, data
            ))
            self.poke32(addr, data)

        with self._regs:
            poke_and_trace(
                ip_base_offset + dst_offset,
                ip_addr_int
            )
            poke_and_trace(
                mac_lo_base_offset + dst_offset,
                mac_addr_int & 0xFFFFFFFF,
            )
            poke_and_trace(
                port_mac_hi_base_offset + dst_offset,
                (udp_port << 16) | (mac_addr_int >> 32)
            )

    def set_forward_policy(self, forward_eth, forward_bcast):
        """
        Forward Ethernet packet not matching OWN_IP to CROSSOVER
        Forward broadcast packet to CPU and CROSSOVER
        """
        reg_value = int(bool(forward_eth) << 1) | int(bool(forward_bcast))
        self.log.trace("Writing to address 0x{:04X}: 0x{:04X}".format(
            self.FORWARD_ETH_BCAST_OFFSET, reg_value
        ))
        with self._regs:
            self.poke32(self.FORWARD_ETH_BCAST_OFFSET, reg_value)