Coding Divert Sockets/How Do They Work?
Divert Socket Anatomy
Divert sockets are quite similar to raw IP sockets. One major difference between them is instead of binding to an IP address, like raw IP sockets, divert sockets bind to a programmer-defined divert port. The IPFW program or other packet filter is used to match packets you want the socket to receive, and once a match is found, it can divert or tee the packet.
Is there a tee socket?
Nope. Teeing is a process in which a copy of the packet is sent to your divert socket, meanwhile diverting stops the packet completely from continuing up the network stack and requires the application reinject it into the network stack. Teeing is generally a good idea if you want to just capture packets.
Receiving incoming and outgoing packets happen through either one of the read(), recv(), and recvfrom() calls. In the latter function, the address port returned is a tag set by the packet filter. IPFW, in this case, would set the port to the rule number of the matching rule that diverted the packet. As for the IP address field, incoming packets will trigger setting the IP address returned to the device that received the packet. The BSD name of the device is placed in the last 8 bytes of the address, assuming it fits. Otherwise, the IP address is simply set to INADDR_ANY when outgoing packets are received, and the last 8 bytes of the returned address are null.
Sending packets works just as it does with raw IP sockets. You may send packets through the socket by using either of the write(), send(), or sendto() calls. In the latter case, a destination address of INADDR_ANY will cause the packet to be treated as an outgoing packet. Any other address will cause the packet to be treated as an incoming packet.
Error checking on raw packets
When a raw packet enters IP processing, little error checking is done (just as in raw IP sockets.) If you send a packet with a bad IP checksum, it will be sent with a bad IP checksum and will report no error. If you send a packet with an incorrect datagram length, the packet will be dropped and errno will be set to EINVAL. It is your job to make sure there is no mistake when sending your packet, or it will forever be lost in the mighty depths of the Internet.
send() or sendto() to send packets?
Send() is much easier to work with, but it does not allow you to explicitly choose which device will receive the packet you want to send. If you're clueless, you want send(), save yourself a couple debug sessions.