L4Re Operating System Framework
Interface and Usage Documentation
Loading...
Searching...
No Matches
switch.cc
1/*
2 * Copyright (C) 2016-2018, 2020, 2023-2024 Kernkonzept GmbH.
3 * Author(s): Jean Wolter <jean.wolter@kernkonzept.com>
4 * Alexander Warg <warg@os.inf.tu-dresden.de>
5 *
6 * License: see LICENSE.spdx (in this directory or the directories above)
7 */
8#include "debug.h"
9#include "switch.h"
10#include "filter.h"
11
13: _ports{new Port_iface *[max_ports]()},
14 _max_ports{max_ports}
15{
16}
17
18int
19Virtio_switch::lookup_free_slot()
20{
21 for (unsigned idx = 0; idx < _max_ports; ++idx)
22 if (!_ports[idx])
23 return idx;
24
25 return -1;
26}
27
28bool
29Virtio_switch::add_port(Port_iface *port)
30{
31 if (!port->mac().is_unknown())
32 for (unsigned idx = 0; idx < _max_ports; ++idx)
33 if (_ports[idx] && _ports[idx]->mac() == port->mac())
34 {
35 Dbg(Dbg::Port, Dbg::Warn)
36 .printf("Rejecting port '%s'. MAC address already in use.\n",
37 port->get_name());
38 return false;
39 }
40
41 int idx = lookup_free_slot();
42 if (idx < 0)
43 return false;
44
45 unsigned uidx = static_cast<unsigned>(idx);
46 _ports[uidx] = port;
47 if (_max_used == uidx)
48 ++_max_used;
49
50 return true;
51}
52
53bool
55{
56 if (!_monitor)
57 {
58 _monitor = port;
59 return true;
60 }
61
62 Dbg(Dbg::Port, Dbg::Warn).printf("'%s' already defined as monitor port,"
63 " rejecting monitor port '%s'\n",
64 _monitor->get_name(), port->get_name());
65 return false;
66}
67
68void
70{
71 for (unsigned idx = 0; idx < _max_used; ++idx)
72 {
73 Port_iface *port = _ports[idx];
74 if (port && port->is_gone())
75 {
76 Dbg(Dbg::Port, Dbg::Info)
77 .printf("Client on port %p has gone. Deleting...\n", port);
78
79 _ports[idx] = nullptr;
80 if (idx == _max_used-1)
81 --_max_used;
82
83 _mac_table.flush(port);
84 delete(port);
85 }
86 }
87
88 if (_monitor && _monitor->is_gone())
89 {
90 delete(_monitor);
91 _monitor = nullptr;
92 }
93}
94
95template<typename REQ>
96void
97Virtio_switch::handle_tx_request(Port_iface *port, REQ const &request)
98{
99 // Trunk ports are required to have a VLAN tag and only accept packets that
100 // belong to a configured VLAN.
101 if (port->is_trunk() && !port->match_vlan(request.vlan_id()))
102 {
103 // Drop packet.
104 port->stat_inc_tx_dropped();
105 return;
106 }
107
108 // Access ports must not be VLAN tagged to prevent double tagging attacks.
109 if (port->is_access() && request.has_vlan())
110 {
111 // Drop packet.
112 port->stat_inc_tx_dropped();
113 return;
114 }
115
116 auto handle_request = [](Port_iface *dst_port, Port_iface *src_port,
117 REQ const &req)
118 {
119 auto transfer_src = req.transfer_src();
120 l4_uint64_t bytes;
121 auto res = dst_port->handle_request(src_port, transfer_src, &bytes);
122 switch (res)
123 {
124 case Port_iface::Result::Delivered:
125 dst_port->stat_inc_tx_num();
126 dst_port->stat_inc_tx_bytes(bytes);
127 src_port->stat_inc_rx_num();
128 src_port->stat_inc_rx_bytes(bytes);
129 break;
130 case Port_iface::Result::Dropped:
131 [[fallthrough]];
132 case Port_iface::Result::Exception:
133 [[fallthrough]];
134 default:
135 dst_port->stat_inc_tx_dropped();
136 break;
137 }
138 };
139
140 Mac_addr src = request.src_mac();
141
142 auto dst = request.dst_mac();
143 bool is_broadcast = dst.is_broadcast();
144 uint16_t vlan = request.has_vlan() ? request.vlan_id() : port->get_vlan();
145 _mac_table.learn(src, port, vlan);
146 if (L4_LIKELY(!is_broadcast))
147 {
148 auto *target = _mac_table.lookup(dst, vlan);
149 if (target)
150 {
151 // Do not send packets to the port they came in; they might
152 // be sent to us by another switch which does not know how
153 // to reach the target.
154 if (target != port)
155 {
156 handle_request(target, port, request);
157 if (_monitor && !filter_request(request))
158 handle_request(_monitor, port, request);
159 }
160 return;
161 }
162 }
163
164 // It is either a broadcast or an unknown destination - send to all
165 // known ports except the source port
166 for (unsigned idx = 0; idx < _max_used && _ports[idx]; ++idx)
167 {
168 auto *target = _ports[idx];
169 if (target != port && target->match_vlan(vlan))
170 handle_request(target, port, request);
171 }
172
173 // Send a copy to the monitor port
174 if (_monitor && !filter_request(request))
175 handle_request(_monitor, port, request);
176}
177
178template<typename PORT>
179void
180Virtio_switch::handle_tx_requests(PORT *port, unsigned &num_reqs_handled)
181{
182 while (auto req = port->get_tx_request())
183 {
184 req->dump_request(port);
185 handle_tx_request(port, *req);
186
187 if (++num_reqs_handled >= Tx_burst)
188 // Port has hit its TX burst limit.
189 break;
190 }
191}
192
193bool
195{
196 /* handle IRQ on one port for the time being */
197 if (!port->tx_work_pending())
198 Dbg(Dbg::Port, Dbg::Debug)
199 .printf("%s: Irq without pending work\n", port->get_name());
200
201 unsigned num_reqs_handled = 0;
202 do
203 {
204 port->tx_q()->disable_notify();
205 port->rx_q()->disable_notify();
206
207 if (num_reqs_handled >= Tx_burst)
208 {
209 Dbg(Dbg::Port, Dbg::Debug)
210 .printf(
211 "%s: Tx burst limit hit, reschedule remaining Tx work.\n",
212 port->get_name());
213
214 // Port has hit its TX burst limit, so for fairness reasons, stop
215 // processing TX work from this port, and instead reschedule the
216 // pending work for later.
217 port->reschedule_pending_tx();
218 // NOTE: Notifications for this port remain disabled, until eventually
219 // the reschedule handler calls `handle_l4virtio_port_tx` again.
220 return false;
221 }
222
223 // Within the loop, to trigger before enabling notifications again.
224 all_rx_notify_disable_and_remember();
225
226 try
227 {
228 // throws Bad_descriptor exceptions raised on SRC port
229 handle_tx_requests(port, num_reqs_handled);
230 }
232 {
233 Dbg(Dbg::Port, Dbg::Warn, "REQ")
234 .printf("%s: caught bad descriptor exception: %s - %i"
235 " -- Signal device error on device %p.\n",
236 __PRETTY_FUNCTION__, e.message(), e.error, port);
237 port->device_error();
238 all_rx_notify_emit_and_enable();
239 return false;
240 }
241
242 all_rx_notify_emit_and_enable();
243
244 port->tx_q()->enable_notify();
245 port->rx_q()->enable_notify();
246
247 L4virtio::wmb();
248 L4virtio::rmb();
249 }
250 while (port->tx_work_pending());
251
252 return true;
253}
254
255#if CONFIG_VNS_IXL
256bool
257Virtio_switch::handle_ixl_port_tx(Ixl_port *port)
258{
259 unsigned num_reqs_handled = 0;
260
261 all_rx_notify_disable_and_remember();
262 handle_tx_requests(port, num_reqs_handled);
263 all_rx_notify_emit_and_enable();
264
265 if (num_reqs_handled >= Tx_burst && port->tx_work_pending())
266 {
267 Dbg(Dbg::Port, Dbg::Info)
268 .printf("%s: Tx burst limit hit, reschedule remaining Tx work.\n",
269 port->get_name());
270
271 // Port has hit its TX burst limit, so for fairness reasons, stop
272 // processing TX work from this port, and instead reschedule the
273 // pending work for later.
274 port->reschedule_pending_tx();
275 return false;
276 }
277
278 return true;
279}
280#endif
281
void device_error()
Transition device into DEVICE_NEEDS_RESET state.
Definition l4virtio:1024
void enable_notify()
Clear the 'no notify' flag for this queue.
Definition virtio:284
void disable_notify()
Set the 'no notify' flag for this queue.
Definition virtio:273
A Port on the Virtio Net Switch.
bool tx_work_pending() const
Check whether there is any work pending on the transmission queue.
bool is_unknown() const
Check if the MAC address is not yet known.
Definition mac_addr.h:66
Virtqueue * rx_q()
Getter for the receive queue.
Definition virtio_net.h:307
Virtqueue * tx_q()
Getter for the transmission queue.
Definition virtio_net.h:305
void check_ports()
Check validity of ports.
Definition switch.cc:69
Virtio_switch(unsigned max_ports)
Create a switch with n ports.
Definition switch.cc:12
bool add_monitor_port(Port_iface *port)
Add a monitor port to the switch.
Definition switch.cc:54
bool handle_l4virtio_port_tx(L4virtio_port *port)
Handle TX queue of the given port.
Definition switch.cc:194
bool add_port(Port_iface *port)
Add a port to the switch.
Definition switch.cc:29
unsigned long long l4_uint64_t
Unsigned 64bit value.
Definition l4int.h:31
#define L4_LIKELY(x)
Expression is likely to execute.
Definition compiler.h:283
Exception used by Queue to indicate descriptor errors.
Definition virtio:398
char const * message() const
Get a human readable description of the error code.
Definition virtio:430