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.
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.
// 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()
}
// 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?
2. Which Go package is primarily used for HTTP (Application Layer)?
3. What is the PDU (Protocol Data Unit) at the Application Layer?
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.
// 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?
2. In Go, which package handles gzip compression?
3. TLS (Transport Layer Security) primarily handles what concern at L6?
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.
// 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?
2. Which concept in the Session Layer allows communication to recover from a failure mid-transfer?
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.
// 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()
}
// 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?
2. When would you choose UDP over TCP?
3. What Go type represents a low-level TCP connection for tuning (e.g., SetNoDelay)?
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.
// 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()
}
// 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?
2. What does CIDR /24 notation mean?
3. Which protocol at L3 is used for error reporting and diagnostics (e.g., ping)?
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).
// 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?
2. What is the broadcast MAC address in Ethernet?
3. ARP (Address Resolution Protocol) operates at which boundary?
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.
// 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?
2. What is the standard Ethernet MTU (Maximum Transmission Unit) in bytes?
3. Why does Go use big-endian (network byte order) when encoding integers for transmission?
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.
// 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?
2. In Go, which struct field provides TLS connection metadata after an HTTPS response?
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.
// 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?
2. What modern protocol replaces TCP for low-latency web communication?
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.
// 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?
2. What is the loopback address in IPv4?
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.
// 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?
2. What does the FlagLoopback flag indicate for a network interface?
| Feature | OSI Model | TCP/IP Model |
|---|---|---|
| Number of Layers | 7 | 4 |
| Developed By | ISO (1984) | DARPA (1970s) |
| Purpose | Conceptual / teaching reference | Practical implementation |
| Usage | Troubleshooting, vendor-neutral docs | Powers the actual internet |
| Session layer | Explicit (Layer 5) | Merged into Application |
| Presentation layer | Explicit (Layer 6) | Merged into Application |
| Physical + Data Link | Two separate layers (1 & 2) | Combined as Link Layer |
| Protocol independence | Protocol-independent | TCP/IP-specific |
| Adoption | Reference / certification (CCNA, CompTIA) | Universally implemented |
| PDU naming | Data, Data, Data, Segment, Packet, Frame, Bit | Message, Segment, Packet, Frame |
Key Go packages and their OSI/TCP-IP layer correspondences:
| Go Package | OSI Layer(s) | TCP/IP Layer | What it does |
|---|---|---|---|
net/http | L7 | Application | HTTP/HTTPS client & server |
crypto/tls | L6 | Application | TLS encryption / handshake |
encoding/json | L6 | Application | Data serialization |
compress/gzip | L6 | Application | Compression |
net (Dial/Listen) | L4–L5 | Transport | TCP/UDP connections |
net.TCPConn | L4 | Transport | TCP tuning (NoDelay, KeepAlive) |
net.UDPConn | L4 | Transport | UDP datagrams |
net.IP / IPNet | L3 | Internet | IP addressing & subnetting |
net.Interfaces() | L2–L3 | Link / Internet | NIC info, MAC, flags |
encoding/binary | L1 | Link | Network byte order encoding |
net.ParseMAC | L2 | Link | MAC address parsing |
net.Resolver | L7 | Application | DNS queries (A, MX, PTR…) |
// 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()
}
}