Skip to content

Commit 491c2fd

Browse files
authored
Filter data between layers (#133)
This pull request includes significant refactoring and enhancements to the packet handling and UI components in the project. The most important changes include the introduction of a new `Layer` type, improvements to the `createToggleInfo` function, and the addition of detailed packet information based on network layers. ### Packet Handling Enhancements: * Added a `Layer` type to represent different network layers and integrated it into packet handling functions. [[1]](diffhunk://#diff-8cfa69637400e5dcf458a21251cdc017dd839965ff80eabcd7b8c8753add795cR2-R5) [[2]](diffhunk://#diff-994b9ef4337ca516e723b42da903ee81d4f40ebd6d763ba5901f85046fce3c26R2) * Updated `IpPayload` and its implementations (`IcmpPacket`, `TcpSegment`, `IPv4Packet`) to include a `getDetails` method that returns detailed information based on the network layer. [[1]](diffhunk://#diff-8cfa69637400e5dcf458a21251cdc017dd839965ff80eabcd7b8c8753add795cR56-R57) [[2]](diffhunk://#diff-994b9ef4337ca516e723b42da903ee81d4f40ebd6d763ba5901f85046fce3c26R20-R23) [[3]](diffhunk://#diff-994b9ef4337ca516e723b42da903ee81d4f40ebd6d763ba5901f85046fce3c26R127-R128) [[4]](diffhunk://#diff-994b9ef4337ca516e723b42da903ee81d4f40ebd6d763ba5901f85046fce3c26R262-R301) [[5]](diffhunk://#diff-31f0da4d2b4af8ea4de3ed7b0c5ca53c82fa6c81319c0f39314844bc7c89678dR183-R187) ### UI Component Improvements: * Refactored `createToggleInfo` to use a configuration object and modularized the creation of different types of detail elements (e.g., warning, payload, regular detail). [[1]](diffhunk://#diff-2ffc3b298d27be79ce1fc85f908ca77f488f67ee59e05d3d17b8a9e1aad6d696L90-R95) [[2]](diffhunk://#diff-2ffc3b298d27be79ce1fc85f908ca77f488f67ee59e05d3d17b8a9e1aad6d696L352-R413) [[3]](diffhunk://#diff-2ffc3b298d27be79ce1fc85f908ca77f488f67ee59e05d3d17b8a9e1aad6d696L402-R461) * Added new CSS styles for warning messages in the `info.css` file. ### Error Handling: * Improved error handling in the `EchoServer` class by adding a check to ensure the `echoProgram` is initialized before proceeding. ### Code Cleanup: * Removed redundant code for packet details extraction and replaced it with the new `getDetails` method. [[1]](diffhunk://#diff-e546d6337dcb4e908b738659b527f00387c07ab30674ab0dfbd61edab3d03ff4L77-R79) [[2]](diffhunk://#diff-e546d6337dcb4e908b738659b527f00387c07ab30674ab0dfbd61edab3d03ff4L146-R112)
1 parent e0154fd commit 491c2fd

File tree

7 files changed

+223
-79
lines changed

7 files changed

+223
-79
lines changed

src/graphics/right_bar.ts

Lines changed: 97 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,12 @@ export class RightBar {
8787
buttonClass = "right-bar-toggle-button",
8888
infoClass = "right-bar-info",
8989
) {
90-
const container = createToggleInfo(title, details, buttonClass, infoClass);
90+
const container = createToggleInfo({
91+
title,
92+
details,
93+
buttonClass,
94+
infoClass,
95+
});
9196
const infoContent = document.getElementById("info-content");
9297

9398
if (infoContent) {
@@ -371,57 +376,116 @@ function isValidInterface(interfaceStr: string): boolean {
371376
return interfacePattern.test(interfaceStr);
372377
}
373378

374-
export function createToggleInfo(
375-
title: string,
376-
details: Record<string, string | number | object>,
377-
buttonClass = "right-bar-toggle-button",
378-
infoClass = "right-bar-info",
379-
) {
380-
const container = document.createElement("div");
381-
container.classList.add("toggle-info-container");
379+
interface ToggleInfoConfig {
380+
title: string;
381+
details: Record<string, string | number | object>;
382+
buttonClass?: string;
383+
infoClass?: string;
384+
}
385+
386+
// Function to create warning element
387+
function createWarningElement(key: string, value: string): HTMLLIElement {
388+
const listItem = document.createElement("li");
389+
const warningSign = document.createElement("div");
390+
warningSign.classList.add("warning-sign");
391+
392+
const warningTitle = document.createElement("h3");
393+
warningTitle.classList.add("warning-title");
394+
warningTitle.textContent = key;
395+
396+
const warningMessage = document.createElement("p");
397+
warningMessage.classList.add("warning-message");
398+
warningMessage.textContent = String(value);
382399

383-
// Create toggle button
400+
warningSign.appendChild(warningTitle);
401+
warningSign.appendChild(warningMessage);
402+
listItem.appendChild(warningSign);
403+
404+
return listItem;
405+
}
406+
407+
// Function to create payload element
408+
function createPayloadElement(key: string, value: object): HTMLLIElement {
409+
const listItem = document.createElement("li");
410+
const pre = document.createElement("pre");
411+
pre.textContent = JSON.stringify(value, null, 2); // Pretty-print JSON
412+
listItem.innerHTML = `<strong>${key}:</strong>`;
413+
listItem.appendChild(pre);
414+
415+
return listItem;
416+
}
417+
418+
// Function to create regular detail element
419+
function createDetailElement(key: string, value: string): HTMLLIElement {
420+
const listItem = document.createElement("li");
421+
listItem.innerHTML = `<strong>${key}:</strong> ${value}`;
422+
return listItem;
423+
}
424+
425+
// Function to create toggle button
426+
function createInfoToggleButton(
427+
buttonClass: string,
428+
list: HTMLUListElement,
429+
container: HTMLDivElement,
430+
header: HTMLHeadingElement,
431+
): HTMLButtonElement {
384432
const button = document.createElement("button");
385433
button.classList.add(buttonClass);
386434
button.textContent = "Show Details";
387435

388-
// Create Packet Details title
436+
button.onclick = () => {
437+
const isHidden = list.classList.contains("hidden");
438+
439+
if (isHidden) {
440+
list.classList.remove("hidden");
441+
header.classList.remove("hidden");
442+
button.textContent = "Hide Details";
443+
button.classList.add("open");
444+
} else {
445+
list.classList.add("hidden");
446+
header.classList.add("hidden");
447+
button.textContent = "Show Details";
448+
button.classList.remove("open");
449+
}
450+
};
451+
452+
return button;
453+
}
454+
455+
// Main function to create toggle info
456+
export function createToggleInfo({
457+
title,
458+
details,
459+
buttonClass = "right-bar-toggle-button",
460+
infoClass = "right-bar-info",
461+
}: ToggleInfoConfig): HTMLDivElement {
462+
const container = document.createElement("div");
463+
container.classList.add("toggle-info-container");
464+
389465
const header = document.createElement("h3");
390466
header.classList.toggle("hidden", true);
391467
header.textContent = title;
392468

393-
// Create info list
394469
const list = document.createElement("ul");
395470
list.classList.add(infoClass, "hidden");
396471

397-
// Add details to the list
472+
// Process details
398473
Object.entries(details).forEach(([key, value]) => {
399-
const listItem = document.createElement("li");
400-
if (key === "Payload") {
401-
// Format the payload as JSON
402-
const pre = document.createElement("pre");
403-
pre.textContent = JSON.stringify(value, null, 2); // Pretty-print JSON
404-
listItem.innerHTML = `<strong>${key}:</strong>`;
405-
listItem.appendChild(pre);
474+
let listItem: HTMLLIElement;
475+
476+
if (typeof value === "object" && value !== null) {
477+
listItem = createPayloadElement(key, value);
478+
} else if (key === "Warning") {
479+
listItem = createWarningElement(key, value as string);
406480
} else {
407-
listItem.innerHTML = `<strong>${key}:</strong> ${value}`;
481+
listItem = createDetailElement(key, value as string);
408482
}
483+
409484
list.appendChild(listItem);
410485
});
411486

412-
// Toggle when clicking on button
413-
button.onclick = () => {
414-
const isHidden = list.classList.contains("hidden");
415-
list.classList.toggle("hidden", !isHidden);
416-
list.classList.toggle("open", isHidden);
417-
container.classList.toggle("hidden", !isHidden);
418-
container.classList.toggle("open", isHidden);
419-
button.classList.toggle("open", isHidden);
420-
header.classList.toggle("hidden", !isHidden);
421-
button.textContent = isHidden ? "Hide Details" : "Show Details";
422-
};
487+
const button = createInfoToggleButton(buttonClass, list, container, header);
423488

424-
// Add elements to container
425489
container.appendChild(button);
426490
container.appendChild(header);
427491
container.appendChild(list);

src/packets/icmp.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
import { ICMP_PROTOCOL_NUMBER, IpPayload, computeIpChecksum } from "./ip";
2+
import { Layer } from "../types/devices/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.";
26

37
// More info in RFC-792
48
// 0 1 2 3
@@ -49,6 +53,8 @@ abstract class IcmpPacket implements IpPayload {
4953
getPacketType(): string {
5054
return `ICMP-${this.type}`;
5155
}
56+
57+
abstract getDetails(layer: Layer): Record<string, string | number | object>;
5258
}
5359

5460
class EchoMessage extends IcmpPacket {
@@ -81,6 +87,27 @@ class EchoMessage extends IcmpPacket {
8187
...this.data,
8288
]);
8389
}
90+
91+
getDetails(layer: number): Record<string, string | number | object> {
92+
if (layer == Layer.Transport) {
93+
return {
94+
Warning: ICMP_WARNING,
95+
};
96+
}
97+
98+
// TODO: If we decide to hide ICMP packets on Application layer, this should be removed
99+
if (this.type == 8) {
100+
return {
101+
Application: "Ping",
102+
Task: "Echo Request",
103+
};
104+
} else {
105+
return {
106+
Application: "Ping",
107+
Task: "Echo Reply",
108+
};
109+
}
110+
}
84111
}
85112

86113
export class EchoRequest extends EchoMessage {

src/packets/ip.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { FramePayload, IP_PROTOCOL_TYPE } from "./ethernet";
2+
import { Layer } from "../types/devices/layer";
23

34
// Taken from here: https://en.wikipedia.org/wiki/List_of_IP_protocol_numbers
45
export const ICMP_PROTOCOL_NUMBER = 1;
@@ -16,6 +17,10 @@ export class EmptyPayload implements IpPayload {
1617
getPacketType(): string {
1718
return "EMPTY-PROTOCOL";
1819
}
20+
21+
getDetails() {
22+
return {};
23+
}
1924
}
2025

2126
/// Internet Protocol (IP) address
@@ -119,6 +124,8 @@ export interface IpPayload {
119124
protocol(): number;
120125
// Packet protocol name
121126
getPacketType(): string;
127+
// Get details of the payload
128+
getDetails(layer: Layer): Record<string, string | number | object>;
122129
}
123130

124131
// Info taken from the original RFC: https://datatracker.ietf.org/doc/html/rfc791#section-3.1
@@ -252,6 +259,44 @@ export class IPv4Packet implements FramePayload {
252259
type(): number {
253260
return IP_PROTOCOL_TYPE;
254261
}
262+
263+
getDetails(layer: Layer) {
264+
// TODO: Refactor Packet Building Process
265+
//
266+
// Current Implementation:
267+
// - Packet building starts at the Network layer
268+
// - Frame payload data is directly included here
269+
//
270+
// Desired Implementation:
271+
// - Move frame-specific data to EthernetFrame class
272+
// - Implement packet sending using MAC addresses at device level
273+
if (layer == Layer.Link) {
274+
return {
275+
"Ethernet Header": "---",
276+
"Destination MAC": "---",
277+
"Source MAC": "---",
278+
EtherType: "0x0800",
279+
};
280+
}
281+
282+
if (layer == Layer.Network) {
283+
return {
284+
Version: this.version,
285+
"Internet Header Length": this.internetHeaderLength,
286+
"Type of Service": this.typeOfService,
287+
"Total Length": this.totalLength,
288+
Identification: this.identification,
289+
Flags: this.flags,
290+
"Fragment Offset": this.fragmentOffset,
291+
"Time to Live": this.timeToLive,
292+
Protocol: this.protocol,
293+
"Header Checksum": this.headerChecksum,
294+
Payload: this.payload,
295+
};
296+
} else {
297+
return this.payload.getDetails(layer);
298+
}
299+
}
255300
}
256301

257302
export function computeIpChecksum(octets: Uint8Array): number {

src/packets/tcp.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
IpPayload,
55
TCP_PROTOCOL_NUMBER,
66
} from "./ip";
7+
import { Layer } from "../types/devices/layer";
78

89
export class Flags {
910
// Urgent Pointer field significant
@@ -179,6 +180,11 @@ export class TcpSegment implements IpPayload {
179180
return computeIpChecksum(totalBytes);
180181
}
181182

183+
// Dummy Method for the moment
184+
getDetails(layer: Layer) {
185+
return { Layer: layer };
186+
}
187+
182188
// ### IpPayload ###
183189
toBytes({
184190
withChecksum = true,

src/programs/echo_sender.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,14 @@ export class EchoServer extends ProgramBase {
8787
if (this.progress < delay) {
8888
return;
8989
}
90-
this.echoProgram.run(() => {
91-
// do nothing
92-
});
93-
this.progress -= delay;
90+
this.progress += ticker.deltaMS * this.viewgraph.getSpeed();
91+
92+
if (this.progress >= delay) {
93+
this.echoProgram.run(() => {
94+
// Do nothing
95+
});
96+
this.progress -= delay;
97+
}
9498
}
9599

96100
protected _stop() {

src/styles/info.css

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,35 @@
5959
.hidden {
6060
display: none;
6161
}
62+
63+
/* Style for the warning sign container */
64+
.warning-sign {
65+
display: flex;
66+
flex-direction: column;
67+
border: 2px solid #ffcc3d;
68+
border-radius: 8px;
69+
margin: 10px 0;
70+
overflow: hidden;
71+
max-width: 300px;
72+
}
73+
74+
/* Title style for the warning */
75+
.warning-sign .warning-title {
76+
background-color: #ffcc3d;
77+
color: #000;
78+
font-weight: bold;
79+
font-size: 20px;
80+
padding: 5px;
81+
margin: 0;
82+
text-align: center;
83+
width: 100%;
84+
}
85+
86+
/* Message style for the warning */
87+
.warning-sign .warning-message {
88+
padding: 10px;
89+
margin: 0;
90+
text-align: center;
91+
font-size: 18px;
92+
line-height: 1.4;
93+
}

0 commit comments

Comments
 (0)