Socket Programming- Change Socket IP - c

Note: the question was edited.
I have few questions on Berkeley Socket Programming:
Is it possible to change a socket address after binding? If so- What are the C commands to do so?
2.According to https://www.cs.cmu.edu/~srini/15-441/F01.full/www/assignments/P2/htmlsim_split/node18.html, when a socket is bind to INADDR_ANY, it receives packets from all interfaces, but when a packet is sent,(using sent command) it sends through a single NIC with the default IP.
If I understand correctly- if a server has two active NICs, with different IPs, then a socket with the INADDR_ANY parameter can receive a packet with dst IP=x and send a packet with src IP=y, where x is not y. This can cause problems, for example in TCP connections, where the socket in the other destination is will receive the packet, but will drop it due as the dest IP is not the one expected.
Is it true? And if so- does it means that server programs are NOT using INADDR_ANY where there are (two or more) active NICs with different IPs?
Suppose the NIC of the default IP causes bottleneck. Can we change the socket options so that the packets will be send through another NICs(and not the previous NIC)? Can we do so if the NICs have the default IP address?
Can we send packets through one NIC, and set the IP destination to another NIC? (I.e, NIC1 will only send packets, and NIC2 will only receive packets )

Is it possible to change a socket address after created?
Sockets don't have an IP address to change when created. They get one when bound.
If so- What are the C commands to do so?
The Berkeley Sockets API functions to do so are bind() and connect().
When a socket is bind to INADDR_ANY, it receives packets from all interfaces, but sends through a single NIC with the default IP.
No. It sends packets via whichever NIC is necessary to reach the target in each case.
Your cited source draws a distinction without a difference. A socket bound to INADDR_ANY receives packets from any interface. There is no difference between 'any' and 'all' as far as INADDR_ANY is concerned. It is far easier to understand as 'any'.
If I understand correctly- if a server has two active NICs, with different IPs, then a socket with the INADDR_ANY parameter can receive a packet with dst IP=x and send a packet with src IP=y, where x is not y
No. It sends the packet with the same source address the client originally connected to. That's part of what defines the connection.
This can cause problems, for example in TCP connections, where the socket in the other destination is will receive the packet, but will drop it due as the dest IP is not the one expected.
No. The destination in the packet is the client's IP address. Otherwise it wouldn't even get there. This is just nonsense. If you mean the source IP, see above.
Is it true?
No.
And if so- does it means that server programs are NOT using INADDR_ANY where there are (two or more) active NICs with different IPs?
No. INADDR_ANY means exactly what it says. Any IP address: any NIC.
Suppose the NIC of the default IP causes bottleneck. Can we change the socket options so that the packets will be send through another NICs (and not the previous NIC)?
No, but you can alter the IP routing tables.
Can we do so if the NICs have the default IP address?
Only one of them can have the default IP address. The question doesn't make sense.
Can we send packets through one NIC, and set the destination to another NIC?
Only if you're sending to yourself. Otherwise the question doesn't make sense.
(from your citation) When sending, a socket bound with INADDR_ANY binds to the default IP address, which is that of the lowest-numbered interface
I hope this refers to whatever simulator is being described. If it is meant to be a description of how TCP works it is wrong.

Related

How to filter a multicast receiving socket by interface?

