🛠 Workshop

OSI Model &
TCP/IP Model
in Go

A comprehensive, hands-on guide to both network reference models using real, runnable Go code from the standard library. Understand each layer conceptually and practically.

7 OSI Layers 4 TCP/IP Layers Go stdlib
OSI Reference Model
📡 OSI — 7 Layers
7Application— Data
6Presentation— Data
5Session— Data
4Transport— Segment/Datagram
3Network— Packet
2Data Link— Frame
1Physical— Bit
🌐 TCP/IP — 4 Layers
4Application— HTTP, DNS, SMTP…
3Transport— TCP, UDP
2Internet— IP, ICMP
1Link (Network Access)— Ethernet, Wi-Fi
Key Distinction The OSI model is a conceptual reference model created by ISO (1984) with 7 layers for teaching and troubleshooting. The TCP/IP model is the practical, implemented model (1970s, DARPA) with 4 layers that powers the real internet. Both describe the same underlying communication but at different levels of abstraction.
7
Application Layer
OSI Layer 7 · PDU: Data · Protocols: HTTP, HTTPS, DNS, SMTP, FTP, SSH

The Application layer is the closest to the end user. It provides network services directly to applications, handling high-level APIs, user authentication, and data display. This is NOT the application itself — it's the interface through which applications access network services.

PDU: Data/Message
Protocols: HTTP, HTTPS, DNS, SMTP, FTP, SSH, WebSocket
Go pkg: net/http, net, crypto/tls
Devices: Application servers, Proxies
Go · HTTP Client & Server (Application Layer)
// layer7_application.go
// Demonstrates HTTP (Application Layer) using Go stdlib net/http
package main

import (
    "fmt"
    "io"
    "log"
    "net/http"
    "time"
)

// --- Part 1: HTTP Server ---
func startServer() {
    mux := http.NewServeMux()

    mux.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
        // Inspect application-layer metadata
        fmt.Printf("Method: %s | Path: %s | User-Agent: %s\n",
            r.Method, r.URL.Path, r.Header.Get("User-Agent"))

        w.Header().Set("Content-Type", "application/json")
        w.WriteHeader(http.StatusOK)
        fmt.Fprintln(w, `{"message":"Hello from OSI Layer 7"}`)
    })

    server := &http.Server{
        Addr:         ":8080",
        Handler:      mux,
        ReadTimeout:  5 * time.Second,
        WriteTimeout: 10 * time.Second,
    }
    log.Println("Server listening on :8080")
    if err := server.ListenAndServe(); err != nil {
        log.Fatal(err)
    }
}

// --- Part 2: HTTP Client ---
func makeRequest() {
    client := &http.Client{Timeout: 10 * time.Second}

    resp, err := client.Get("http://localhost:8080/hello")
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    body, _ := io.ReadAll(resp.Body)
    fmt.Printf("Status: %s\nBody: %s\n", resp.Status, body)

    // Application-layer headers
    for k, v := range resp.Header {
        fmt.Printf("Header: %s = %v\n", k, v)
    }
}

func main() {
    go startServer()
    time.Sleep(100 * time.Millisecond)
    makeRequest()
}
Go · DNS Resolution (Application Layer)
// DNS lookup — another Application Layer protocol
package main

import (
    "context"
    "fmt"
    "net"
    "time"
)

func main() {
    resolver := &net.Resolver{PreferGo: true}
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    // DNS A record lookup
    addrs, err := resolver.LookupHost(ctx, "example.com")
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    fmt.Println("IPv4/IPv6 addresses:", addrs)

    // MX record lookup
    mxRecords, _ := resolver.LookupMX(ctx, "gmail.com")
    for _, mx := range mxRecords {
        fmt.Printf("MX: %s (priority %d)\n", mx.Host, mx.Pref)
    }
}

🧪 Quiz — Layer 7: Application

1. What does the Application Layer (L7) provide?

Physical transmission of bits
Network services directly to end-user applications
IP addressing and routing
MAC addressing

2. Which Go package is primarily used for HTTP (Application Layer)?

net/tcp
os/net
net/http
http/server

3. What is the PDU (Protocol Data Unit) at the Application Layer?

Data / Message
Frame
Packet
Segment
6
Presentation Layer
OSI Layer 6 · PDU: Data · Functions: Encoding, Encryption, Compression

The Presentation layer acts as the translator between the network and application. It handles data format translation (e.g., ASCII ↔ EBCDIC), compression (gzip, deflate), and encryption/decryption (TLS/SSL). It ensures data sent by the application on one system is readable by the application on another.

PDU: Data
Functions: Encoding, Encryption, Compression
Formats: JSON, XML, base64, gzip, TLS
Go pkg: encoding/json, encoding/base64, compress/gzip, crypto/tls
Go · Encoding, Compression & TLS (Presentation Layer)
// layer6_presentation.go
package main

import (
    "bytes"
    "compress/gzip"
    "crypto/tls"
    "encoding/base64"
    "encoding/json"
    "fmt"
    "log"
)

type Message struct {
    Layer   string `json:"layer"`
    Content string `json:"content"`
}

