TEMU  4.4
The Terma Emulator
Ethernet.h
Go to the documentation of this file.
1 //===------------------------------------------------------------*- C++ -*-===//
2 //
3 // TEMU: The Terma Emulator
4 // (c) Terma 2020
5 // Authors: Mattias Holm <maho (at) terma.com>
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #ifndef TEMU_ETHERNET_H
10 #define TEMU_ETHERNET_H
11 
12 #include "temu-c/Support/Bitmanip.h"
13 #include "temu-c/Support/Buffer.h"
14 #include "temu-c/Support/Objsys.h"
15 
16 #include <stdint.h>
17 
18 #ifdef __cplusplus
19 extern "C" {
20 #endif
21 
22 /*
23  The ethernet device interface is EXPERIMENTAL and UNSTABLE at the
24  moment.
25 
26  MAC addresses are 6 bytes, as convention here, we encode these in a
27  uint64_t field, where the two MSBs are set to 0. The, MAC address
28  will be in host endianess.
29 
30  Ethernet simulation is based on the transmission of frames. Frames
31  do not need to have valid CRCs, but the CRC data bytes should be
32  allocated for the frames. This speeds up simulation significantly.
33 
34  CRC errors can instead be efficiently injected by flipping the
35  relevant bit in the flags field.
36 
37  Ethernet is not a point to point protocol (although in practice,
38  these days it is P2P thanks to switches which are collission free),
39  but we could immagine some one simulating an ethernet link via coax
40  in which case there may be multiple nodes connected to the same
41  cable. It is possible to tap the ethernet object by adding an
42  ethernet device to it that listens to all addresses (i.e. it
43  notifies the bus model about being "promiscuous").
44 
45  ETH support is split using multiple levels, i.e. a MAC and a PHY
46  controller. The PHY controller implements the PHY interface and
47  usually also the MII interface (there is a generic MII device that
48  can be used which implements both PHY and MII). PHY is used for
49  connecting to other PHY controllers, when sending a frame, the MAC
50  will (using e.g. DMA) get the frame data, assemble a temu_EthFrame
51  and send this to the PHY controller, the PHY controller in turn
52  forwards the frame to the destination PHY. In addition, there is an
53  MIIBus model which lets a single master control multiple PHY
54  devices. The MIIBus model implements the MII interface only and
55  simply routes the calls to the correct attached PHY device.
56 
57  So, the use is typically:
58 
59  MAC -> PHY -> ETH -> PHY -> MAC
60  */
61 
62 // Flag bits
63 #define TEMU_ETH_CRC_ERR 1
64 
65 #define TEMU_ETH_ETH_CRC_NOT_SET (1 << 1)
66 #define TEMU_ETH_IP_CRC_NOT_SET (1 << 2)
67 #define TEMU_ETH_UDP_CRC_NOT_SET (1 << 3)
68 #define TEMU_ETH_TCP_CRC_NOT_SET (1 << 4)
69 #define TEMU_ETH_NON_STANDARD_PREAMBLE (1 << 5)
70 #define TEMU_ETH_PREAMBLE_LENGTH_MASK (0xf << 6)
71 #define TEMU_ETH_PREAMBLE_LENGTH_SHIFT 6
72 
73 typedef struct {
74  uint32_t Flags; //!< Flags used for error injection
75  temu_Buff Data; //!< ETH frame data
76  uint8_t Preamble[15]; //!< Preamble bits, normally 0x[aa aa aa aa aa aa aa]
77  uint8_t Sfd; //!< Start frame delimiter, normally 0xab
78 } temu_EthFrame;
79 
80 typedef struct temu_PHYIface temu_PHYIface;
81 typedef struct temu_MACIface temu_MACIface;
82 typedef struct temu_EthernetIface temu_EthernetIface;
83 TEMU_IFACE_REFERENCE_TYPE(temu_MAC);
84 TEMU_IFACE_REFERENCE_TYPE(temu_PHY);
85 TEMU_IFACE_REFERENCE_TYPE(temu_Ethernet);
86 
87 struct temu_MACIface {
88  void (*connected)(
89  void *Dev); // Called when link is connected (i.e. cable inserted)
90  void (*disconnected)(
91  void *Dev); // Called when link is disconnected (i.e. cable removed)
92  void (*up)(void *Dev); // Called when link goes up
93  void (*down)(void *Dev); // Called when link goes down
94 
95  int (*receive)(void *Dev, temu_EthFrame *Frame); // Receive frame
96  uint64_t (*getMAC)(void *Dev); // Get MAC address
97  void (*setMAC)(void *Dev, uint64_t MAC); // Set MAC address
98 };
99 #define TEMU_MAC_IFACE_TYPE "temu::MACIface"
100 
102  void (*connected)(
103  void *Dev,
104  temu_EthernetIfaceRef
105  Link); // Called when link is connected (i.e. cable inserted)
106  void (*disconnected)(
107  void *Dev,
108  temu_EthernetIfaceRef
109  Link); // Called when link is disconnected (i.e. cable removed), note
110  // that the PHY must remove the MACs from the link
111  void (*up)(void *Dev); // Called when link goes up
112  void (*down)(void *Dev); // Called when link goes down
113 
114  int (*send)(void *Dev, temu_EthFrame *Frame); // Send frame from MAC
115  int (*receive)(void *Dev, temu_EthFrame *Frame); // Receive frame
116 
117  // PHY should clear bits not related to its capabilities in the status and
118  // ext status registers
119  uint32_t (*autoNegotiate)(void *Obj, uint32_t Caps); // Do auto-negotiatio
120  void (*autoNegotiateDone)(void *Obj, uint32_t Caps);
121 };
122 #define TEMU_PHY_IFACE_TYPE "temu::PHYIface"
123 
124 // These bits should be transferred using auto-negotiation requests
125 // Matches MII status reg (reg 1), but needs to be shifted 8 bits up
126 #define TEMU_ETH_100BASE_T4 (1 << (15 - 8))
127 #define TEMU_ETH_100BASE_X_FD (1 << (14 - 8))
128 #define TEMU_ETH_100BASE_X_HD (1 << (13 - 8))
129 #define TEMU_ETH_10BASE_FD (1 << (12 - 8))
130 #define TEMU_ETH_10BASE_HD (1 << (11 - 8))
131 #define TEMU_ETH_100BASE_T2_FD (1 << (10 - 8))
132 #define TEMU_ETH_100BASE_T2_HD (1 << (9 - 8))
133 
134 // Matches MII extended status register (reg 15)
135 #define TEMU_ETH_1000BASE_X_FD (1 << 15)
136 #define TEMU_ETH_1000BASE_X_HD (1 << 14)
137 #define TEMU_ETH_1000BASE_T_FD (1 << 13)
138 #define TEMU_ETH_1000BASE_T_HD (1 << 12)
139 
140 // Interface for the actual link, a link can be a switch, hub, or a
141 // direct connection, note that ETH is not seen as a Point to Point
142 // connection for the reason of supporting HUBs, splitters etc.
144  void (*connect)(void *Obj,
145  temu_PHYIfaceRef Dev); // Called to connect a device
146  void (*disconnect)(void *Obj,
147  temu_PHYIfaceRef Dev); // Called to disconnect link
148 
149  // Ensure the device receives messages for this MAC address
150  // Note, do not forget to add broadcast and multicast bits
151  void (*addMAC)(void *Obj, temu_PHYIfaceRef Dev, uint64_t MAC);
152  void (*removeMAC)(void *Obj, temu_PHYIfaceRef Dev, uint64_t MAC);
153  void (*setPromiscuous)(void *Obj, temu_PHYIfaceRef Dev, int PromiscuousMode);
154 
155  // Do auto-negotiation, this results in queries on all attached devices
156  // that determines the shared link speed
157  uint32_t (*autoNegotiate)(void *Obj, uint32_t Caps);
158 
159  // Send frame to virtual ethernet cable return non zero if frame is
160  // accepted, link will route the frame to relevant devices
161  int (*send)(void *Obj, temu_EthFrame *Frame);
162 };
163 #define TEMU_ETHERNET_IFACE_TYPE "temu::EthernetIface"
164 
165 // I.e. Eth::connect() -> MAC::connected()
166 // I.e. Phy::send() -> Eth::receive()
167 
168 // MDIO interface
169 // The MDIO interface is for creating devices connected to an MDIO bus
170 // or to simulate an MDIO, note that we do not actually
171 // simulate MDIO per see, but do support mulpiple PHY controllers to
172 // be attached to the same bus.
173 typedef struct {
174  // The readReg returns negative if the call failed, it can fail if
175  // the access is to a bus and the PHY_ID is invalid or if the
176  // address is invalid. A successful means that the result can be
177  // safely be converted to a uint16_t.
178  int32_t (*readReg)(void *Obj, unsigned PHY_ID, uint16_t Addr);
179  int (*writeReg)(void *Obj, unsigned PHY_ID, uint16_t Addr, uint16_t Value);
180 } temu_MDIOIface;
181 #define TEMU_MDIO_IFACE_TYPE "temu::MDIOIface"
182 TEMU_IFACE_REFERENCE_TYPE(temu_MDIO);
183 
184 // Interface for adding a delay object in the connection
185 // between eth link and eth device
187  void (*addDelay)(temu_Object *link, temu_Object *device, temu_Object *delay,
188  double seconds); // Called to connect a delay object
189 };
190 #define TEMU_ETH_DELAY_IFACE_TYPE "temu::EthDelayIface"
191 
192 #define TEMU_ETH_MIN_PAYLOAD 46
193 #define TEMU_ETH_MAX_PAYLOAD 1500
194 #define TEMU_ETH_LAYER1_HEADER_LENGTH (7 + 1 + 12 + 2)
195 #define TEMU_ETH_LAYER2_HEADER_LENGTH (6 + 6 + 2)
196 #define TEMU_ETH_CRC_LENGTH 4
197 #define TEMU_ETH_INTERPACKET_GAP 12
198 #define TEMU_ETH_PAYLOAD_OFFSET (6 + 6 + 2)
199 #define TEMU_ETH_MAX_FRAME_LENGTH 1518
200 #define TEMU_ETH_802_1Q_TAG_BYTES 4
201 #define TEMU_ETH_802_1Q_MAX_FRAME_LENGTH 1522
202 
203 // An assortment of ethertype constants, non exhaustive.
204 // See: https://www.iana.org/assignments/ieee-802-numbers/ieee-802-numbers.xhtml
205 // for a more complete list.
206 #define TEMU_ETHTYPE_IPV4 0x0800
207 #define TEMU_ETHTYPE_ARP 0x0806
208 #define TEMU_ETHTYPE_WAKE_ON_LAN 0x0842
209 #define TEMU_ETHTYPE_SRP 0x22ea
210 #define TEMU_ETHTYPE_RARP 0x8035
211 #define TEMU_ETHTYPE_802_1Q 0x8100
212 #define TEMU_ETHTYPE_IPV6 0x86dd
213 #define TEMU_ETHTYPE_PTP 0x88f7
214 #define TEMU_ETHTYPE_TTE_CTRL 0x891d
215 
216 // Note, this excludes 802.1Q field
217 #define TEMU_ETH_IPV4_VERS_IHL_OFFSET (TEMU_ETH_PAYLOAD_OFFSET + 0)
218 #define TEMU_ETH_IPV4_DSCP_ECN_OFFSET (TEMU_ETH_PAYLOAD_OFFSET + 1)
219 #define TEMU_ETH_IPV4_TOTAL_LENGTH_OFFSET (TEMU_ETH_PAYLOAD_OFFSET + 2)
220 #define TEMU_ETH_IPV4_FRAGMENTATION_INFO_OFFSET (TEMU_ETH_PAYLOAD_OFFSET + 6)
221 #define TEMU_ETH_IPV4_TTL_OFFSET (TEMU_ETH_PAYLOAD_OFFSET + 8)
222 #define TEMU_ETH_IPV4_PROTOCOL_OFFSET (TEMU_ETH_PAYLOAD_OFFSET + 9)
223 #define TEMU_ETH_IPV4_CRC_OFFSET (TEMU_ETH_PAYLOAD_OFFSET + 10)
224 #define TEMU_ETH_IPV4_SRC_IP_OFFSET (TEMU_ETH_PAYLOAD_OFFSET + 12)
225 #define TEMU_ETH_IPV4_DST_IP_OFFSET (TEMU_ETH_PAYLOAD_OFFSET + 16)
226 
227 #define TEMU_IP_PROT_TCP 0x06
228 #define TEMU_IP_PROT_UDP 0x11
229 
230 // Relative to start of TCP/UDP headers
231 #define TEMU_TCP_CRC_OFFSET 16
232 #define TEMU_UDP_CRC_OFFSET 6
233 
234 static inline uint64_t
235 temu_ethGetDestMAC(temu_EthFrame *Frame)
236 {
237  uint64_t MAC = 0;
238  const uint8_t *Data = temu_buffReadableData(&Frame->Data);
239 
240  for (int i = 0; i < 6; ++i) {
241  MAC <<= 8;
242  MAC |= Data[i];
243  }
244  return MAC;
245 }
246 
247 static inline uint64_t
248 temu_ethGetSourceMAC(temu_EthFrame *Frame)
249 {
250  uint64_t MAC = 0;
251  const uint8_t *Data = temu_buffReadableData(&Frame->Data);
252 
253  for (int i = 6; i < 12; ++i) {
254  MAC <<= 8;
255  MAC |= Data[i];
256  }
257  return MAC;
258 }
259 
260 /*!
261  * Get length / EtherType field.
262  * By ethernet standard, the size field which is 16 bit, will be a length if it
263  * is <= 1500 and an ethertype tag if it is >= 1536.
264  */
265 static inline uint16_t
266 temu_ethGetEthTypeSizeField(temu_EthFrame *Frame)
267 {
268  uint16_t Size = 0;
269  const uint8_t *Data = temu_buffReadableData(&Frame->Data);
270 
271  for (int i = 12; i < 14; ++i) {
272  Size <<= 8;
273  Size |= Data[i];
274  }
275 
276  if (Size == TEMU_ETHTYPE_802_1Q) {
277  Size = 0;
278  for (int i = 12 + TEMU_ETH_802_1Q_TAG_BYTES;
279  i < 14 + TEMU_ETH_802_1Q_TAG_BYTES; ++i) {
280  Size <<= 8;
281  Size |= Data[i];
282  }
283  }
284 
285  return Size;
286 }
287 
288 static inline bool
289 temu_ethIs8021Q(temu_EthFrame *Frame)
290 {
291  uint16_t TagStart = 0;
292  const uint8_t *Data = temu_buffReadableData(&Frame->Data);
293 
294  for (int i = 12; i < 14; ++i) {
295  TagStart <<= 8;
296  TagStart |= Data[i];
297  }
298 
299  return TagStart == TEMU_ETHTYPE_802_1Q;
300 }
301 
302 static inline uint16_t
303 temu_eth8021QTagBytes(temu_EthFrame *Frame)
304 {
305  return temu_ethIs8021Q(Frame) ? TEMU_ETH_802_1Q_TAG_BYTES : 0;
306 }
307 
308 static inline uint16_t
309 temu_ethGetLength(temu_EthFrame *Frame)
310 {
311  // Should compute number of bits / bytes for the whole frame
312  // including headers
313  return temu_buffLen(&Frame->Data);
314 }
315 
316 /*!
317  * Get a readable pointer to the payload.
318  * Note that the first bytes will often be an LLC header, it may not be your
319  * actual data payload.
320  */
321 static inline const uint8_t *
322 temu_ethGetPayload(temu_EthFrame *Frame)
323 {
324  const uint8_t *Data = temu_buffReadableData(&Frame->Data);
325  return &Data[TEMU_ETH_PAYLOAD_OFFSET + temu_eth8021QTagBytes(Frame)];
326 }
327 
328 // Return actual payload size (including padding)
329 static inline const uint16_t
330 temu_ethGetPayloadAndPaddingSize(temu_EthFrame *Frame)
331 {
332  return temu_ethGetLength(Frame) - TEMU_ETH_PAYLOAD_OFFSET -
333  TEMU_ETH_CRC_LENGTH - temu_eth8021QTagBytes(Frame);
334 }
335 
336 #define ETH_BCAST_ADDR UINT64_C(0xffffffffffff)
337 
338 static inline int
339 temu_ethIsBroadcastFrame(temu_EthFrame *Frame)
340 {
341  uint64_t MAC = temu_ethGetDestMAC(Frame);
342  if (MAC == ETH_BCAST_ADDR) {
343  return 1;
344  }
345 
346  return 0;
347 }
348 
349 // Return 1 if frame is multicast (OR BROADCAST)
350 static inline int
351 temu_ethIsMulticastFrame(temu_EthFrame *Frame)
352 {
353  const uint8_t *Data = temu_buffReadableData(&Frame->Data);
354 
355  if (Data[0] & 1)
356  return 1;
357  return 0;
358 }
359 
360 // Len field specify ethertype if larger or equal to this value
361 #define ETH_ETHERTYPE_MIN 1536
362 
363 #define ETH_CRC_MAGIC 0xC704DD7B
364 #define ETH_CRC_POLY_NORM 0x04C11DB7
365 #define ETH_CRC_POLY_REV 0xEDB88320
366 #define ETH_CRC_POLY_REV_RECIP 0x82608EDB
367 
368 #define ETH_CRC_POLY_LE 0xedb88320
369 #define ETH_CRC_POLY_BE 0x04c11db6
370 
371 #ifdef __cplusplus
372 }
373 #endif
374 #endif /* !TEMU_ETHERNET_H */
TEMU_ETHTYPE_802_1Q
#define TEMU_ETHTYPE_802_1Q
Definition: Ethernet.h:211
temu_EthernetIface::removeMAC
void(* removeMAC)(void *Obj, temu_PHYIfaceRef Dev, uint64_t MAC)
Definition: Ethernet.h:152
TEMU_ETH_PAYLOAD_OFFSET
#define TEMU_ETH_PAYLOAD_OFFSET
Definition: Ethernet.h:198
temu_EthernetIface::send
int(* send)(void *Obj, temu_EthFrame *Frame)
Definition: Ethernet.h:161
temu_PHYIface::disconnected
void(* disconnected)(void *Dev, temu_EthernetIfaceRef Link)
Definition: Ethernet.h:106
temu_PHYIface::connected
void(* connected)(void *Dev, temu_EthernetIfaceRef Link)
Definition: Ethernet.h:102
temu_EthFrame::Data
temu_Buff Data
ETH frame data.
Definition: Ethernet.h:75
temu_MACIface::up
void(* up)(void *Dev)
Definition: Ethernet.h:92
temu_EthDelayIface
Definition: Ethernet.h:186
temu_PHYIface
struct temu_PHYIface temu_PHYIface
Definition: Ethernet.h:80
temu_PHYIface::down
void(* down)(void *Dev)
Definition: Ethernet.h:112
temu_PHYIface::autoNegotiateDone
void(* autoNegotiateDone)(void *Obj, uint32_t Caps)
Definition: Ethernet.h:120
temu_MDIOIface::writeReg
int(* writeReg)(void *Obj, unsigned PHY_ID, uint16_t Addr, uint16_t Value)
Definition: Ethernet.h:179
temu_MACIface::receive
int(* receive)(void *Dev, temu_EthFrame *Frame)
Definition: Ethernet.h:95
temu_MACIface::setMAC
void(* setMAC)(void *Dev, uint64_t MAC)
Definition: Ethernet.h:97
ETH_BCAST_ADDR
#define ETH_BCAST_ADDR
Definition: Ethernet.h:336
temu_EthFrame::Flags
uint32_t Flags
Flags used for error injection.
Definition: Ethernet.h:74
temu_EthernetIface::setPromiscuous
void(* setPromiscuous)(void *Obj, temu_PHYIfaceRef Dev, int PromiscuousMode)
Definition: Ethernet.h:153
temu_EthFrame::Preamble
uint8_t Preamble[15]
Preamble bits, normally 0x[aa aa aa aa aa aa aa].
Definition: Ethernet.h:76
temu_EthFrame::Sfd
uint8_t Sfd
Start frame delimiter, normally 0xab.
Definition: Ethernet.h:77
temu_EthDelayIface::addDelay
void(* addDelay)(temu_Object *link, temu_Object *device, temu_Object *delay, double seconds)
Definition: Ethernet.h:187
TEMU_ETH_CRC_LENGTH
#define TEMU_ETH_CRC_LENGTH
Definition: Ethernet.h:196
temu_EthernetIface::addMAC
void(* addMAC)(void *Obj, temu_PHYIfaceRef Dev, uint64_t MAC)
Definition: Ethernet.h:151
temu_MACIface
struct temu_MACIface temu_MACIface
Definition: Ethernet.h:81
TEMU_ETH_802_1Q_TAG_BYTES
#define TEMU_ETH_802_1Q_TAG_BYTES
Definition: Ethernet.h:200
temu_MACIface::connected
void(* connected)(void *Dev)
Definition: Ethernet.h:88
temu_PHYIface::send
int(* send)(void *Dev, temu_EthFrame *Frame)
Definition: Ethernet.h:114
temu_EthernetIface
struct temu_EthernetIface temu_EthernetIface
Definition: Ethernet.h:82
temu_PHYIface::up
void(* up)(void *Dev)
Definition: Ethernet.h:111
temu_EthernetIface::disconnect
void(* disconnect)(void *Obj, temu_PHYIfaceRef Dev)
Definition: Ethernet.h:146
temu_EthernetIface::connect
void(* connect)(void *Obj, temu_PHYIfaceRef Dev)
Definition: Ethernet.h:144
temu_MACIface::down
void(* down)(void *Dev)
Definition: Ethernet.h:93
temu_MACIface::disconnected
void(* disconnected)(void *Dev)
Definition: Ethernet.h:90
temu_PHYIface::receive
int(* receive)(void *Dev, temu_EthFrame *Frame)
Definition: Ethernet.h:115