I need to create two sockets listening on the same IP:port but on different interfaces:
socket0 receives UDP traffic sent to 224.2.2.2:5000 on interface eth0
socket1 receives UDP traffic sent to 224.2.2.2:5000 on interface eth1
It seemed pretty straight forward until I realized that Linux merges all of that into the same traffic. For example, say there's only traffic on eth1 and there's no activity on eth0. When I first create socket0 it won't be receiving any data but as soon as I create socket1 (and join the multicast group) then socket0 will also start receiving the same data. I found this link that explains this.
Now this actually makes sense to me because the only moment when I specify the network interface is when joining the multicast group setsockopt(socket,IPPROTO_IP,IP_ADD_MEMBERSHIP,...) with ip_mreq.imr_interface.s_addr. I believe this specifies which interface joins the group but has nothing to do with from which interface your socket will receive from.
What I tried so far is binding the sockets to the multicast address and port, which behaves like mentioned above. I've tried binding to the interface address but that doesn't work on Linux (it seems to do so on Windows though), you don't receive any traffic on the socket. And finally, I've tried binding to INADDR_ANY but this isn't what I want since I will receive any other data sent to the port regardless of the destination IP, say unicast for example, and it will still not stop multicast data from other interfaces.
I cannot use SO_BINDTODEVICE since it requires root privileges.
So what I want to know is if this is possible at all. If it can't be done then that's fine, I'll take that as an answer and move on, I just haven't been able to find any way around it. Oh, and I've tagged the question as C because that's what we're using, but I'm thinking it really might not be specific to the language.
I haven't included the code for this because I believe it's more of a theoretical question rather than a problem with the source code. We've been working with sockets (multicast or otherwise) for a while now without any problems, it's just this is the first time we've had to deal with multiple interfaces. But if you think it might help I can write some minimal working example.
Edit about the possible duplicate:
I think the usecase I'm trying to achieve here is different. The socket is supposed to receive data from the same multicast group and port (224.2.2.2:5000 in the example above) but only from one specific interface. To put it another way, both interfaces are receiving data from the same multicast group (but different networks, so data is different) and I need each socket to only listen on one interface.
I think that question is about multiple groups on same port, rather than same group from different interfaces. Unless there's something I'm not seeing there that might actually help me with this.
Yes, you can do what you want on Linux, without root privileges:
Bind to INADDR_ANY and set the IP_PKTINFO socket option. You then have to use recvmsg() to receive your multicast UDP packets and to scan for the IP_PKTINFO control message. This gives you some side band information of the received UDP packet:
struct in_pktinfo {
unsigned int ipi_ifindex; /* Interface index */
struct in_addr ipi_spec_dst; /* Local address */
struct in_addr ipi_addr; /* Header Destination address */
};
The ipi_ifindex is the interface index the packet was received on. (You can turn this into an interface name using if_indextoname() or the other way round with if_nametoindex().
As you said on Windows the same network functions have different semantics, especially for UDP and even more for multicast.
The Linux bind() semantics for the IP address for UDP sockets are mostly useless. It is essentially just a destination address filter. You will almost always want to bind to INADDR_ANY for UDP sockets since you either do not care to which address a packet was sent or you want to receive packets for multiple addresses (e.g. receiving unicast and multicast).

Purpose of sendto address for C raw socket?

I'm sending some ping packets via a raw socket in C, on my linux machine.
int sock_fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
This means that I specify the IP packet header when I write to the socket (IP_HDRINCL is implied).
Writing to the socket with send fails, telling me I need to specify an address.
If I use sendto then it works. For sendto I must specify a sockaddr_in struct to use, which includes the fields sin_family, sin_port and sin_addr.
However, I have noticed a few things:
The sin_family is AF_INET - which was already specified when the socket was created.
The sin_port is naturally unused (ports are not a concept for IP).
It doesn't matter what address I use, so long as it is an external address (the IP packet specifies 8.8.8.8 and the sin_addr specifies 1.1.1.1).
It seems none of the extra fields in sendto are actually used to great extent. So, is there a technical reason why I have to use sendto instead of send or is it just an oversight in the API?
Writing to the socket with send fails, telling me I need to specify an address.
It fails, because the send() function can only be used on connected sockets (as stated here). Usually you would use send() for TCP communication (connection-oriented) and sendto() can be used to send UDP datagrams (connectionless).
Since you want to send "ping" packets, or more correctly ICMP datagrams, which are clearly connectionless, you have to use the sendto() function.
It seems none of the extra fields in sendto are actually used to great
extent. So, is there a technical reason why I have to use sendto
instead of send or is it just an oversight in the API?
Short answer:
When you are not allowed to use send(), then there is only one option left, called sendto().
Long answer:
It is not just an oversight in the API. If you want to send a UDP datagram by using an ordinary socket (e.g. SOCK_DGRAM), sendto() needs the information about the destination address and port, which you provided in the struct sockaddr_in, right? The kernel will insert that information into the resulting IP header, since the struct sockaddr_in is the only place where you specified who the receiver will be. Or in other words: in this case the kernel has to take the destination info from your struct as you don't provide an additional IP header.
Because sendto() is not only used for UDP but also raw sockets, it has to be a more or less "generic" function which can cover all the different use cases, even when some parameters like the port number are not relevant/used in the end.
For instance, by using IPPROTO_RAW (which automatically implies IP_HDRINCL), you show your intention that you want to create the IP header on your own. Thus the last two arguments of sendto() are actually redundant information, because they're already included in the data buffer you pass to sendto() as the second argument. Note that, even when you use IP_HDRINCL with your raw socket, the kernel will fill in the source address and checksum of your IP datagram if you set the corresponding fields to 0.
If you want to write your own ping program, you could also change the last argument in your socket() function from IPPROTO_RAW to IPPROTO_ICMP and let the kernel create the IP header for you, so you have one thing less to worry about. Now you can easily see how the two sendto()-parameters *dest_addr and addrlen become significant again because it's the only place where you provide a destination address.
The language and APIs are very old and have grown over time. Some APIs can look weird from todays perspective but you can't change the old interfaces without breaking a huge amount of existing code. Sometimes you just have to get used to things that were defined/designed many years or decades ago.
Hope that answers your question.
The send() call is used when the sockets are in a TCP SOCK_STREAM connected state.
From the man page:
the send() call may be used only when the socket is in a connected
state (so that the intended recipient is known).
Since your application obviously does not connect with any other socket, we cannot expect send() to work.
In addition to InvertedHeli's answer, the dest_addr passed in sendto() will be used by kernel to determine which network interface to used.
For example, if dest_addr has ip 127.0.0.1 and the raw packet has dest address 8.8.8.8, your packet will still be routed to the lo interface.

Few queries regarding raw sockets in C

I want to make a chat room using raw socket in C. I have following problems:
Q 1 : Can I use select function to handle multiple connections in case of raw sockets ?
Q 2 : Port nos in sockets are real ports or logically implemented for various applications on transport layer??
Q 3 : I am having one computer only so using lo ( local loop) as my interface. So the process which is initiating the chat has send first and then receive call, so it's receiving it's own data. How to restrict it?
Any help would be appreciated since that would help me in increasing my confidence on raw sockets.
Thanks :)
If you want this to be a real, usable chat system, stop. Don't use raw sockets. Huge mistake.
If you are just playing around because you want to put “raw sockets” under the “Experience” section of your résumé, you may read on.
You can use the select function to detect when a raw socket has a packet available to receive, and when it can accept a packet to transmit. You can pass multiple file descriptors to a single call to select if you want to check multiple raw sockets (or whatever) simultaneously.
Port numbers are part of the TCP and UDP protocols (and some other transport layer protocols). The kernel doesn't look for port numbers when receiving packets for raw sockets.
The raw(7) man page‚ states:
All packets or errors matching the protocol number specified for the raw socket are passed to this socket.
And it also states:
A raw socket can be bound to a specific local address using the bind(2) call. If it isn't bound, all packets with the specified IP protocol are received.
Therefore you probably want to at least use different IP addresses for each end of the “connection”, and bind each end to its address.
“But!” you say, “I'm using loopback! I can only use the 127.0.0.1 address!” Not so, my friend. The entire 127.0.0.0/8 address block is reserved for loopback addresses; 127.0.0.1 is merely the most commonly-used loopback address. Linux (but perhaps not other systems) responds to every address in the loopback block. Try this in one window:
nc -v -l 10150
And then in another window:
nc -s 127.0.0.1 127.0.0.2 10150
You will see that you have created a TCP connection from 127.0.0.1 to 127.0.0.2. I think you can also bind your raw sockets to separate addresses. Then, when you receive a packet, you can check whether it's from the other end's IP address to decide whether to process or discard it.
Just curious, why do you want to use raw sockets? Raw sockets (AF_INET, SOCK_RAW) allow you to send out "raw" packets, where you are responsible for crafting everything but the MAC and IP layers.
A1: There are no "connections" with raw sockets. Just packets.
A2: There are no "ports" with raw sockets. Just packets. "Port numbers" as we know them are part of the TCP or UDP protocols, both of which are above the level at which we work with raw sockets.
A3: This is not specific to raw sockets - you would have this issue regardless of your protocol selection. To really answer this, we would need to know much more about your proposed protocol, since right now, you're simply blasting out raw IP packets.