// JSON encoding (data serialization)
func demoJSON() {
    msg := Message{Layer: "Presentation", Content: "Hello OSI"}

    encoded, _ := json.Marshal(msg)
    fmt.Printf("JSON encoded: %s\n", encoded)

    var decoded Message
    json.Unmarshal(encoded, &decoded)
    fmt.Printf("JSON decoded: %+v\n", decoded)
}

// Base64 encoding
func demoBase64() {
    original := []byte("Layer 6: Presentation")
    b64 := base64.StdEncoding.EncodeToString(original)
    fmt.Printf("Base64: %s\n", b64)

    decoded, _ := base64.StdEncoding.DecodeString(b64)
    fmt.Printf("Decoded: %s\n", decoded)
}

// Gzip compression
func demoGzip() {
    var buf bytes.Buffer
    gz := gzip.NewWriter(&buf)
    gz.Write([]byte("Presentation layer compresses data before sending"))
    gz.Close()

    fmt.Printf("Compressed size: %d bytes\n", buf.Len())

    reader, _ := gzip.NewReader(&buf)
    var out bytes.Buffer
    out.ReadFrom(reader)
    fmt.Printf("Decompressed: %s\n", out.String())
}

// TLS configuration (encryption at Presentation layer)
func demoTLSConfig() {
    tlsCfg := &tls.Config{
        MinVersion: tls.VersionTLS13,
        CipherSuites: []uint16{
            tls.TLS_AES_256_GCM_SHA384,
            tls.TLS_CHACHA20_POLY1305_SHA256,
        },
    }
    fmt.Printf("TLS Min Version: %d (TLS 1.3)\n", tlsCfg.MinVersion)
    fmt.Printf("Configured %d cipher suites\n", len(tlsCfg.CipherSuites))
}

func main() {
    fmt.Println("=== Presentation Layer Demo ===")
    demoJSON()
    demoBase64()
    demoGzip()
    demoTLSConfig()
    _ = log.Writer()
}

🧪 Quiz — Layer 6: Presentation

1. Which of the following is NOT a function of the Presentation Layer?

Data encryption (TLS)
IP packet routing
Data compression (gzip)
Character encoding (ASCII, UTF-8)

2. In Go, which package handles gzip compression?

encoding/zip
archive/gzip
compress/gzip
net/compress

3. TLS (Transport Layer Security) primarily handles what concern at L6?

Routing between networks
Error correction at physical level
Session establishment
Data encryption and format negotiation
5
Session Layer
OSI Layer 5 · PDU: Data · Functions: Dialog control, Synchronization, Session management

The Session layer establishes, manages, and terminates sessions between communicating parties. It handles dialog control (who transmits when), checkpointing for recovery, and session restoration. In TCP/IP this is largely handled by the OS and application-level protocols.

PDU: Data
Protocols: NetBIOS, RPC, SIP, PPTP
Concepts: Half-duplex / Full-duplex, Checkpointing
Go pkg: net, context, sync
Go · Session Management via TCP Connection Lifecycle
// layer5_session.go
// Session layer concepts: establish, use, and terminate sessions
package main

import (
    "bufio"
    "context"
    "fmt"
    "net"
    "sync"
    "time"
)

type Session struct {
    ID      string
    conn    net.Conn
    Created time.Time
    mu      sync.Mutex
}

func NewSession(conn net.Conn) *Session {
    return &Session{
        ID:      fmt.Sprintf("sess-%d", time.Now().UnixNano()),
        conn:    conn,
        Created: time.Now(),
    }
}

func (s *Session) Send(msg string) error {
    s.mu.Lock()
    defer s.mu.Unlock()
    _, err := fmt.Fprintf(s.conn, "%s\n", msg)
    return err
}

func (s *Session) Close() {
    fmt.Printf("[Session %s] Terminating after %v\n", s.ID, time.Since(s.Created))
    s.conn.Close()
}

// Server: manages session lifecycle
func sessionServer(ctx context.Context) {
    ln, _ := net.Listen("tcp", ":9090")
    defer ln.Close()

    go func() {
        <-ctx.Done()
        ln.Close()
    }()

    for {
        conn, err := ln.Accept()
        if err != nil { return }

        go func(c net.Conn) {
            sess := NewSession(c)
            fmt.Printf("[Session %s] Established from %s\n",
                sess.ID, c.RemoteAddr())

            scanner := bufio.NewScanner(c)
            for scanner.Scan() {          // session dialog loop
                text := scanner.Text()
                fmt.Printf("[%s] Received: %s\n", sess.ID, text)
                sess.Send("ACK: " + text)   // half-duplex echo
            }
            sess.Close()                   // session teardown
        }(conn)
    }
}

func sessionClient() {
    conn, _ := net.Dial("tcp", "localhost:9090")
    sess := NewSession(conn)
    defer sess.Close()

    messages := []string{"HELLO", "DATA_CHUNK_1", "DATA_CHUNK_2", "BYE"}
    scanner := bufio.NewScanner(conn)

    for _, m := range messages {
        sess.Send(m)
        if scanner.Scan() {
            fmt.Printf("Server: %s\n", scanner.Text())
        }
        time.Sleep(50 * time.Millisecond)
    }
}

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    go sessionServer(ctx)
    time.Sleep(50 * time.Millisecond)
    sessionClient()
    cancel()
}

