Your OS maintains a route table which tell it where a packet should go. This table is also used to put on the packet from which address the packet comes from.
Then, you can program
source.sin_addr.s_addr=htol(INADDR_ANY); to indicate before
bind() that the source address is not specified. (It is unspecified by default, but this line is useful if you want to use
bind() to bind only the source port.)
Note that the
-B option makes
ping bind the socket then the second and following source address can be fixed. The
-I can bind an interface which set the source address.
This replies more or less to the 3 first points.
About the case of a ping from LAN1 (let’s say 10.0.1.5) to LAN2 (10.0.2.5)
10.0.1.5 uses its route table indicating that 10.0.2.0/24 is reacheable through 10.0.1.1 (the gateway). It sends to this ethernet address a ping « from 10.0.1.5 to 10.0.2.5 ». Then the gateway see it, find 10.0.2.5 is directly reachable via LAN2 and forward the packet. The answer from 10.0.2.5 to 10.0.1.5 follows the same pattern.
The fourth point is more tricky. With public addresses, it works flawlessly because all gateways exchange their routes and everyone knows who is where (through which gateway). Sometimes, the route table is simple : LAN1 is reachable directly through the interface eth0, everything else through the router X on the LAN1. But for many uses we use local addresses : a 10.0.0.5 addresse is not reachable from Internet. Then when 10.0.0.5 send an IP packet, the router change the from address with a public address well known by Internet routers and memorizes the switch. When the answer come back to the public address, the router (which had memorised the switch), recover the actual address (10.0.0.5) and 10.0.0.5 can have its response.