Skip to content

Commit f75e2b9

Browse files
V-SANTMegaRedHand
andauthored
Refactor/improve packet information (#218)
This PR rearranges the info of packets and devices depending on the chosen layer. # Information On devices it shows: - MAC address and IP address on Link Layer - Only IP address on Network, Transport and App ## Hosts ### Link ![image](https://github.com/user-attachments/assets/ea269bba-3579-4811-afdc-c1859e9159be) ### Network ![image](https://github.com/user-attachments/assets/0aa1ee7f-0098-43c3-9da6-62a60af0b3ce) ### Transport ![image](https://github.com/user-attachments/assets/63737736-d22a-4d63-b3be-cc5e355938f2) ### App ![image](https://github.com/user-attachments/assets/5bf82c4a-1a1b-4579-bb2d-ee643005c96f) ## Routers ### Link ![image](https://github.com/user-attachments/assets/d012d512-5243-43d2-9d92-d180c4a40190) ### Network ![image](https://github.com/user-attachments/assets/1cb40b90-e02f-477e-bba9-e8c098ce1e20) ## Switches ### Link ![image](https://github.com/user-attachments/assets/6650ba7f-04eb-499e-9dbe-a39a785487e0) On packets, it shows: - MAC Address on Link ![image](https://github.com/user-attachments/assets/0f9bda43-31c9-44f3-a274-7eb742f0cf55) - IP Address on Network ![image](https://github.com/user-attachments/assets/99f0242b-f204-4998-828c-b6a586a68c4a) - IP Address and Ports on Transport and App ![image](https://github.com/user-attachments/assets/1558e010-b2eb-4b0f-b965-9799da8d77a6) ![image](https://github.com/user-attachments/assets/802d068a-1a3a-4809-912f-83c592eafb18) # Packet Details ## TCP - Encoded request on App ![image](https://github.com/user-attachments/assets/4e154c0c-10df-4379-ae4f-82d96dd5beb0) - Sequence number, Ack number, Window Size, Flags and Payload on Transport ![image](https://github.com/user-attachments/assets/db46f9b1-bbb0-4629-930a-1c75ef05a93e) - IPV4 data and Payload on Network ![image](https://github.com/user-attachments/assets/ad2d9588-9dec-40ae-8c23-78a837295288) - MAC address and EtherType on Link ![image](https://github.com/user-attachments/assets/414e1ef9-6e82-4552-8189-8e8612b697af) ## ICMP - IPV4 data and payload on Network ![image](https://github.com/user-attachments/assets/912c68e1-1d96-41d1-9fe9-696bd187b158) - MAC address and Ethertype on Link ![image](https://github.com/user-attachments/assets/dbe0cfb6-68f7-482d-b271-79b8daf23ea2) --- closes #210 closes #211 closes #212 --------- Co-authored-by: Tomás Grüner <[email protected]>
1 parent 35464ef commit f75e2b9

File tree

11 files changed

+230
-66
lines changed

11 files changed

+230
-66
lines changed

src/graphics/basic_components/text_info.ts

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { CSS_CLASSES } from "../../utils/constants/css_constants";
22
import { attachTooltip } from "../renderables/tooltip_manager";
3+
import { Table } from "./table";
4+
import { Flags, TCP_FLAGS_KEY } from "../../packets/tcp";
35

46
export interface InfoField {
57
key: string;
@@ -58,7 +60,10 @@ export class TextInfo {
5860
private createListItem(field: InfoField): HTMLLIElement {
5961
const { key, value, tooltip } = field;
6062

61-
if (typeof value === "object" && value !== null) {
63+
if (key === TCP_FLAGS_KEY) {
64+
const flags = value as Flags;
65+
return this.createFlagsElement(flags, tooltip);
66+
} else if (typeof value === "object" && value !== null) {
6267
return this.createPayloadElement(key, value, tooltip);
6368
} else {
6469
return this.createDetailElement(key, value as string, tooltip);
@@ -133,4 +138,77 @@ export class TextInfo {
133138

134139
return listItem;
135140
}
141+
142+
/**
143+
* Creates a TCP flags display element with ticks and crosses using the Table class
144+
* @param flags An object containing the TCP flag values
145+
* @param tooltip Optional tooltip for the flags element
146+
* @returns HTMLLIElement containing the flags table
147+
*/
148+
createFlagsElement(flags: Flags, tooltip?: string): HTMLLIElement {
149+
// Create list item container
150+
const listItem = document.createElement("li");
151+
listItem.classList.add(CSS_CLASSES.DETAIL_ITEM);
152+
153+
// Create key element
154+
const keyElement = document.createElement("span");
155+
keyElement.textContent = "TCP Flags";
156+
keyElement.classList.add(
157+
CSS_CLASSES.DETAIL_KEY,
158+
CSS_CLASSES.TCP_FLAG_HEADER,
159+
);
160+
keyElement.style.display = CSS_CLASSES.BLOCK;
161+
162+
if (tooltip) {
163+
attachTooltip(keyElement, tooltip);
164+
}
165+
166+
// Create rows for the table
167+
const statusRow: string[] = [];
168+
const valueRow: string[] = [];
169+
170+
Object.entries(flags).forEach(([, value]) => {
171+
statusRow.push(value ? "✓" : "✗");
172+
valueRow.push(value ? "1" : "0");
173+
});
174+
175+
// Create headers for the table
176+
const headers = Object.keys(flags);
177+
178+
// Create the table using the Table class
179+
const flagsTable = new Table({
180+
headers: headers.reduce(
181+
(acc, header) => {
182+
acc[header] = header;
183+
return acc;
184+
},
185+
{} as Record<string, string>,
186+
),
187+
fieldsPerRow: Object.keys(flags).length,
188+
rows: [statusRow, valueRow],
189+
tableClasses: [CSS_CLASSES.TABLE, CSS_CLASSES.RIGHT_BAR_TABLE],
190+
});
191+
192+
// Get the table element
193+
const tableElement = flagsTable.toHTML();
194+
195+
// Apply custom styling to the status row cells (ticks and crosses)
196+
const tbody = tableElement.querySelector("tbody");
197+
if (tbody && tbody.rows.length > 0) {
198+
const statusCells = tbody.rows[0].cells;
199+
for (const cell of statusCells) {
200+
if (cell.textContent === "✓") {
201+
cell.classList.add(CSS_CLASSES.TCP_FLAG_ACTIVE);
202+
} else {
203+
cell.classList.add(CSS_CLASSES.TCP_FLAG_INACTIVE);
204+
}
205+
}
206+
}
207+
208+
// Append elements to list item
209+
listItem.appendChild(keyElement);
210+
listItem.appendChild(tableElement);
211+
212+
return listItem;
213+
}
136214
}

src/graphics/renderables/device_info.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { CSS_CLASSES } from "../../utils/constants/css_constants";
1414
import { BaseInfo } from "./base_info";
1515
import { ProgressBar } from "../basic_components/progress_bar";
1616
import { LabeledProgressBar } from "../components/labeled_progress_bar";
17+
import { Layer } from "../../types/layer";
1718

1819
export class DeviceInfo extends BaseInfo {
1920
readonly device: ViewDevice;
@@ -34,11 +35,16 @@ export class DeviceInfo extends BaseInfo {
3435
connections,
3536
TOOLTIP_KEYS.CONNECTED_DEVICES,
3637
);
37-
this.information.addField(
38-
TOOLTIP_KEYS.MAC_ADDRESS,
39-
mac.toString(),
40-
TOOLTIP_KEYS.MAC_ADDRESS,
41-
);
38+
39+
const layer = this.device.viewgraph.getLayer();
40+
41+
if (layer == Layer.Link) {
42+
this.information.addField(
43+
TOOLTIP_KEYS.MAC_ADDRESS,
44+
mac.toString(),
45+
TOOLTIP_KEYS.MAC_ADDRESS,
46+
);
47+
}
4248
}
4349

4450
protected addCommonButtons(): void {

src/graphics/renderables/packet_info.ts

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { Button } from "../basic_components/button";
44
import { CSS_CLASSES } from "../../utils/constants/css_constants";
55
import { BaseInfo } from "./base_info";
66
import { ToggleInfo } from "../components/toggle_info";
7+
import { Layer } from "../../types/layer";
8+
import { IPv4Packet } from "../../packets/ip";
79

810
export class PacketInfo extends BaseInfo {
911
readonly packet: Packet;
@@ -22,16 +24,51 @@ export class PacketInfo extends BaseInfo {
2224
this.packet.type,
2325
TOOLTIP_KEYS.PACKET_TYPE,
2426
);
25-
this.information.addField(
26-
TOOLTIP_KEYS.SOURCE_MAC_ADDRESS,
27-
this.packet.rawPacket.source.toString(),
28-
TOOLTIP_KEYS.SOURCE_MAC_ADDRESS,
29-
);
30-
this.information.addField(
31-
TOOLTIP_KEYS.DESTINATION_MAC_ADDRESS,
32-
this.packet.rawPacket.destination.toString(),
33-
TOOLTIP_KEYS.DESTINATION_MAC_ADDRESS,
34-
);
27+
28+
const layer = this.packet.viewgraph.getLayer();
29+
const framePayload = this.packet.rawPacket.payload as IPv4Packet;
30+
31+
if (layer == Layer.Link) {
32+
this.information.addField(
33+
TOOLTIP_KEYS.SOURCE_MAC_ADDRESS,
34+
this.packet.rawPacket.source.toString(),
35+
TOOLTIP_KEYS.SOURCE_MAC_ADDRESS,
36+
);
37+
this.information.addField(
38+
TOOLTIP_KEYS.DESTINATION_MAC_ADDRESS,
39+
this.packet.rawPacket.destination.toString(),
40+
TOOLTIP_KEYS.DESTINATION_MAC_ADDRESS,
41+
);
42+
}
43+
44+
if (layer >= Layer.Network) {
45+
this.information.addField(
46+
TOOLTIP_KEYS.SOURCE_IP_ADDRESS,
47+
framePayload.sourceAddress.toString(),
48+
TOOLTIP_KEYS.SOURCE_IP_ADDRESS,
49+
);
50+
this.information.addField(
51+
TOOLTIP_KEYS.DESTINATION_IP_ADDRESS,
52+
framePayload.destinationAddress.toString(),
53+
TOOLTIP_KEYS.DESTINATION_IP_ADDRESS,
54+
);
55+
}
56+
57+
if (layer >= Layer.Transport) {
58+
const ports = framePayload.payload.getPorts();
59+
if (ports) {
60+
this.information.addField(
61+
TOOLTIP_KEYS.SOURCE_PORT,
62+
ports.sourcePort,
63+
TOOLTIP_KEYS.SOURCE_PORT,
64+
);
65+
this.information.addField(
66+
TOOLTIP_KEYS.DESTINATION_PORT,
67+
ports.destinationPort,
68+
TOOLTIP_KEYS.DESTINATION_PORT,
69+
);
70+
}
71+
}
3572
}
3673

3774
protected addCommonButtons(): void {
@@ -56,7 +93,7 @@ export class PacketInfo extends BaseInfo {
5693
private addToggleInfo(): void {
5794
// Obtener los detalles del paquete
5895
const packetDetails = this.packet.getPacketDetails(
59-
this.packet.belongingLayer,
96+
this.packet.viewgraph.getLayer(),
6097
this.packet.rawPacket,
6198
);
6299

src/packets/ethernet.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,8 @@ export class EthernetFrame {
148148
getDetails(layer: Layer) {
149149
if (layer == Layer.Link) {
150150
return {
151-
"Destination MAC": this.destination.toString(),
152151
"Source MAC": this.source.toString(),
152+
"Destination MAC": this.destination.toString(),
153153
EtherType: this.type.toString(),
154154
};
155155
} else {

src/packets/icmp.ts

Lines changed: 9 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { ICMP_PROTOCOL_NUMBER, IpPayload, computeIpChecksum } from "./ip";
2-
import { Layer } from "../types/layer";
3-
4-
const ICMP_WARNING =
5-
"ICMP operates directly on top of IP at the Network layer, bypassing the Transport layer (TCP/UDP). This is because ICMP is primarily used for network diagnostics and error reporting, not for end-to-end data transport.";
6-
1+
import {
2+
ICMP_PROTOCOL_NUMBER,
3+
IpPayload,
4+
computeIpChecksum,
5+
Ports,
6+
} from "./ip";
77
export const ICMP_REQUEST_TYPE_NUMBER = 8;
88
export const ICMP_REPLY_TYPE_NUMBER = 0;
99

@@ -60,7 +60,9 @@ export abstract class IcmpPacket implements IpPayload {
6060
return `ICMP-${this.type}`;
6161
}
6262

63-
abstract getDetails(layer: Layer): Record<string, string | number | object>;
63+
getPorts(): Ports {
64+
return null;
65+
}
6466
}
6567

6668
// 0 1 2 3
@@ -102,27 +104,6 @@ class EchoMessage extends IcmpPacket {
102104
...this.data,
103105
]);
104106
}
105-
106-
getDetails(layer: number): Record<string, string | number | object> {
107-
if (layer == Layer.Transport) {
108-
return {
109-
Warning: ICMP_WARNING,
110-
};
111-
}
112-
113-
// TODO: If we decide to hide ICMP packets on Application layer, this should be removed
114-
if (this.type == 8) {
115-
return {
116-
Application: "Ping",
117-
Task: "Echo Request",
118-
};
119-
} else {
120-
return {
121-
Application: "Ping",
122-
Task: "Echo Reply",
123-
};
124-
}
125-
}
126107
}
127108

128109
export class EchoRequest extends EchoMessage {

src/packets/ip.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ export const ICMP_PROTOCOL_NUMBER = 1;
66
export const TCP_PROTOCOL_NUMBER = 6;
77
export const UDP_PROTOCOL_NUMBER = 17;
88

9+
export interface Ports {
10+
sourcePort: number;
11+
destinationPort: number;
12+
}
13+
914
export class EmptyPayload implements IpPayload {
1015
byteLength() {
1116
return 0;
@@ -24,6 +29,10 @@ export class EmptyPayload implements IpPayload {
2429
getDetails() {
2530
return {};
2631
}
32+
33+
getPorts(): Ports {
34+
return null;
35+
}
2736
}
2837

2938
/// Internet Protocol (IP) address
@@ -130,7 +139,9 @@ export interface IpPayload {
130139
// Packet protocol name
131140
getPacketType(): string;
132141
// Get details of the payload
133-
getDetails(layer: Layer): Record<string, string | number | object>;
142+
getDetails?(layer: Layer): Record<string, string | number | object>;
143+
// Get ports of the payload (if any)
144+
getPorts(): Ports;
134145
}
135146

136147
// Info taken from the original RFC: https://datatracker.ietf.org/doc/html/rfc791#section-3.1
@@ -267,24 +278,13 @@ export class IPv4Packet implements FramePayload {
267278
}
268279

269280
getDetails(layer: Layer) {
270-
// TODO: Refactor Packet Building Process
271-
//
272-
// Current Implementation:
273-
// - Packet building starts at the Network layer
274-
// - Frame payload data is directly included here
275-
//
276-
// Desired Implementation:
277-
// - Move frame-specific data to EthernetFrame class
278-
// - Implement packet sending using MAC addresses at device level
279-
280281
if (layer == Layer.Network) {
281282
return {
282283
Version: this.version,
283284
"Internet Header Length": this.internetHeaderLength,
284285
"Type of Service": this.typeOfService,
285286
"Total Length": this.totalLength,
286287
Identification: this.identification,
287-
Flags: this.flags,
288288
"Fragment Offset": this.fragmentOffset,
289289
"Time to Live": this.timeToLive,
290290
Protocol: this.protocol,

src/packets/tcp.ts

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import {
55
TCP_PROTOCOL_NUMBER,
66
} from "./ip";
77
import { Layer } from "../types/layer";
8+
import { Ports } from "./ip";
9+
10+
export const TCP_FLAGS_KEY = "tcp_flags";
811

912
export class Flags {
1013
// Urgent Pointer field significant
@@ -211,12 +214,33 @@ export class TcpSegment implements IpPayload {
211214
return "TCP";
212215
}
213216

214-
// Dummy Method for the moment
215217
getDetails(layer: Layer) {
216-
return { Layer: layer };
218+
if (layer == Layer.Transport) {
219+
return {
220+
"Seq Number": this.sequenceNumber,
221+
"Ack Number": this.acknowledgementNumber,
222+
"Window Size": this.window,
223+
[TCP_FLAGS_KEY]: {
224+
Urg: this.flags.urg,
225+
Ack: this.flags.ack,
226+
Psh: this.flags.psh,
227+
Rst: this.flags.rst,
228+
Syn: this.flags.syn,
229+
Fin: this.flags.fin,
230+
},
231+
Payload: this.data,
232+
};
233+
} else if (layer == Layer.App) {
234+
return { Request: new TextDecoder("utf-8").decode(this.data) };
235+
}
217236
}
218237

219-
// ### IpPayload ###
238+
getPorts(): Ports {
239+
return {
240+
sourcePort: this.sourcePort,
241+
destinationPort: this.destinationPort,
242+
};
243+
}
220244
}
221245

222246
function checkUint(n: number, numBits: number): void {

src/styles/info.css

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,20 @@
9494
max-width: 100%; /* Prevents it from exceeding the container */
9595
display: block;
9696
}
97+
98+
/* TCP Flags Table Styles */
99+
.tcp-flag-active {
100+
font-weight: bold;
101+
color: #28a745;
102+
}
103+
104+
.tcp-flag-inactive {
105+
font-weight: bold;
106+
color: #dc3545;
107+
}
108+
109+
.tcp-flag-header {
110+
display: block;
111+
text-align: center;
112+
margin-bottom: 8px;
113+
}

0 commit comments

Comments
 (0)