🧪 Quiz — Layer 5: Session

1. What is the primary responsibility of the Session Layer?

Establishing, managing, and terminating sessions
Compressing data
Physical bit transmission
IP routing

2. Which concept in the Session Layer allows communication to recover from a failure mid-transfer?

ARP caching
MTU fragmentation
CSMA/CD
Checkpointing / Synchronization
4
Transport Layer
OSI Layer 4 · PDU: Segment (TCP) / Datagram (UDP) · Protocols: TCP, UDP, SCTP

The Transport layer provides end-to-end communication services for applications. It handles segmentation, flow control, error recovery, and multiplexing via ports. TCP provides reliable, ordered delivery; UDP provides fast, connectionless delivery.

PDU (TCP): Segment
PDU (UDP): Datagram
Protocols: TCP, UDP, SCTP, QUIC
Go pkg: net (Dial, Listen), net.TCPConn, net.UDPConn
Go · TCP (Reliable Transport) with flow control
// layer4_transport_tcp.go
package main

import (
    "fmt"
    "net"
    "time"
)

func tcpServer() {
    ln, _ := net.Listen("tcp", ":7001")
    defer ln.Close()

    conn, _ := ln.Accept()
    defer conn.Close()

    tcpConn := conn.(*net.TCPConn)

    // TCP transport tuning
    tcpConn.SetNoDelay(true)      // disable Nagle algorithm
    tcpConn.SetKeepAlive(true)   // keep session alive
    tcpConn.SetKeepAlivePeriod(30 * time.Second)

    buf := make([]byte, 1024)
    n, _ := conn.Read(buf)
    fmt.Printf("TCP received %d bytes: %s\n", n, buf[:n])

    // Send ACK (TCP handles acknowledgement at the protocol level)
    conn.Write([]byte("ACK"))
}

func tcpClient() {
    conn, err := net.DialTimeout("tcp", "localhost:7001", 3*time.Second)
    if err != nil {
        fmt.Println("Connection failed:", err); return
    }
    defer conn.Close()

    // Segment: data broken into segments for reliable delivery
    payload := []byte("Layer 4 Transport: reliable segment")
    conn.Write(payload)
    fmt.Printf("Sent %d bytes\n", len(payload))

    buf := make([]byte, 64)
    n, _ := conn.Read(buf)
    fmt.Printf("Server response: %s\n", buf[:n])
}

func main() {
    go tcpServer()
    time.Sleep(50 * time.Millisecond)
    tcpClient()
}
Go · UDP (Unreliable / Fast Transport)
// UDP — connectionless transport, no handshake, no ordering
package main

import (
    "fmt"
    "net"
    "time"
)

func udpServer() {
    addr, _ := net.ResolveUDPAddr("udp", ":7002")
    conn, _ := net.ListenUDP("udp", addr)
    defer conn.Close()

    buf := make([]byte, 1024)
    n, remoteAddr, _ := conn.ReadFromUDP(buf)
    fmt.Printf("UDP datagram from %s: %s\n", remoteAddr, buf[:n])
    conn.WriteToUDP([]byte("PONG"), remoteAddr)
}

func udpClient() {
    serverAddr, _ := net.ResolveUDPAddr("udp", "localhost:7002")
    conn, _ := net.DialUDP("udp", nil, serverAddr)
    defer conn.Close()

    conn.Write([]byte("PING"))   // fire-and-forget datagram

    buf := make([]byte, 64)
    conn.SetDeadline(time.Now().Add(2 * time.Second))
    n, _ := conn.Read(buf)
    fmt.Printf("UDP response: %s\n", buf[:n])
}

func main() {
    go udpServer()
    time.Sleep(30 * time.Millisecond)
    udpClient()
}

🧪 Quiz — Layer 4: Transport

1. What mechanism does TCP use to ensure all data arrives in order?

MAC filtering
Sequence numbers and acknowledgements
IP checksums
VLAN tagging

2. When would you choose UDP over TCP?

Online banking transactions
File downloads
Real-time video streaming / gaming
Email delivery

3. What Go type represents a low-level TCP connection for tuning (e.g., SetNoDelay)?

net.TCPConn
net.UDPConn
http.Conn
net.IPConn
3
Network Layer
OSI Layer 3 · PDU: Packet · Protocols: IPv4, IPv6, ICMP, OSPF, BGP

The Network layer handles logical addressing and routing. It determines the best path for data to travel between networks, manages IP addressing (IPv4/IPv6), and handles packet fragmentation. Routers operate at this layer.

PDU: Packet
Protocols: IP, ICMP, IGMP, OSPF, BGP
Addressing: IPv4, IPv6, CIDR
Go pkg: net (IP, IPNet, interfaces)
Devices: Router, Layer-3 Switch
Go · IP Addressing, Subnets & Network Interfaces
// layer3_network.go
package main

import (
    "fmt"
    "net"
)

