已知两端本地及公网IP,跨网络发送数据包(Scapy/Python Socket)
Great question! Sending packets across separate networks means dealing with NAT (Network Address Translation) — that thing your home router uses to let multiple devices share a single public IP. Let's walk through how to pull this off with both Python's socket module and Scapy, including the critical setup you'll need first.
First, The Critical Pre-Requisite: Port Forwarding
Before writing any code, you must configure port forwarding on the receiving network's router. Here's why:
- When a packet hits the receiver's public IP, the router has no idea which local device to send it to. Port forwarding tells the router: "Any traffic coming in on port X should go to the receiver's local IP (e.g., 192.168.1.100) on port Y."
Make sure you set this up first — without it, your packets will just get dropped at the receiver's router.
Method 1: Using Python's Built-in socket Module
The socket module is perfect for sending application-layer data (like TCP/UDP) across networks. It handles most low-level routing automatically, as long as port forwarding is in place.
Example: Sending UDP Packets
UDP is connectionless, so it's simpler for quick testing:
import socket # Sender's details (you can omit local IP/port if you want the OS to pick them) sender_local_ip = "192.168.0.5" # Replace with your local IP sender_local_port = 12345 # Receiver's details receiver_public_ip = "203.0.113.45" # Replace with receiver's public IP receiver_forwarded_port = 54321 # The port you forwarded on their router # Create a UDP socket sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Optional: Bind to the sender's specific local IP/port sock.bind((sender_local_ip, sender_local_port)) # Send data message = b"Hello from the other network!" sock.sendto(message, (receiver_public_ip, receiver_forwarded_port)) print(f"Sent packet to {receiver_public_ip}:{receiver_forwarded_port}") # Close the socket sock.close()
Example: Sending TCP Packets
TCP is connection-oriented, so you'll need a listener on the receiver's end first:
Receiver Side (Local Network)
import socket receiver_local_ip = "192.168.1.100" receiver_local_port = 54321 # Create TCP socket server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_sock.bind((receiver_local_ip, receiver_local_port)) server_sock.listen(1) print(f"Listening for connections on {receiver_local_ip}:{receiver_local_port}") conn, addr = server_sock.accept() print(f"Connected by {addr}") while True: data = conn.recv(1024) if not data: break print(f"Received: {data.decode()}") conn.sendall(b"Message received!") conn.close()
Sender Side
import socket sender_local_ip = "192.168.0.5" sender_local_port = 12345 receiver_public_ip = "203.0.113.45" receiver_forwarded_port = 54321 # Matches the port forwarded to receiver's local port # Create TCP socket client_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client_sock.bind((sender_local_ip, sender_local_port)) # Optional # Connect to receiver's public IP/forwarded port client_sock.connect((receiver_public_ip, receiver_forwarded_port)) # Send data message = b"Hello via TCP!" client_sock.sendall(message) # Receive response response = client_sock.recv(1024) print(f"Received response: {response.decode()}") client_sock.close()
Method 2: Using Scapy
Scapy lets you craft raw packets at the network/transport layer, giving you full control over every field. Note that you'll need root/sudo privileges to send raw packets.
Example: Sending a Raw UDP Packet
from scapy.all import IP, UDP, send # Sender's details sender_local_ip = "192.168.0.5" sender_public_ip = "198.51.100.20" # Your network's public IP (Scapy might auto-detect this, but you can specify) # Receiver's details receiver_public_ip = "203.0.113.45" receiver_forwarded_port = 54321 # Craft the packet packet = IP(src=sender_local_ip, dst=receiver_public_ip) / UDP(sport=12345, dport=receiver_forwarded_port) / b"Raw UDP packet from Scapy!" # Send the packet (use sudo on Linux/macOS) send(packet, verbose=1)
Important Notes for Scapy:
- NAT Handling: Scapy will handle NAT on your sender's router automatically (your router will replace the source local IP with your public IP in the outgoing packet). The receiver's router needs port forwarding to send the packet to their local device.
- Permissions: On Linux/macOS, run the script with
sudo— raw packet sending requires root access. On Windows, you might need to install WinPcap/Npcap and run the command prompt as administrator. - TCP Packets: If you want to send TCP packets, you'll need to handle the 3-way handshake (SYN, SYN-ACK, ACK) manually, or use Scapy's
sendpwith more advanced logic. For most use cases, thesocketmodule is easier for TCP.
Key Things to Remember
- Firewalls: Make sure both sender and receiver have firewalls allowing traffic on the ports you're using (e.g., 54321 in the examples).
- Dynamic Public IPs: If either network has a dynamic public IP (common with home ISPs), you'll need a dynamic DNS service to track changes.
- NAT Traversal: For scenarios where port forwarding isn't possible (e.g., both devices are behind strict NAT), you might need a relay server (like a VPN or a custom middleman server) to route traffic between them.
内容的提问来源于stack exchange,提问作者Liron

