import socket
import sys


''' 
    Sniff IPv4 packets and parse the bytes into their IP fields

	Author: Tim Pierson, Dartmouth CS60, Fall 2026
            Assisted by Microsoft Copilot
	
	run: python3 parse_ip_starter.py
'''

def get_nibble_left(byte):
    """
    Extracts the left (most significant / high-order) 4 bits of a byte.
    Used for extracting fields like the IP Version.
    """
    # Shift the high 4 bits to the rightmost position (e.g., 0x45 -> 0x04)
    return byte >> 4


def get_nibble_right(byte):
    """
    Extracts the right (least significant / low-order) 4 bits of a byte.
    Used for extracting fields like the IP Internet Header Length (IHL).
    """
    # Isolate the low 4 bits using a bitwise AND mask of 1111 (0x0F)
    return byte & 0x0F

def parse_ipv4_packet(packet_bytes):
    # TODO: decode Layer 3 headers
    
    print("-"*40)
    print(f"IP Version: {version}")
    print(f"IHL (Header Length): {ip_header_length} bytes")
    print(f"Protocol: {protocol}")
    print(f"TTL: {ttl}")
    print(f"Source IP: {src_ip}")
    print(f"Destination IP: {dest_ip}")
    print("-"*40)


if __name__ == "__main__":
    try:
        # AF_PACKET: Sniff directly at the device driver level (Linux)
        # socket.SOCK_DGRAM: Strips Layer 2 header, but keeps Layer 3 header
        # socket.htons(3): ETH_P_ALL captures ALL protocols (IP, ARP, IPv6, etc.) incoming & outgoing
        sniffer = socket.socket(socket.AF_PACKET, socket.SOCK_DGRAM, socket.htons(3))
        #sniffer.bind(("tun0", 0)) #capture only on this interface, otherwise capture on all interfaces
        print("Starting to sniff... (Ctrl+C to stop)")
    except PermissionError:
        print("You must run this script with 'sudo'")
        sys.exit(1)

    try:
        while True:
            # Receive pure Layer 3 IP packets (Max size 65535 bytes)
            raw_data, _ = sniffer.recvfrom(65535)
            if len(raw_data) < 20:
                print("Not an IP packet")
                continue 
            
            # Extract packet fields
            packet_info = parse_ipv4_packet(raw_data)
            
    except KeyboardInterrupt:
        print("\nSniffer terminated by Ctrl+C")