func demoIPParsing() {
    // Parse and inspect IP addresses (Network Layer addressing)
    ips := []string{"192.168.1.100", "2001:db8::1", "10.0.0.1"}
    for _, s := range ips {
        ip := net.ParseIP(s)
        if ip == nil { continue }
        fmt.Printf("IP: %-20s  IsLoopback: %v  IsPrivate: %v  v4: %v\n",
            ip, ip.IsLoopback(), ip.IsPrivate(), ip.To4() != nil)
    }
}

func demoCIDR() {
    // CIDR notation — subnet masking at the Network Layer
    cidrs := []string{"192.168.0.0/24", "10.0.0.0/8", "172.16.0.0/12"}
    for _, cidr := range cidrs {
        ip, ipnet, _ := net.ParseCIDR(cidr)
        ones, bits := ipnet.Mask.Size()
        fmt.Printf("CIDR: %-18s  Network: %-18s  Mask: /%d of %d  Host: %s\n",
            cidr, ipnet.IP, ones, bits, ip)
    }

    // Check if an IP is in a subnet
    _, subnet, _ := net.ParseCIDR("192.168.1.0/24")
    testIP := net.ParseIP("192.168.1.55")
    fmt.Printf("192.168.1.55 in 192.168.1.0/24: %v\n", subnet.Contains(testIP))
}

func demoInterfaces() {
    // Enumerate local network interfaces (Network Layer device view)
    ifaces, _ := net.Interfaces()
    for _, iface := range ifaces {
        addrs, _ := iface.Addrs()
        if len(addrs) == 0 { continue }
        fmt.Printf("Interface: %-12s  Flags: %v\n", iface.Name, iface.Flags)
        for _, addr := range addrs {
            fmt.Printf("  Address: %s\n", addr)
        }
    }
}

func main() {
    fmt.Println("=== Network Layer (L3) Demo ===")
    fmt.Println("\n-- IP Parsing --")
    demoIPParsing()
    fmt.Println("\n-- CIDR / Subnets --")
    demoCIDR()
    fmt.Println("\n-- Network Interfaces --")
    demoInterfaces()
}
Go · Raw ICMP Ping (Network Layer Protocol)
// ICMP operates at Layer 3. Raw sockets require privileges.
// This shows the concept using net.Dial with "ip4:icmp"
package main

import (
    "fmt"
    "net"
    "time"
)

func checkRouteToHost(host string) {
    // net.Dial resolves at L3; shows routing is happening
    conn, err := net.DialTimeout("tcp", host+":80", 2*time.Second)
    if err != nil {
        fmt.Printf("Route to %s: UNREACHABLE (%v)\n", host, err)
        return
    }
    conn.Close()

    // Get the local IP used for routing to this host
    localConn, _ := net.Dial("udp", host+":80")
    if localConn != nil {
        defer localConn.Close()
        localIP := localConn.LocalAddr().(*net.UDPAddr).IP
        fmt.Printf("Route to %s: OK (outbound IP: %s)\n", host, localIP)
    }
}

func main() {
    hosts := []string{"8.8.8.8", "1.1.1.1", "192.0.2.1"}
    for _, h := range hosts {
        checkRouteToHost(h)
    }
}

🧪 Quiz — Layer 3: Network

1. What is the primary addressing scheme at Layer 3?

MAC Address
IP Address
Port Number
VLAN ID

2. What does CIDR /24 notation mean?

24 hosts in the network
Network can have 24 subnets
24 routers in the path
First 24 bits are the network prefix

3. Which protocol at L3 is used for error reporting and diagnostics (e.g., ping)?

ICMP
ARP
UDP
SNMP
2
Data Link Layer
OSI Layer 2 · PDU: Frame · Protocols: Ethernet, Wi-Fi (802.11), PPP, ARP

The Data Link layer is responsible for node-to-node delivery within the same network. It creates frames (encapsulating packets), handles MAC addressing, error detection (CRC), and media access control. Switches and network interface cards operate here. Divided into two sub-layers: LLC (Logical Link Control) and MAC (Media Access Control).

PDU: Frame
Addressing: MAC (48-bit hardware)
Protocols: Ethernet, Wi-Fi, ARP, VLAN (802.1Q)
Go pkg: net (HardwareAddr, Interfaces)
Devices: Switch, Bridge, NIC
Go · MAC Addresses, Interfaces & ARP concepts
// layer2_datalink.go
package main

import (
    "fmt"
    "net"
)

// Inspect Data Link layer MAC addresses and interface hardware info
func demoMACAddresses() {
    ifaces, err := net.Interfaces()
    if err != nil {
        fmt.Println("Error:", err); return
    }

    for _, iface := range ifaces {
        if iface.HardwareAddr == nil { continue } // skip loopback

        fmt.Printf("Interface: %-12s  MAC: %-20s  MTU: %d\n",
            iface.Name,
            iface.HardwareAddr.String(), // 48-bit MAC address (L2)
            iface.MTU,                    // Max Transmission Unit
        )
        fmt.Printf("  Flags: %v\n", iface.Flags)

        addrs, _ := iface.Addrs()
        for _, a := range addrs {
            fmt.Printf("  IP Addr: %s\n", a)
        }
    }
}

