ADVANCED COMPUTER NETWORKS LAB MANUAL
1. IMPLEMENT THE IP FRAGMENTATION AND REASSEMBLY
ALGORITHM
from math import floor
from typing import List
class IPFragment:
def __init__(self, identification, offset, mf, header_len, payload):
self.identification = identification
self.offset = offset # in 8-byte blocks
self.mf = mf # More Fragments flag (1 if more fragments follow)
self.header_len = header_len
self.payload = payload
def __repr__(self):
return (f"ID={self.identification}, Offset={self.offset * 8} bytes, "
f"MF={self.mf}, PayloadLen={len(self.payload)}")
def fragment_ip_packet(payload: bytes, mtu: int, header_len: int = 20, identification:
int = 1234) -> List[IPFragment]:
"""
Fragments the payload based on MTU and returns a list of IPFragment objects.
"""
max_data = floor((mtu - header_len) / 8) * 8
fragments = []
offset = 0
i=0
while offset < len(payload):
end = min(offset + max_data, len(payload))
frag_payload = payload[offset:end]
mf = 1 if end < len(payload) else 0
fragment = IPFragment(
identification=identification,
offset=offset // 8,
mf=mf,
header_len=header_len,
payload=frag_payload
)
fragments.append(fragment)
offset += max_data
i += 1
return fragments
def reassemble_ip_fragments(fragments: List[IPFragment]) -> bytes:
"""
Reassembles fragments and returns the full payload.
"""
fragments.sort(key=lambda f: f.offset)
complete_payload = bytearray()
expected_offset = 0
for frag in fragments:
actual_offset = frag.offset * 8
if actual_offset != expected_offset:
raise ValueError(f"Fragment at offset {actual_offset} doesn't match expected
offset {expected_offset}")
complete_payload += frag.payload
expected_offset += len(frag.payload)
if fragments[-1].mf != 0:
raise ValueError("Reassembly failed: last fragment missing (MF != 0)")
return bytes(complete_payload)
# ------------------ DEMO ------------------ #
if __name__ == "__main__":
# Input data
data = b"This is a sample data string that is going to be fragmented and then
reassembled."
mtu = 30
header_len = 20 # standard IPv4 header
print("Original Payload:", data.decode())
print("Length:", len(data))
# Fragment
fragments = fragment_ip_packet(data, mtu, header_len)
print(f"\nGenerated {len(fragments)} fragments:\n")
for i, frag in enumerate(fragments):
print(f"Fragment {i}: {frag}")
# Reassemble
try:
reassembled = reassemble_ip_fragments(fragments)
print("\nReassembled Payload:", reassembled.decode())
print("Reassembly Successful:", reassembled == data)
except ValueError as e:
print("Reassembly Error:", e)
2. IMPLEMENT THE IP FORWARDING ALGORITHM
from __future__ import annotations
from dataclasses import dataclass
from ipaddress import IPv4Address, IPv4Network
from typing import List, Optional
# -------------------------------
# Data models
# -------------------------------
@dataclass
class Route:
network: IPv4Network # e.g. IPv4Network("10.0.0.0/8")
next_hop: Optional[IPv4Address] # None => directly connected
iface: str
@dataclass
class Packet:
src: IPv4Address
dst: IPv4Address
ttl: int = 64
payload: bytes = b""
# -------------------------------
# Forwarding logic
# -------------------------------
def longest_prefix_match(routes: List[Route], dst: IPv4Address) -> Optional[Route]:
"""
Return the route with the longest prefix that matches 'dst'.
If none matches, return None.
"""
best = None
best_prefix = -1
for r in routes:
if dst in r.network:
plen = r.network.prefixlen
if plen > best_prefix:
best_prefix = plen
best = r
return best
def forward(packet: Packet, routes: List[Route]) -> Optional[tuple[Route,
IPv4Address]]:
"""
Simulate IPv4 forwarding:
- Decrement TTL; drop if TTL <= 0
- Find longest-prefix-match route
- Return (selected_route, next_hop_ip) if forwardable, else None
next_hop_ip is:
- route.next_hop if present
- packet.dst if directly connected
"""
# TTL handling
packet.ttl -= 1
if packet.ttl <= 0:
print("Drop: TTL expired")
return None
route = longest_prefix_match(routes, packet.dst)
if route is None:
print("Drop: No matching route")
return None
next_hop = route.next_hop if route.next_hop is not None else packet.dst
return route, next_hop
# -------------------------------
# Demo
# -------------------------------
if __name__ == "__main__":
# Build a tiny routing table
routing_table = [
Route(IPv4Network("10.1.0.0/16"), next_hop=None, iface="eth0"),
# directly connected
Route(IPv4Network("10.1.2.0/24"), next_hop=None, iface="eth1"),
# more specific, directly connected
Route(IPv4Network("0.0.0.0/0"), next_hop=IPv4Address("192.0.2.1"),
iface="eth2"), # default via gateway
]
# Packets to test
pkts = [
Packet(IPv4Address("10.1.2.50"), IPv4Address("10.1.2.99")), # should
pick /24 on eth1
Packet(IPv4Address("10.1.2.50"), IPv4Address("10.1.9.8")), # should
pick /16 on eth0
Packet(IPv4Address("10.1.2.50"), IPv4Address("8.8.8.8")), # should pick
default via 192.0.2.1
]
for i, p in enumerate(pkts, 1):
print(f"\nPacket {i}: {p.src} -> {p.dst}, ttl={p.ttl}")
res = forward(p, routing_table)
if res is None:
print(" => dropped")
else:
route, nh = res
print(f" => forwarded out {route.iface}, next-hop {nh}, new ttl={p.ttl}")
3. IMPLEMENT THE SIMPLEST SLIDING WINDOW PROTOCOL OF TCP
import random
from collections import deque
from dataclasses import dataclass
# -------------------- Config --------------------
MSS = 4 # bytes per segment
WINDOW_SIZE = 4 # sender window size
RTT = 5 # one-way delay = RTT/2, but we’ll just schedule arrivals at
now+RTT
RTO = 15 # retransmission timeout
LOSS_PROB = 0.15 # packet loss prob (data or ACK)
DATA = b"HELLO_THIS_IS_A_SMALL_TCP_SLIDING_WINDOW_DEMO"
random.seed(1)
# -------------------- Data structures --------------------
@dataclass
class Segment:
seq: int
data: bytes
@dataclass
class Ack:
ackno: int # cumulative ack (next byte expected)
# -------------------- Network simulator --------------------
class Network:
def __init__(self):
self.events = [] # list of (deliver_time, payload, dst)
def send(self, now, payload, dst):
# Drop?
if random.random() < LOSS_PROB:
print(f"[{now:03}] NET: *** LOST *** {payload}")
return
deliver_time = now + RTT
self.events.append((deliver_time, payload, dst))
def tick(self, now):
delivered = []
remain = []
for t, p, d in self.events:
if t <= now:
delivered.append((p, d))
else:
remain.append((t, p, d))
self.events = remain
return delivered
# -------------------- Receiver --------------------
class Receiver:
def __init__(self):
self.rcv_nxt = 0 # next expected byte
self.buffer = {} # (we won't use out-of-order buffering in GBN)
def recv(self, seg: Segment):
if seg.seq == self.rcv_nxt:
self.rcv_nxt += len(seg.data)
# (Go-Back-N: drop any out-of-order; in Selective Repeat we'd buffer)
# else: ignore out-of-order
return Ack(self.rcv_nxt)
# -------------------- Sender (Go-Back-N) --------------------
class Sender:
def __init__(self, data: bytes):
self.data = data
self.base = 0 # oldest unacked byte
self.nextseq = 0 # next byte to send
self.win = WINDOW_SIZE * MSS
self.unacked = {} # seq -> (segment, send_time)
self.timer_start = None
self.done = False
def in_window(self):
return self.nextseq < self.base + self.win
def make_segment(self):
if self.nextseq >= len(self.data):
return None
# amount we can send now
to_send = min(MSS, len(self.data) - self.nextseq)
seg = Segment(self.nextseq, self.data[self.nextseq:self.nextseq + to_send])
return seg
def on_send(self, now, net: Network):
while self.in_window():
seg = self.make_segment()
if seg is None:
break
net.send(now, seg, "receiver")
print(f"[{now:03}] SND: seq={seg.seq:03}, len={len(seg.data)},
win=[{self.base},{self.base+self.win})")
self.unacked[seg.seq] = (seg, now)
if self.timer_start is None:
self.timer_start = now
self.nextseq += len(seg.data)
def on_ack(self, now, ack: Ack):
if ack.ackno > self.base:
# ack all bytes below ackno
to_del = [s for s in self.unacked if s + len(self.unacked[s][0].data) <=
ack.ackno]
for s in to_del:
del self.unacked[s]
self.base = ack.ackno
print(f"[{now:03}] RCV: ACK {ack.ackno:03} -> base={self.base:03}")
# restart timer if we still have unacked data
self.timer_start = now if self.unacked else None
if self.base >= len(self.data) and not self.unacked:
self.done = True
def check_timeout(self, now, net: Network):
if self.timer_start is None:
return
if now - self.timer_start >= RTO:
print(f"[{now:03}] TO! Retransmitting from base={self.base:03}")
self.timer_start = now
# Go-Back-N: retransmit all unacked from base
for seq in sorted(self.unacked):
seg, _ = self.unacked[seq]
net.send(now, seg, "receiver")
self.unacked[seq] = (seg, now)
print(f"[{now:03}] RTX: seq={seg.seq:03}, len={len(seg.data)}")
# -------------------- Main simulation loop --------------------
def simulate():
sender = Sender(DATA)
receiver = Receiver()
net = Network()
now = 0
MAX_TIME = 1000
# initial send
sender.on_send(now, net)
while now < MAX_TIME and not sender.done:
# deliver network events
for payload, dst in net.tick(now):
if dst == "receiver" and isinstance(payload, Segment):
ack = receiver.recv(payload)
net.send(now, ack, "sender")
print(f"[{now:03}] RCV: seg seq={payload.seq:03},
len={len(payload.data)} -> send ACK {ack.ackno}")
elif dst == "sender" and isinstance(payload, Ack):
sender.on_ack(now, payload)
# after ACK, try sending more
sender.on_send(now, net)
# timeout?
sender.check_timeout(now, net)
now += 1
print("\nDone.")
if __name__ == "__main__":
simulate()
4. CONNECT TWO SYSTEMS USING A SWITCH AND CONFIGURE
PRIVATE IP ADDRESSES TO THE SYSTEMS AND PING THEM FROM EACH
OTHER.USING WIRESHARK, CAPTURE PACKETS AND ANALYZE ALL
THE HEADER INFORMATION IN THE PACKETS CAPTURED
1.AIM
To interconnect two hosts through a Layer-2 switch, assign private IPv4 addresses,
verify connectivity using ping (ICMP), and capture/analyze the exchanged
frames/packets in Wireshark.
2) Objectives
Physically connect two systems via a switch.
Configure IPv4 addresses from a private range (RFC 1918).
Test reachability with ping.
Capture ARP and ICMP traffic.
Decode Ethernet, IPv4, and ICMP header fields.
3) Topology
css
CopyEdit
PC-A <----> Switch <----> PC-B
Interfaces (example):
PC-A: Ethernet0
PC-B: Ethernet0
4) Requirements
2 PCs / VMs with Ethernet NICs (Windows or Linux).
1 unmanaged switch (any simple L2 switch will do) + 2 straight-through
Ethernet cables.
Wireshark installed on at least one PC (capture on both if available).
Admin privileges to set IP addresses and run Wireshark.
5) IP Addressing Plan (example)
Use any private block; here we choose 192.168.10.0/24:
PC-A: 192.168.10.1/24
PC-B: 192.168.10.2/24
Gateway: not required (same LAN); leave blank or 0.0.0.0.
6) Procedure
A. Physical setup
Power on the switch.
Connect PC-A and PC-B to any two switch ports using Ethernet cables.
Verify link lights on NICs and switch ports.
B. Configure IP addresses
Windows (PowerShell as Administrator):
powershell
CopyEdit
Get-NetAdapter
New-NetIPAddress -InterfaceAlias "Ethernet" -IPAddress 192.168.10.1 -
PrefixLength 24
# On PC-B:
New-NetIPAddress -InterfaceAlias "Ethernet" -IPAddress 192.168.10.2 -
PrefixLength 24
(Or via Control Panel → Network & Internet → Change adapter settings → IPv4 →
Manual.)
Linux:
bash
CopyEdit
# PC-A
sudo ip addr add 192.168.10.1/24 dev eth0
sudo ip link set eth0 up
# PC-B
sudo ip addr add 192.168.10.2/24 dev eth0
sudo ip link set eth0 up
Verify:
bash
CopyEdit
# Windows
ipconfig
# Linux
ip addr show dev eth0
C. Start Wireshark capture
Open Wireshark on PC-A.
Select the active Ethernet interface.
(Optional) Set a capture filter to reduce noise:
arp or icmp
Click Start.
D. Ping test
From PC-A:
bash
CopyEdit
ping 192.168.10.2
From PC-B (optional):
bash
CopyEdit
ping 192.168.10.1
Observe that the first ping may take slightly longer because ARP resolution
happens before ICMP.
E. Stop capture & save
Stop Wireshark after a few replies.
Save as two_host_switch_ping.pcapng.
7) What you should see in Wireshark
(i) ARP exchange
ARP Request (Broadcast):
Ethernet
Dst MAC: ff:ff:ff:ff:ff:ff (broadcast)
Src MAC: MAC of PC-A
Type: 0x0806 (ARP)
ARP
Opcode: 1 (request)
Sender IP: 192.168.10.1
Target IP: 192.168.10.2
Question: “Who has 192.168.10.2? Tell 192.168.10.1”
ARP Reply (Unicast):
Ethernet
Dst MAC: MAC of PC-A
Src MAC: MAC of PC-B
Type: 0x0806
ARP
Opcode: 2 (reply)
Sender IP/MAC: 192.168.10.2 / MAC of PC-B
After this, the ARP cache on PC-A will map 192.168.10.2 -> <PC-B MAC>.
(ii) ICMP Echo / Echo Reply
For each ping:
Ethernet
Type: 0x0800 (IPv4)
Src MAC: PC-A MAC
Dst MAC: PC-B MAC
IPv4 Header
Version: 4
IHL: 5 (if no options) ⇒ 20 bytes
DSCP/ECN: Usually 0
Total Length: e.g., 84 bytes (depends on OS/ping size)
Identification: (varies per packet)
Flags/Fragment Offset: DF may be set; usually no fragmentation on
LAN
TTL: ~128 (Windows) or 64 (Linux)
Protocol: 1 (ICMP)
Header Checksum
Source IP: 192.168.10.1
Destination IP: 192.168.10.2
ICMP Header
Type: 8 (Echo Request) / 0 (Echo Reply)
Code: 0
Checksum
Identifier & Sequence Number
Data: typically ASCII bytes (e.g., 56 bytes in Linux default)
8) Useful Wireshark Display Filters
arp — show only ARP frames.
icmp — show only ICMP packets.
ip.addr == 192.168.10.1 && icmp
eth.addr == aa:bb:cc:dd:ee:ff
ip.flags.df == 1 — show packets with DF (Don't Fragment) set.
frame.number == X — jump to a specific frame.
9) Sample Observation Table
Frame # Proto Src MAC/IP Dst MAC/IP Key Fields
1 ARP 192.168.10.1 192.168.10.2 (asked) ARP Request, Broadcast
2 ARP 192.168.10.2 192.168.10.1 ARP Reply
3 ICMP 192.168.10.1 192.168.10.2 Echo Request, TTL=128
4 ICMP 192.168.10.2 192.168.10.1 Echo Reply, TTL=64
10) Troubleshooting
No link lights: bad cable/NIC or disabled interface.
Ping fails:
Wrong subnet mask (must both be /24 here).
Firewalls blocking ICMP (enable/allow ping).
ARP cache stale—clear with arp -d * (Windows) / ip neigh flush all
(Linux).
No packets in Wireshark: captured wrong interface, or running without admin
privileges.
11) Result
Two hosts on the same switch were successfully configured with private IPv4
addresses, verified with ICMP ping, and the ARP + ICMP packet flows were
captured and fully analyzed at Ethernet, IPv4, and ICMP layers.
5. INSTALL TELNET ON ONE OF THE SYSTEMS CONNECTED
BY A SWITCH AND TELNET TO IT FROM THE OTHER
SYSTEM.USING WIRESHARK, CAPTURE THE PACKETS AND
ANALYZE THE TCP 3 WAY HANDSHAKE FOR CONNECTION
ESTTABLISHMENT AND TEAR SOWN
1) Aim
Install and run Telnet on one host, connect to it from another host on the same
switch, and capture the traffic with Wireshark to analyze:
The TCP 3-way handshake (SYN → SYN/ACK → ACK) used for
connection establishment
The TCP connection teardown (FIN/ACK … FIN/ACK) — or RST if
aborted
2) Network Topology
css
CopyEdit
PC-A (client) <----> Switch <----> PC-B (Telnet server)
Both hosts are already assigned private IPs (from your previous experiment), e.g.:
PC-A: 192.168.10.1/24
PC-B: 192.168.10.2/24
3) Requirements
2 PCs (Windows or Linux) connected via a switch
Wireshark (installed on at least one PC; ideally on the server side to see full
flow)
Telnet server on PC-B, Telnet client on PC-A
Admin/root privileges
4) Installation & Configuration
A) Linux
On PC-B (server):
bash
CopyEdit
# Ubuntu/Debian
sudo apt update
sudo apt install telnetd # (BSD telnetd) or openbsd-inetd + telnetd
sudo systemctl enable inetd --now # if using inetd/openbsd-inetd# confirm it's
listening on TCP/23
sudo ss -ltnp | grep :23
On PC-A (client):
bash
CopyEdit
sudo apt install telnet
B) Windows
Client: Windows often ships with Telnet client disabled. Enable from:
Control Panel → Programs and Features → Turn Windows features on or off
→ Telnet Client.
Server: Windows no longer ships Telnet Server by default. Use a lightweight
alternative (e.g., WinSSHD/freeSSHd with Telnet enabled) or run a Linux
VM/container as the server.
(If your lab insists on Windows-only, you may set up a Telnet server on Windows
using third-party tools, or run a Linux VM as the Telnet host.)
5) Wireshark Capture
Launch Wireshark on PC-B (server) or PC-A (client).
Select the active Ethernet interface.
(Optional) Capture filter to limit traffic:
nginx
CopyEdit
tcp port 23
or leave blank and use display filters later.
Click Start.
6) Perform Telnet Session
From PC-A (client):
bash
CopyEdit
telnet 192.168.10.2 23
You should see a login banner/prompt (depends on telnetd config). Type a few
characters/commands, then logout or press Ctrl-] then quit to close.
Stop Wireshark capture and save it as telnet_tcp_handshake.pcapng.
7) Analyze in Wireshark
A) Useful display filters
Handshake only:
ini
CopyEdit
tcp.flags.syn == 1
Show just Telnet TCP stream:
ini
CopyEdit
tcp.port == 23
or right-click any Telnet packet → Follow → TCP Stream.
Teardown packets:
ini
CopyEdit
tcp.flags.fin == 1 or tcp.flags.rst == 1
B) TCP 3-Way Handshake (what to look for)
Step Packet Flags Key fields to examine
Client → Seq=x, ACK=0 (not valid); MSS, Window Scale,
1 SYN
Server SACK Permitted, Timestamps options
Server → SYN,
2 Seq=y, Ack=x+1; Options (often symmetric to client)
Client ACK
Client →
3 ACK Seq=x+1, Ack=y+1, no data
Server
In Wireshark “Packet Details” pane, expand:
Ethernet → IP → Transmission Control Protocol
Look at TCP Options (common: MSS, Window Scale, SACK Permitted,
Timestamps)
Note Initial Window Size, Window Scaling factor, Calculated window size
Example (relative sequence numbers enabled):
Frame 10: 192.168.10.1 → 192.168.10.2 TCP [SYN] Seq=0, Win=64240,
MSS=1460, WS=128, SACK_PERM=1, TSval=1000
Frame 11: 192.168.10.2 → 192.168.10.1 TCP [SYN, ACK] Seq=0, Ack=1,
Win=65160, MSS=1460, WS=128, SACK_PERM=1, TSval=2000
Frame 12: 192.168.10.1 → 192.168.10.2 TCP [ACK] Seq=1, Ack=1,
Win=64240, TSval=1010
(Numbers are illustrative; yours will differ.)
C) Data transfer phase
Telnet is cleartext. You can inspect keystrokes and server responses in Follow
TCP Stream.
You’ll see PSH, ACK flags frequently as interactive data is pushed.
D) TCP Teardown (FIN/ACK)
A graceful close usually looks like:
FIN, ACK from the side that closes first (say client).
Seq = last_byte_sent + 1, Ack = last_byte_received + 1
ACK from the peer (server).
FIN, ACK from the peer (server).
ACK from the original side (client).
(Sometimes a side sends a RST to abort. Note the flag and reason.)
Typical filter to see all FIN/RST:
ini
CopyEdit
tcp.flags.fin == 1 or tcp.flags.rst == 1
Example (relative numbers):
# Dir Flags Seq Ack
50 Client → Server FIN, ACK 105 210
51 Server → Client ACK 210 106
52 Server → Client FIN, ACK 210 106
53 Client → Server ACK 106 211
8) Sample Observation Table
Frame Src IP:Port → Dst Seq Ack Options
Flags Window
# IP:Port (rel) (rel) (MSS/WS/SACK/TS)
192.168.10.1:50000 → MSS=1460, WS=128,
10 SYN 0 0 64240
192.168.10.2:23 SACK, TS
192.168.10.2:23 → SYN, MSS=1460, WS=128,
11 0 1 65160
192.168.10.1:50000 ACK SACK, TS
192.168.10.1:50000 →
12 ACK 1 1 64240 TS
192.168.10.2:23
PSH,
… … … … … …
ACK
192.168.10.1:50000 → FIN,
50 105 210 … …
192.168.10.2:23 ACK
192.168.10.2:23 →
51 ACK 210 106 … …
192.168.10.1:50000
192.168.10.2:23 → FIN,
52 210 106 … …
192.168.10.1:50000 ACK
192.168.10.1:50000 →
53 ACK 106 211 … …
192.168.10.2:23
9) Troubleshooting
Cannot connect:
Telnet server not running or blocked by firewall.
Wrong IP/port (default Telnet is TCP/23).
No packets in Wireshark:
Wrong capture interface or no admin privileges.
RST observed immediately:
Server not listening on port 23, or TCP wrappers/inetd disallows
connection.
“Connection closed by foreign host”:
Server/inetd closed session after login failure/timeouts.
10) Result
Successfully installed and ran Telnet between two hosts on the same switch, captured
the traffic, and analyzed the TCP 3-way handshake and 4-way termination (or
RST) using Wireshark, identifying key header fields (flags, sequence/ack numbers,
window, options).