# NimPacket Python Bindings

> **Fast, low-level packet manipulation from Python - powered by Nim**

NimPacket's Python bindings give you full access to every protocol, parser, checksum, raw socket, and fragmentation feature. The compiled Nim extension runs at native speed.

## Table of Contents

- [Installation](#installation)
- [Quick Start](#quick-start)
- [API Reference](#api-reference)
  - [Packet Builders](#packet-builders)
  - [IPv4](#ipv4)
  - [IPv6](#ipv6)
  - [TCP](#tcp)
  - [UDP](#udp)
  - [ICMP](#icmp)
  - [ICMPv6](#icmpv6)
  - [Ethernet](#ethernet)
  - [ARP](#arp)
  - [DNS](#dns)
  - [DHCP](#dhcp)
  - [Full Packet Parsing](#full-packet-parsing)
  - [IP Fragmentation](#ip-fragmentation)
  - [Raw Sockets](#raw-sockets)
  - [Utilities](#utilities)
- [Constants](#constants)
- [Examples](#examples)
- [Building from Source](#building-from-source)

---

## Installation

### Option 1: Build from source (recommended)

```bash
git clone https://github.com/0x57Origin/NimPacket.git
cd NimPacket/python
python build.py
```

### Option 2: Dev install

```bash
cd NimPacket/python
pip install -e .
python build.py
```

### Requirements

- Python 3.8+
- Nim 1.6.0+
- nimpy (`nimble install nimpy`)

---

## Quick Start

```python
import nimpacket as np

# Build a complete TCP SYN packet (checksums included)
packet = np.build_tcp_syn_packet("192.168.1.100", "192.168.1.1", 54321, 80)
print(f"Packet: {len(packet)} bytes")

# Parse it back
parsed = np.parse_packet(packet)
print(f"{parsed['ipv4']['src_ip']}:{parsed['tcp']['src_port']} -> "
      f"{parsed['ipv4']['dst_ip']}:{parsed['tcp']['dst_port']}")

# Create a DNS query
dns = np.create_dns_query("example.com", np.DNS_TYPE_A)
print(np.parse_dns_packet(dns))

# Create a DHCP Discover
dhcp = np.create_dhcp_discover("AA:BB:CC:DD:EE:FF")
print(np.parse_dhcp_packet(dhcp))
```

---

## API Reference

### Packet Builders

High-level functions that build complete packets with proper checksums.

#### `build_tcp_syn_packet(src_ip, dst_ip, src_port, dst_port, ttl=64) -> bytes`

Builds a complete IPv4 + TCP SYN packet.

```python
packet = np.build_tcp_syn_packet("192.168.1.100", "10.0.0.1", 54321, 80, ttl=128)
```

#### `build_icmp_echo_packet(src_ip, dst_ip, identifier, sequence, payload=b"", ttl=64) -> bytes`

Builds a complete IPv4 + ICMP Echo Request packet.

```python
ping = np.build_icmp_echo_packet("192.168.1.100", "8.8.8.8", 1234, 1)
ping_with_data = np.build_icmp_echo_packet("192.168.1.100", "8.8.8.8", 1234, 1, payload=b"hello")
```

#### `build_udp_packet(src_ip, dst_ip, src_port, dst_port, payload=b"", ttl=64) -> bytes`

Builds a complete IPv4 + UDP packet.

```python
udp = np.build_udp_packet("10.0.0.1", "10.0.0.2", 12345, 53, payload=b"data")
```

#### `build_arp_frame(src_mac, src_ip, dst_ip, dst_mac="FF:FF:FF:FF:FF:FF") -> bytes`

Builds a complete Ethernet + ARP frame. Broadcast MAC = ARP request, specific MAC = ARP reply.

```python
request = np.build_arp_frame("AA:BB:CC:DD:EE:FF", "192.168.1.100", "192.168.1.1")
reply = np.build_arp_frame("AA:BB:CC:DD:EE:FF", "192.168.1.100", "192.168.1.1", dst_mac="11:22:33:44:55:66")
```

---

### IPv4

#### `create_ipv4_header(src_ip, dst_ip, protocol, ttl=64, identification=0, total_length=20) -> bytes`

```python
ip = np.create_ipv4_header("192.168.1.100", "10.0.0.1", np.IPPROTO_TCP, ttl=128)
```

#### `parse_ipv4_header(data: bytes) -> dict`

Returns: `{version, header_length, type_of_service, total_length, identification, flags, fragment_offset, ttl, protocol, checksum, src_ip, dst_ip}`

```python
parsed = np.parse_ipv4_header(ip)
print(parsed["src_ip"], parsed["ttl"])
```

#### `calculate_ipv4_checksum(data: bytes) -> int`

```python
checksum = np.calculate_ipv4_checksum(ip)
```

---

### IPv6

#### `create_ipv6_header(src_ip, dst_ip, next_header, payload_length=0, hop_limit=64) -> bytes`

```python
ipv6 = np.create_ipv6_header("2001:db8::1", "2001:db8::2", np.IPV6_NEXT_HEADER_TCP, payload_length=20)
```

#### `parse_ipv6_header(data: bytes) -> dict`

Returns: `{version, traffic_class, flow_label, payload_length, next_header, hop_limit, src_ip, dst_ip}`

```python
parsed = np.parse_ipv6_header(ipv6)
```

---

### TCP

#### `create_tcp_header(src_port, dst_port, flags, seq_num=0, ack_num=0, window_size=65535) -> bytes`

```python
syn = np.create_tcp_header(54321, 80, np.TCP_SYN)
syn_ack = np.create_tcp_header(80, 54321, np.TCP_SYN | np.TCP_ACK, seq_num=1000, ack_num=1001)
```

#### `parse_tcp_header(data: bytes) -> dict`

Returns: `{src_port, dst_port, seq_num, ack_num, header_length, flags, window_size, checksum, urgent_pointer}`

#### `calculate_tcp_checksum(ip_header: bytes, tcp_header: bytes, payload: bytes = b"") -> int`

```python
checksum = np.calculate_tcp_checksum(ip, tcp, payload)
```

---

### UDP

#### `create_udp_header(src_port, dst_port, length=8) -> bytes`

```python
udp = np.create_udp_header(12345, 53, length=16)
```

#### `parse_udp_header(data: bytes) -> dict`

Returns: `{src_port, dst_port, length, checksum}`

#### `calculate_udp_checksum(ip_header: bytes, udp_header: bytes, payload: bytes = b"") -> int`

---

### ICMP

#### `create_icmp_header(icmp_type, code=0, identifier=0, sequence=0) -> bytes`

```python
echo_req = np.create_icmp_header(np.ICMP_ECHO_REQUEST, identifier=1234, sequence=1)
```

#### `parse_icmp_header(data: bytes) -> dict`

Returns: `{icmp_type, code, checksum, identifier, sequence}`

#### `calculate_icmp_checksum(icmp_header: bytes, payload: bytes = b"") -> int`

---

### ICMPv6

#### `create_icmpv6_echo_request(identifier, sequence) -> bytes`

```python
req = np.create_icmpv6_echo_request(1234, 1)
```

#### `create_icmpv6_echo_reply(identifier, sequence) -> bytes`

#### `parse_icmpv6_header(data: bytes) -> dict`

Returns: `{icmp_type, code, checksum}`

---

### Ethernet

#### `create_ethernet_header(src_mac, dst_mac, ether_type) -> bytes`

```python
eth = np.create_ethernet_header("AA:BB:CC:DD:EE:FF", "FF:FF:FF:FF:FF:FF", np.ETHERTYPE_ARP)
```

#### `parse_ethernet_header(data: bytes) -> dict`

Returns: `{src_mac, dst_mac, ether_type}`

---

### ARP

#### `create_arp_request(sender_mac, sender_ip, target_ip) -> bytes`

```python
arp = np.create_arp_request("AA:BB:CC:DD:EE:FF", "192.168.1.100", "192.168.1.1")
```

#### `create_arp_reply(sender_mac, sender_ip, target_mac, target_ip) -> bytes`

```python
arp = np.create_arp_reply("AA:BB:CC:DD:EE:FF", "192.168.1.100", "11:22:33:44:55:66", "192.168.1.1")
```

#### `parse_arp_packet(data: bytes) -> dict`

Returns: `{hardware_type, protocol_type, hardware_size, protocol_size, opcode, sender_mac, sender_ip, target_mac, target_ip}`

---

### DNS

#### `create_dns_query(domain, query_type=1, transaction_id=0) -> bytes`

```python
query_a = np.create_dns_query("example.com", np.DNS_TYPE_A, 0x1234)
query_mx = np.create_dns_query("example.com", np.DNS_TYPE_MX)
query_aaaa = np.create_dns_query("example.com", np.DNS_TYPE_AAAA)
```

#### `parse_dns_packet(data: bytes) -> dict`

Returns: `{transaction_id, is_response, opcode, authoritative, truncated, recursion_desired, recursion_available, rcode, question_count, answer_count, authority_count, additional_count, questions, answers}`

Each question: `{name, type, type_name, class}`
Each answer: `{name, type, type_name, class, ttl, rdlength}`

```python
parsed = np.parse_dns_packet(query)
print(parsed["questions"][0]["name"])  # "example.com"
```

#### `encode_domain_name(domain) -> bytes`

Encodes domain to DNS wire format.

```python
encoded = np.encode_domain_name("www.example.com")
# b'\x03www\x07example\x03com\x00'
```

**Supported record types:** A, AAAA, CNAME, MX, TXT, NS, PTR, SOA, SRV, ANY

---

### DHCP

#### `create_dhcp_discover(mac, transaction_id=0) -> bytes`

```python
discover = np.create_dhcp_discover("AA:BB:CC:DD:EE:FF")
discover = np.create_dhcp_discover("AA:BB:CC:DD:EE:FF", 0xDEADBEEF)
```

#### `create_dhcp_request(mac, requested_ip, server_ip, transaction_id=0) -> bytes`

```python
request = np.create_dhcp_request("AA:BB:CC:DD:EE:FF", "192.168.1.100", "192.168.1.1")
```

#### `create_dhcp_release(mac, client_ip, server_ip, transaction_id=0) -> bytes`

```python
release = np.create_dhcp_release("AA:BB:CC:DD:EE:FF", "192.168.1.100", "192.168.1.1")
```

#### `parse_dhcp_packet(data: bytes) -> dict`

Returns: `{op, htype, hlen, hops, transaction_id, secs, flags, client_ip, your_ip, server_ip, gateway_ip, client_mac, message_type, message_type_code, options}`

Each option: `{code, length}`

```python
parsed = np.parse_dhcp_packet(discover)
print(parsed["message_type"])  # "DISCOVER"
print(parsed["client_mac"])    # "AA:BB:CC:DD:EE:FF"
```

**Supported message types:** DISCOVER, OFFER, REQUEST, DECLINE, ACK, NAK, RELEASE, INFORM

---

### Full Packet Parsing

#### `parse_packet(data: bytes) -> dict`

Auto-detects protocol stack (Ethernet/IPv4/IPv6/TCP/UDP/ICMP/ARP) and parses all layers.

```python
packet = np.build_tcp_syn_packet("1.2.3.4", "5.6.7.8", 1234, 80)
p = np.parse_packet(packet)

print(p["ipv4"]["src_ip"])    # "1.2.3.4"
print(p["tcp"]["dst_port"])   # 80
print(p["has_ipv6"])          # False
print(p["has_ethernet"])      # False
```

Works with: IPv4+TCP, IPv4+UDP, IPv4+ICMP, IPv6+TCP, IPv6+UDP, IPv6+ICMPv6, Ethernet+ARP, Ethernet+IPv4/IPv6

---

### IP Fragmentation

5 evasion strategies for IDS/IPS testing.

#### `fragment_packet(ip_header, payload, strategy, fragment_size=8) -> list[bytes]`

```python
ip = np.create_ipv4_header("1.2.3.4", "5.6.7.8", np.IPPROTO_TCP, total_length=60)
payload = bytes(range(40))

frags = np.fragment_packet(ip, payload, np.FRAG_TINY, fragment_size=8)
frags = np.fragment_packet(ip, payload, np.FRAG_OVERLAP)
frags = np.fragment_packet(ip, payload, np.FRAG_OUT_OF_ORDER)
frags = np.fragment_packet(ip, payload, np.FRAG_TIME_DELAYED)
frags = np.fragment_packet(ip, payload, np.FRAG_POLYMORPHIC)
```

#### `send_fragmented(ip_header, payload, dest_ip, strategy, delay_ms=100) -> int`

Sends fragmented packets over a raw socket. Returns bytes sent or -1 on error. Requires admin.

---

### Raw Sockets

All raw socket functions require administrator/root privileges.

#### `is_admin() -> bool`

```python
if np.is_admin():
    print("Running as admin")
```

#### Socket creation

```python
np.create_raw_socket(protocol=6)  # 1=ICMP, 6=TCP, 17=UDP
np.create_icmp_socket()
np.create_tcp_socket()
np.create_udp_socket()
np.close_socket()
```

#### Sending and receiving

```python
bytes_sent = np.send_packet(packet, "192.168.1.1")    # returns bytes sent or -1
response = np.receive_packet(timeout_ms=5000, max_size=65535)  # returns bytes
```

#### Socket options

```python
np.set_socket_timeout(3000)       # 3 second timeout
np.set_ip_header_include(True)    # custom IP headers
np.set_broadcast(True)            # enable broadcast
```

---

### Utilities

#### IP Address

```python
np.parse_ipv4("192.168.1.1")       # -> 3232235777
np.ip_to_string(3232235777)         # -> "192.168.1.1"
np.parse_ipv6("2001:db8::1")       # -> bytes (16 bytes)
np.ipv6_to_string(ipv6_bytes)      # -> "2001:db8::1"
```

#### MAC Address

```python
np.parse_mac("AA:BB:CC:DD:EE:FF")  # -> bytes (6 bytes)
np.mac_to_string(mac_bytes)         # -> "AA:BB:CC:DD:EE:FF"
np.broadcast_mac()                  # -> b'\xff\xff\xff\xff\xff\xff'
```

#### Byte Order

```python
np.htons(80)     # host to network (16-bit)
np.ntohs(x)      # network to host (16-bit)
np.htonl(x)      # host to network (32-bit)
np.ntohl(x)      # network to host (32-bit)
```

#### Info

```python
np.version()              # "0.2.0"
np.supported_protocols()  # ["IPv4", "IPv6", "TCP", ...]
np.help()                 # print all available functions
```

#### Aliases

```python
# These are shortcuts to the create functions
np.IP(...)       # = np.create_ipv4_header
np.IPv4(...)     # = np.create_ipv4_header
np.IPv6(...)     # = np.create_ipv6_header
np.TCP(...)      # = np.create_tcp_header
np.UDP(...)      # = np.create_udp_header
np.ICMP(...)     # = np.create_icmp_header
np.Ethernet(...) # = np.create_ethernet_header
np.ARP(...)      # = np.create_arp_request
np.DNS(...)      # = np.create_dns_query
np.DHCP(...)     # = np.create_dhcp_discover

np.ip_to_int(...)    # = np.parse_ipv4
np.int_to_ip(...)    # = np.ip_to_string
np.mac_to_bytes(...) # = np.parse_mac
np.bytes_to_mac(...) # = np.mac_to_string
```

---

## Constants

### Protocol Numbers
```python
IPPROTO_ICMP   = 1
IPPROTO_TCP    = 6
IPPROTO_UDP    = 17
IPPROTO_ICMPV6 = 58
```

### TCP Flags
```python
TCP_FIN = 0x01
TCP_SYN = 0x02
TCP_RST = 0x04
TCP_PSH = 0x08
TCP_ACK = 0x10
TCP_URG = 0x20
```

### EtherTypes
```python
ETHERTYPE_IPV4 = 0x0800
ETHERTYPE_IPV6 = 0x86DD
ETHERTYPE_ARP  = 0x0806
```

### ARP
```python
ARP_REQUEST = 1
ARP_REPLY   = 2
```

### DNS Record Types
```python
DNS_TYPE_A    = 1     # IPv4 address
DNS_TYPE_NS   = 2     # Nameserver
DNS_TYPE_CNAME = 5    # Canonical name
DNS_TYPE_SOA  = 6     # Start of authority
DNS_TYPE_PTR  = 12    # Pointer
DNS_TYPE_MX   = 15    # Mail exchange
DNS_TYPE_TXT  = 16    # Text record
DNS_TYPE_AAAA = 28    # IPv6 address
DNS_TYPE_SRV  = 33    # Service
DNS_TYPE_ANY  = 255   # Any record type
```

### DHCP Message Types
```python
DHCP_DISCOVER = 1
DHCP_OFFER    = 2
DHCP_REQUEST  = 3
DHCP_DECLINE  = 4
DHCP_ACK      = 5
DHCP_NAK      = 6
DHCP_RELEASE  = 7
DHCP_INFORM   = 8
```

### ICMP Types
```python
ICMP_ECHO_REPLY    = 0
ICMP_ECHO_REQUEST  = 8
ICMP_TIME_EXCEEDED = 11
```

### ICMPv6 Types
```python
ICMPV6_ECHO_REQUEST          = 128
ICMPV6_ECHO_REPLY            = 129
ICMPV6_ROUTER_SOLICITATION   = 133
ICMPV6_NEIGHBOR_SOLICITATION = 135
```

### IPv6 Next Headers
```python
IPV6_NEXT_HEADER_TCP    = 6
IPV6_NEXT_HEADER_UDP    = 17
IPV6_NEXT_HEADER_ICMPV6 = 58
```

### Fragmentation Strategies
```python
FRAG_TINY         = 0  # 8-byte minimum fragments
FRAG_OVERLAP      = 1  # Overlapping with poison data
FRAG_OUT_OF_ORDER = 2  # Reversed sequence
FRAG_TIME_DELAYED = 3  # Delays between fragments
FRAG_POLYMORPHIC  = 4  # Random strategy
```

---

## Examples

### TCP SYN Scan

```python
import nimpacket as np

target = "192.168.1.1"
ports = [22, 80, 443, 8080]

for port in ports:
    packet = np.build_tcp_syn_packet("192.168.1.100", target, 54321, port)
    parsed = np.parse_packet(packet)
    print(f"SYN -> {target}:{parsed['tcp']['dst_port']} ({len(packet)} bytes)")
```

### ICMP Ping

```python
import nimpacket as np

ping = np.build_icmp_echo_packet("192.168.1.100", "8.8.8.8", 1234, 1, payload=b"nimpacket")
parsed = np.parse_packet(ping)
print(f"Ping {parsed['ipv4']['dst_ip']} id={parsed['icmp']['identifier']}")
```

### DNS Query

```python
import nimpacket as np

query = np.create_dns_query("google.com", np.DNS_TYPE_A, 0xBEEF)
parsed = np.parse_dns_packet(query)
print(f"Query: {parsed['questions'][0]['name']} ({parsed['questions'][0]['type_name']})")
```

### DHCP Discovery

```python
import nimpacket as np

discover = np.create_dhcp_discover("AA:BB:CC:DD:EE:FF", 0xDEADBEEF)
parsed = np.parse_dhcp_packet(discover)
print(f"DHCP {parsed['message_type']} from {parsed['client_mac']}")

request = np.create_dhcp_request("AA:BB:CC:DD:EE:FF", "192.168.1.100", "192.168.1.1")
parsed = np.parse_dhcp_packet(request)
print(f"DHCP {parsed['message_type']} options: {len(parsed['options'])}")
```

### Send Real Packets (requires admin)

```python
import nimpacket as np

if not np.is_admin():
    print("Need admin privileges!")
    exit(1)

# Build and send TCP SYN
np.create_tcp_socket()
packet = np.build_tcp_syn_packet("192.168.1.100", "192.168.1.1", 54321, 80)
sent = np.send_packet(packet, "192.168.1.1")
print(f"Sent {sent} bytes")

# Wait for response
response = np.receive_packet(timeout_ms=3000)
if response:
    parsed = np.parse_packet(response)
    print(f"Response from {parsed['ipv4']['src_ip']}")

np.close_socket()
```

### IP Fragmentation

```python
import nimpacket as np

ip = np.create_ipv4_header("1.2.3.4", "5.6.7.8", np.IPPROTO_TCP, total_length=60)
payload = bytes(range(40))

for strategy, name in [(np.FRAG_TINY, "Tiny"), (np.FRAG_OVERLAP, "Overlap"),
                        (np.FRAG_OUT_OF_ORDER, "OutOfOrder")]:
    frags = np.fragment_packet(ip, payload, strategy)
    print(f"{name}: {len(frags)} fragments, sizes: {[len(f) for f in frags]}")
```

---

## Building from Source

### Prerequisites

1. Install Nim (1.6.0+): https://nim-lang.org/install.html
2. Install nimpy: `nimble install nimpy`

### Build

```bash
cd NimPacket/python
python build.py
python build.py --clean   # clean artifacts
python build.py --test    # build and test
```

### Manual Build

```bash
# Windows
nim c --app:lib --out:python/nimpacket/nimpacket_py.pyd -d:release --passL:-static src/nimpacket_py.nim

# Linux/macOS
nim c --app:lib --out:python/nimpacket/nimpacket_py.so -d:release src/nimpacket_py.nim
```

### Run Tests

```bash
cd python
python examples/test_bindings.py   # 233 tests covering every function
python examples/basic_usage.py     # usage demo
```