// Demonstrate MAC address parsing (Data Link frame source/dest)
func demoParseMac() {
    macStr := "00:1A:2B:3C:4D:5E"
    hwAddr, _ := net.ParseMAC(macStr)
    fmt.Printf("\nParsed MAC: %v\n", hwAddr)
    fmt.Printf("Bytes: % X\n", []byte(hwAddr))

    // Check if multicast (bit 0 of first byte = 1)
    isMulticast := hwAddr[0]&0x01 != 0
    fmt.Printf("Is Multicast: %v\n", isMulticast)

    // Check if locally administered (bit 1 of first byte = 1)
    isLocal := hwAddr[0]&0x02 != 0
    fmt.Printf("Is Locally Administered: %v\n", isLocal)
}

// Simulate an Ethernet frame header structure
type EthernetFrame struct {
    DstMAC   net.HardwareAddr
    SrcMAC   net.HardwareAddr
    EtherType uint16          // 0x0800=IPv4, 0x0806=ARP, 0x86DD=IPv6
    Payload  []byte
}

func (f EthernetFrame) String() string {
    etypes := map[uint16]string{
        0x0800: "IPv4", 0x0806: "ARP", 0x86DD: "IPv6",
    }
    name := etypes[f.EtherType]
    if name == "" { name = "Unknown" }
    return fmt.Sprintf(
        "Frame[Dst:%s Src:%s Type:0x%04X(%s) Len:%d]",
        f.DstMAC, f.SrcMAC, f.EtherType, name, len(f.Payload))
}

func main() {
    demoMACAddresses()
    demoParseMac()

    dst, _ := net.ParseMAC("ff:ff:ff:ff:ff:ff") // broadcast
    src, _ := net.ParseMAC("aa:bb:cc:dd:ee:ff")
    frame := EthernetFrame{
        DstMAC: dst, SrcMAC: src,
        EtherType: 0x0800,
        Payload: []byte("IP packet here"),
    }
    fmt.Println("\nSimulated Ethernet Frame:")
    fmt.Println(frame)
}

🧪 Quiz — Layer 2: Data Link

1. What addressing scheme does the Data Link layer use?

IP addresses
Port numbers
MAC addresses
Domain names

2. What is the broadcast MAC address in Ethernet?

00:00:00:00:00:00
ff:ff:ff:ff:ff:ff
255.255.255.255
127.0.0.1

3. ARP (Address Resolution Protocol) operates at which boundary?

Layer 7 only
Layer 1 only
Layer 4–5
Layer 2–3 boundary (maps IP → MAC)
1
Physical Layer
OSI Layer 1 · PDU: Bit · Media: Copper, Fibre, Radio, Coax

The Physical layer deals with the raw transmission of bits over a physical medium. It defines electrical/optical signal levels, connector types, cable specs, and data rates. Go operates far above this layer, but we can observe physical-layer metadata through network interface inspection and raw binary I/O.

PDU: Bit
Media: Ethernet cable, Fibre optic, Wi-Fi, Coax
Concepts: Bandwidth, MTU, Encoding (NRZ, Manchester)
Go pkg: net (Interfaces/MTU), encoding/binary, io
Devices: Hub, Repeater, Modem, NIC
Note Go's standard library operates at Layer 4 and above. Layer 1 is handled by the OS kernel and hardware drivers. However, we can explore binary encoding (how data becomes bits) and interface hardware properties.
Go · Binary Encoding, MTU & Interface Physical Properties
// layer1_physical.go
// Physical layer: bits, MTU, binary encoding, interface hardware props
package main

import (
    "bytes"
    "encoding/binary"
    "fmt"
    "net"
)

// Binary serialization — how data becomes bits on the wire
func demoBinaryEncoding() {
    fmt.Println("=== Binary Encoding (bits on wire) ===")

    var buf bytes.Buffer

    // Big-endian (network byte order — used on the wire)
    values := []interface{}{
        uint16(8080),    // port number as 2 bytes
        uint32(3232235777), // 192.168.1.1 as 4 bytes
        uint8(6),          // TCP protocol number
    }

    for _, v := range values {
        binary.Write(&buf, binary.BigEndian, v)
    }

    fmt.Printf("Encoded bytes (hex): % X\n", buf.Bytes())
    fmt.Printf("Total bytes: %d (= %d bits on the wire)\n",
        buf.Len(), buf.Len()*8)

    // Decode back
    reader := bytes.NewReader(buf.Bytes())
    var port uint16
    var ipInt uint32
    var proto uint8
    binary.Read(reader, binary.BigEndian, &port)
    binary.Read(reader, binary.BigEndian, &ipInt)
    binary.Read(reader, binary.BigEndian, &proto)

    fmt.Printf("Decoded — Port: %d, IP int: %d, Proto: %d\n", port, ipInt, proto)
}