How can i avoid mulitple raw socket bind to the same ip address?

Actually, if i create multiple RAW sockets with the same IP Address. I could bind all of them, and consequently packets are received by all the sockets.
Is there any way that could be avoided, such that the other process trying to bind the same ip address receives an error?
I am using a raw socket
#include <sys/socket.h>
#include <netinet/in.h>
raw_socket = socket(AF_INET, SOCK_RAW, int protocol);
In Man Page raw(7)
A raw socket can be bound to a specific local address using the
bind(2) call. If it isn't bound all packets with the specified IP
protocol are received. In addition a RAW socket can be bound to a
specific network device using SO_BINDTODEVICE; see socket(7).
You cannot bind a raw socket to a specific port because "port" is a concept in TCP and UDP, not IP. With a sneek at the header diagrams for those three protocols and it should become obvious: i am working at a lower level, where the concept of port is not known. This is what i understand regarding port numbers.
No. The mere fact that its RAW means there's no other protocol except RAW Internet Protocol. Without TCP or UDP, there won't be any port to distinguish which application this packet gets sent to, so instead, everything will have to be filtered through the IP packet's payload. You'd have to do this manually. Best way is to make a program that forwards these packets after inspection to wherever you want it to go.

Is there a way to to set a socket option to indicate which interface the packets go to

Currently, this is my predicament.
I have 2 fd's : x and y. When a write(x) happens, it must go to x-tunnel and a write(y) should go to y-tunnel. I cannot create a routing rule for each connection (Reasons not mentioned here)
Is there a sock opt which I can set when I accept a connection or is there any other way to do it?
Thanks
you can control in sock binding on specific interface for x, y tunnels
check for SO_DONTROUTE option in setsockopt
Your description is sketchy, I think setsockopt() by optname of SO_REUSEADDR and SO_REUSEPORT may help you, but I'm not sure.It can't 'indicate' which interface the packets go to,but you can indicate them by your implement.
Here some reference on Unix Network Programming, if you want to know more detail,you can read them and examples in chapter7.5.
The SO_REUSEADDR socket option serves four different purposes:
SO_REUSEADDR allows a listening server to start and bind its well-known port,
even if previously established connections exist that use this port as their local
port.
SO_REUSEADDR allows a new server to be started on the same port as an
existing server that is bound to the wildcard address, as long as each instance
binds a different local IP address.
SO_REUSEADDR allows a single process to bind the same port to multiple
sockets, as long as each bind specifies a different local IP address.
SO_REUSEADDR allows completely duplicate bindings: a bind of an IP address
and port, when that same IP address and port are already bound to another
socket, if the transport protocol supports it. Normally this feature is supported
only for UDP sockets.

Resources