// Show bit representation of a byte
func demoBitView() {
    fmt.Println("\n=== Bit-level view of 'A' (0x41) ===")
    b := byte('A')
    fmt.Printf("Char 'A' = 0x%02X = %08b (8 bits transmitted)\n", b, b)

    word := uint16(0x1F4) // 500
    fmt.Printf("Port 500 = 0x%04X = %016b (16 bits)\n", word, word)
}

// MTU — Maximum Transmission Unit (max frame size in bytes at Layer 1/2)
func demoMTU() {
    fmt.Println("\n=== MTU (Physical Layer frame size limit) ===")
    ifaces, _ := net.Interfaces()
    for _, iface := range ifaces {
        if iface.Flags&net.FlagUp == 0 { continue }
        fmt.Printf("%-12s  MTU: %4d bytes (%5d bits)\n",
            iface.Name, iface.MTU, iface.MTU*8)
    }
}

func main() {
    demoBinaryEncoding()
    demoBitView()
    demoMTU()
}

🧪 Quiz — Layer 1: Physical

1. What is the PDU (unit of data) at the Physical Layer?

Bit
Frame
Packet
Segment

2. What is the standard Ethernet MTU (Maximum Transmission Unit) in bytes?

512
1024
1500
65535

3. Why does Go use big-endian (network byte order) when encoding integers for transmission?

It is the only format CPUs support
It is the Internet standard (RFC 1700) ensuring interoperability
It is faster to encode
It compresses the data
TCP/IP Model
TCP/IP vs OSI The TCP/IP model is a condensed, practical model. Its 4 layers map to the OSI model as: Application = OSI 5+6+7, Transport = OSI 4, Internet = OSI 3, Link = OSI 1+2.
4
Application Layer (TCP/IP)
Maps to OSI Layers 5, 6, 7 · Protocols: HTTP/S, DNS, SMTP, FTP, SSH, WebSocket

In TCP/IP, the Application Layer combines OSI's Application, Presentation, and Session layers. It is the topmost layer and all user-facing protocols live here. Applications directly interact with this layer.

Combines OSI: L5 + L6 + L7
Protocols: HTTP, HTTPS, DNS, SMTP, SSH, FTP, DHCP
Go pkg: net/http, net/smtp, crypto/tls
Go · HTTPS Client with TLS Inspection (TCP/IP Application Layer)
// tcpip_layer4_application.go
package main

import (
    "crypto/tls"
    "fmt"
    "net/http"
    "time"
)

func main() {
    transport := &http.Transport{
        TLSClientConfig: &tls.Config{
            MinVersion: tls.VersionTLS12,
        },
        MaxIdleConnsPerHost: 10,
        IdleConnTimeout:     90 * time.Second,
    }

    client := &http.Client{
        Transport: transport,
        Timeout:   15 * time.Second,
        // CheckRedirect allows observing L7 redirect behaviour
        CheckRedirect: func(req *http.Request, via []*http.Request) error {
            fmt.Printf("Redirect → %s\n", req.URL)
            return nil
        },
    }

    resp, err := client.Get("https://httpbin.org/get")
    if err != nil {
        fmt.Println("Error:", err); return
    }
    defer resp.Body.Close()

    fmt.Printf("Protocol:    %s\n", resp.Proto)
    fmt.Printf("Status:      %s\n", resp.Status)
    fmt.Printf("TLS Version: %v\n", resp.TLS != nil)
    if resp.TLS != nil {
        versions := map[uint16]string{
            tls.VersionTLS10: "TLS 1.0", tls.VersionTLS11: "TLS 1.1",
            tls.VersionTLS12: "TLS 1.2", tls.VersionTLS13: "TLS 1.3",
        }
        fmt.Printf("TLS Version: %s\n", versions[resp.TLS.Version])
        fmt.Printf("Server Name: %s\n", resp.TLS.ServerName)
    }
}

🧪 Quiz — TCP/IP Application Layer

1. Which OSI layers does the TCP/IP Application layer encompass?

Layers 1, 2, 3
Layers 5, 6, 7
Layers 3, 4
Only Layer 7

2. In Go, which struct field provides TLS connection metadata after an HTTPS response?

resp.Header["TLS"]
resp.Security
resp.TLS (*tls.ConnectionState)
resp.Proto
3
Transport Layer (TCP/IP)
Maps directly to OSI Layer 4 · Protocols: TCP, UDP, QUIC

The TCP/IP Transport Layer is identical in function to OSI Layer 4. It provides end-to-end communication, port-based multiplexing, and reliability (TCP) or speed (UDP). This is where Go's net package really shines.

Maps to OSI: L4
Protocols: TCP, UDP, QUIC, DCCP
Go pkg: net, net.Listener, net.Conn
Go · Port scanning & Transport layer introspection
// tcpip_layer3_transport.go
// Explore port states — fundamental to the Transport Layer
package main

import (
    "fmt"
    "net"
    "sync"
    "time"
)

type PortResult struct {
    Port  int
    Open  bool
    Proto string
}

func scanPort(host string, port int, wg *sync.WaitGroup, results chan<- PortResult) {
    defer wg.Done()
    address := fmt.Sprintf("%s:%d", host, port)
    conn, err := net.DialTimeout("tcp", address, 500*time.Millisecond)
    if err == nil {
        conn.Close()
        results <- PortResult{Port: port, Open: true, Proto: "tcp"}
    } else {
        results <- PortResult{Port: port, Open: false, Proto: "tcp"}
    }
}

func main() {
    host := "scanme.nmap.org" // publicly allowed scan target
    ports := []int{22, 80, 443, 8080, 3306, 5432}

    results := make(chan PortResult, len(ports))
    var wg sync.WaitGroup

    for _, port := range ports {
        wg.Add(1)
        go scanPort(host, port, &wg, results)
    }

    wg.Wait()
    close(results)

    portNames := map[int]string{
        22: "SSH", 80: "HTTP", 443: "HTTPS",
        8080: "HTTP-ALT", 3306: "MySQL", 5432: "PostgreSQL",
    }
    for r := range results {
        status := "CLOSED"
        if r.Open { status = "OPEN  " }
        fmt.Printf("Port %5d/%-3s [%s] %s\n",
            r.Port, r.Proto, status, portNames[r.Port])
    }
}

🧪 Quiz — TCP/IP Transport Layer

1. What well-known port does HTTPS use?

443
80
22
8080

2. What modern protocol replaces TCP for low-latency web communication?

FTP
SCTP
UDP raw
QUIC (HTTP/3)
2
Internet Layer (TCP/IP)
Maps to OSI Layer 3 · Protocols: IPv4, IPv6, ICMP, OSPF, BGP

The Internet Layer is the glue of the internet. It provides logical addressing via IP and routing of packets across multiple networks. This is where routers make forwarding decisions. Equivalent to OSI's Network Layer.

Maps to OSI: L3
Protocols: IP (v4/v6), ICMP, IGMP, ARP
Go pkg: net (IP, IPNet, Resolver)
Go · IP routing decisions & multi-network interface resolution
// tcpip_layer2_internet.go
package main

import (
    "context"
    "fmt"
    "net"
    "time"
)

// Simulate routing: determine which interface IP is used per destination
func showRoutingDecision(destinations []string) {
    fmt.Println("=== Internet Layer: Routing Decisions ===")
    for _, dest := range destinations {
        conn, err := net.Dial("udp", dest+":53")
        if err != nil {
            fmt.Printf("%-20s  unreachable: %v\n", dest, err)
            continue
        }
        localAddr := conn.LocalAddr().(*net.UDPAddr)
        conn.Close()
        fmt.Printf("Dest: %-18s  Local IP (router chose): %s\n",
            dest, localAddr.IP)
    }
}

// IPv4 vs IPv6 resolution
func showIPVersions() {
    fmt.Println("\n=== IPv4 vs IPv6 at Internet Layer ===")
    resolver := &net.Resolver{PreferGo: true}
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    ips, _ := resolver.LookupIPAddr(ctx, "google.com")
    for _, ip := range ips {
        version := "IPv4"
        if ip.IP.To4() == nil { version = "IPv6" }
        fmt.Printf("%-8s  %s\n", version, ip.IP)
    }
}

// Display network topology visible to this host
func showNetworkTopology() {
    fmt.Println("\n=== Internet Layer: Local Network Topology ===")
    ifaces, _ := net.Interfaces()
    for _, iface := range ifaces {
        if iface.Flags&net.FlagUp == 0 { continue }
        addrs, _ := iface.Addrs()
        for _, addr := range addrs {
            switch v := addr.(type) {
            case *net.IPNet:
                if v.IP.To4() != nil {
                    fmt.Printf("%-12s  subnet: %-20s  gateway: ~%s.1\n",
                        iface.Name, v.IP, v.IP.Mask(v.Mask))
                }
            }
        }
    }
}

func main() {
    showRoutingDecision([]string{"8.8.8.8", "1.1.1.1", "192.168.1.1"})
    showIPVersions()
    showNetworkTopology()
}

🧪 Quiz — TCP/IP Internet Layer

1. Which protocol at the Internet Layer maps IP addresses to MAC addresses?

DHCP
ARP
OSPF
BGP

2. What is the loopback address in IPv4?

192.168.0.1
255.255.255.255
0.0.0.0
127.0.0.1
1
Link Layer (Network Access) (TCP/IP)
Maps to OSI Layers 1 + 2 · Protocols: Ethernet, Wi-Fi 802.11, PPP

The Link Layer in TCP/IP combines OSI's Physical and Data Link layers. It handles framing, MAC addressing, and physical transmission on a single network segment. This is where hardware (NICs, switches, cables) operates.

Combines OSI: L1 + L2
Protocols: Ethernet, Wi-Fi, PPP, ARP
Go pkg: net (HardwareAddr, Interfaces, Flags)
Go · Link Layer — Interface flags, multicast, MTU
// tcpip_layer1_link.go
package main

import (
    "fmt"
    "net"
)

func main() {
    ifaces, _ := net.Interfaces()

    fmt.Println("=== Link Layer Interface Report ===")
    for _, iface := range ifaces {
        fmt.Printf("\n[%s]\n", iface.Name)
        fmt.Printf("  HW Addr (MAC):  %s\n", iface.HardwareAddr)
        fmt.Printf("  MTU:            %d bytes\n", iface.MTU)
        fmt.Printf("  Index:          %d\n", iface.Index)

        // Decode flag bitmask
        flags := []struct{ mask net.Flags; name string }{
            {net.FlagUp,           "UP"},
            {net.FlagBroadcast,    "BROADCAST"},
            {net.FlagLoopback,     "LOOPBACK"},
            {net.FlagPointToPoint, "P2P"},
            {net.FlagMulticast,    "MULTICAST"},
        }
        fmt.Print("  Flags:          ")
        for _, f := range flags {
            if iface.Flags&f.mask != 0 {
                fmt.Printf("%s ", f.name)
            }
        }
        fmt.Println()

        // Multicast group addresses (Link Layer multicast)
        multicast, _ := iface.MulticastAddrs()
        if len(multicast) > 0 {
            fmt.Printf("  Multicast:      %v\n", multicast[0])
        }
    }
}

🧪 Quiz — TCP/IP Link Layer

1. The TCP/IP Link Layer combines which OSI layers?

Layers 3 and 4
Layers 5, 6, and 7
Layers 1 and 2
Only Layer 2

2. What does the FlagLoopback flag indicate for a network interface?

The interface routes traffic back to localhost (127.0.0.1)
The interface supports multicast
The interface is connected to the internet
The interface is offline
OSI vs TCP/IP — Comparison
Feature OSI Model TCP/IP Model
Number of Layers74
Developed ByISO (1984)DARPA (1970s)
PurposeConceptual / teaching referencePractical implementation
UsageTroubleshooting, vendor-neutral docsPowers the actual internet
Session layerExplicit (Layer 5)Merged into Application
Presentation layerExplicit (Layer 6)Merged into Application
Physical + Data LinkTwo separate layers (1 & 2)Combined as Link Layer
Protocol independenceProtocol-independentTCP/IP-specific
AdoptionReference / certification (CCNA, CompTIA)Universally implemented
PDU namingData, Data, Data, Segment, Packet, Frame, BitMessage, Segment, Packet, Frame
Go stdlib Network Tips

Key Go packages and their OSI/TCP-IP layer correspondences:

Go PackageOSI Layer(s)TCP/IP LayerWhat it does
net/httpL7ApplicationHTTP/HTTPS client & server
crypto/tlsL6ApplicationTLS encryption / handshake
encoding/jsonL6ApplicationData serialization
compress/gzipL6ApplicationCompression
net (Dial/Listen)L4–L5TransportTCP/UDP connections
net.TCPConnL4TransportTCP tuning (NoDelay, KeepAlive)
net.UDPConnL4TransportUDP datagrams
net.IP / IPNetL3InternetIP addressing & subnetting
net.Interfaces()L2–L3Link / InternetNIC info, MAC, flags
encoding/binaryL1LinkNetwork byte order encoding
net.ParseMACL2LinkMAC address parsing
net.ResolverL7ApplicationDNS queries (A, MX, PTR…)
Go · Complete network diagnostic — all layers at once
// network_diagnostic.go — touches all layers in one program
package main

import (
    "context"
    "encoding/binary"
    "fmt"
    "net"
    "net/http"
    "time"
)

func main() {
    // L1/L2 — Physical/Data Link: interface hardware
    fmt.Println("[L1/L2] Network Interfaces:")
    ifaces, _ := net.Interfaces()
    for _, i := range ifaces {
        if i.Flags&net.FlagUp != 0 {
            fmt.Printf("  %s MAC:%s MTU:%d\n", i.Name, i.HardwareAddr, i.MTU)
        }
    }

    // L2 — Binary/frame encoding (network byte order)
    port := uint16(443)
    b := make([]byte, 2)
    binary.BigEndian.PutUint16(b, port)
    fmt.Printf("\n[L2] Port 443 in network bytes: % X\n", b)

    // L3 — Network: IP addressing
    _, net192, _ := net.ParseCIDR("192.168.0.0/16")
    testIP := net.ParseIP("192.168.5.100")
    fmt.Printf("\n[L3] 192.168.5.100 in 192.168.0.0/16: %v\n", net192.Contains(testIP))

    // L4 — Transport: TCP dial with timeout
    conn, err := net.DialTimeout("tcp", "1.1.1.1:443", 2*time.Second)
    if err == nil {
        fmt.Printf("\n[L4] TCP to 1.1.1.1:443 OK — local: %s\n", conn.LocalAddr())
        conn.Close()
    }

    // L7 — Application: DNS resolution
    ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
    defer cancel()
    addrs, _ := net.DefaultResolver.LookupHost(ctx, "cloudflare.com")
    fmt.Printf("\n[L7/DNS] cloudflare.com → %v\n", addrs)

    // L7 — Application: HTTP request
    client := &http.Client{Timeout: 5 * time.Second}
    resp, err := client.Head("https://cloudflare.com")
    if err == nil {
        fmt.Printf("[L7/HTTP] Status: %s  Server: %s\n",
            resp.Status, resp.Header.Get("Server"))
        resp.Body.Close()
    }
}