bind before connect at client code - c

I have multiple ethernet I/Fs. eth0,eth1,eth2... and I want to connect to an external server, eg 1.2.3.4:80.
My connections are OK, but under some special circumstances I want to connect as eth1 and not eth0. the server's code checks the IP address of my interface. I think that I need to bind before connect. Without bind(2) the server always gets packets from eth0
I am looking for code that demonstrates this behavior. Does anybody has a link to an example?

You don't need bind(2) for this.
What you're looking to do here is to use a different network interface with your socket. To use a network interface other than the system default, you need to use the SO_BINDTODEVICE socket option along with setsockopt. The interface you want to use, such as "eth1" for example, should be specified as a string in the ifr_name field of a ifreq struct which is to be passed to setsockopt. To do this, you need to include the <net/if.h> header.
Basically, something like the following (untested) code:
int set_interface(int socket_fd, const char* interface_name)
{
ifreq interface;
memset(&interface, 0, sizeof(interface));
strncpy(interface.ifr_name, interface_name, IFNAMSIZ);
int res = setsockopt(socket_fd, SOL_SOCKET, SO_BINDTODEVICE, &ifreq, sizeof(ifreq));
return res;
}
Also, make sure you check the return code, in case setsockopt fails.

Related

Linux socket use SO_BINDTODEVICE but with specified IP address

I got a program which does
...
/* Only rx/tx packets on the interface */
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, name, strlen(name))) {
...
}
...
for each given ethernet interface and uses UDP.
I want every interface to use the same IP I specified (for example: 50.0.0.1/24).
So that packets coming out from eth0 tell "my source IP is 50.0.0.1"
and packets from eth1 tell the same("source IP: 50.0.0.1").
Both receiving and sending in required.
Is there any way to achieve this?
I've tried
addr.sin_addr.s_addr = inet_addr("50.0.0.1");
...
bind(fd, (struct sockaddr *)&addr, sizeof(addr));
but it won't work (error: cannot assign IP address) unless I set an unrelated interface's IP to 50.0.0.1.
After that, the packets coming out say "source IP: 50.0.0.1" but sending packets with "source IP: 50.0.0.X" to the machine (which runs the above program), it will not receive any.
You can't bind a socket to an interface and an IP at the same time, only one or the other. If an interface is bound, its IP gets used. If an IP is bound, its interface gets used.
And, you can't use bind() to set a source IP that does not belong to the bound interface.
On some platforms (Linux, etc), you can use sendmsg() with IP_PKTINFO to specify a source IP for outgoing packets. However, the OS will lookup and use the interface that belongs to the specified IP, which could be different than the bound interface, so this doesn't address your issue.
So, you will likely have to use a RAW socket and send your UDP packets with a custom IP header, then you can populate that header however you want.
I found the solution.
ip addr add 50.0.0.1/24 dev eth0
ip addr add 50.0.0.1/24 dev eth1
Start the program which does
setsocketopt(fd1, SOL_SOCKET, SO_BINDTODEVICE, "eth0", strlen("eth0"));
...
setsocketopt(fd2, SOL_SOCKET, SO_BINDTODEVICE, "eth1", strlen("eth1"));
Works like a charm.
They all send packets with source IP "50.0.0.1".
And are able receive packets belong to 50.0.0.0/24 network.
The output packets will not go to the wrong interface due to the socket is bond to specific interface(SO_BINDTODEVICE socket option).

Connect to port using TCP with C

I'm 99% new to sockets and any sort of network programming, so please bear with me.
I am aiming to connect to a port (2111 in this case) on my local machine (192.168.0.1). From there, I'm planning on sending and receiving basic information, but that's for another day.
I've currently tried this:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char **argv)
{
int sd;
int port;
int start;
int end;
int rval;
struct hostent *hostaddr;
struct sockaddr_in servaddr;
start = 2111;
end = 2112;
for(port = start; port <= end; port++)
{
sd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sd == -1)
{
perror("Socket()\n");
return (errno);
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port);
hostaddr = gethostbyname("192.168.0.1");
memcpy(&servaddr.sin_addr, hostaddr->h_addr, hostaddr->h_length);
rval = connect(sd, (struct sockaddr *)&servaddr, sizeof(servaddr));
if(rval == -1)
{
printf("Port %d is closed\n", port);
close(sd);
}
else printf("Port %d is open\n", port);
close(sd);
}
return 0;
}
However, my connect() call hangs for about 90 seconds, then returns -1.
The device is directly connected to my Mac Mini's ethernet port and the manufacturer has confirmed that the port is 2111 or 2112.
What am I doing wrong? Also, can it be in the ELI5 (explain like I'm 5) format? I'm much better off with examples.
When you call connect() to connect to a host, your computer sends a SYN packet to begin the three-way handshake of the TCP connection. From here, there are 3 possible scenarios:
If the peer is listening on that port, it responds with a SYN+ACK packet, your computer responds with a final ACK, and the connection is established—connect() returns successfully.
If the peer is not listening on that port, it responds with an ICMP packet with a type and code indicating that the port is closed, which causes your connect() call to fail almost immediately with the error ECONNREFUSED (connection refused). Under normal circumstances, this takes 1 network round-trip time (RTT) to happen, which is typically tens or hundreds if milliseconds.
If your computer never receives either an appropriate SYN+ACK TCP packet or connection refused ICMP packet, it assumes that its original SYN packet got dropped by the network somewhere and will try to resend the SYN packet several times until it gets one of those packets back or it hits an OS-dependent timeout, at which point the connect() call fails with ETIMEDOUT. This is typically 1–2 minutes, depending on the OS and its TCP settings.
You're clearly hitting case #3. This can be caused by a few different issues:
Your original SYN packets were getting lost in the network, possibly due to a faulty link, overloaded router, or firewall
The peer's SYN+ACK or ICMP response packets were getting lost in the network, possibly due to a faulty link, overloaded router, or firewall
The destination address may be unroutable/unreachable
The peer may be failing to properly respond at all with a SYN+ACK or ICMP packet
If you're directly connecting to the device over ethernet, than that rules out #1 and #2. #4 is possible, but I think #3 is the most likely explanation.
A brief aside on packet routing
Your computer has multiple network interfaces—ethernet (sometimes multiple ethernet interfaces), Wi-Fi, the loopback device, VPN tunnels, etc. Whenever you create a socket, it has to be bound to one or more particular network interfaces in order for the OS to know which NIC to actually send the packet through. For listen sockets for servers, you typically bind to all network interfaces (to listen for connections on all of them), but you can also bind to a particular network interface to only listen on that one.
For client sockets, when you connect them to other peers, you don't normally bind them to a particular interface. By default, your computer uses its internal routing tables along with the destination IP address to determine which network interface to use. For example, if you have a gateway machine with two NICs, one of which is connected to the public internet with IP 54.x.y.z and hte other of which is connected to an internal, private network with IP 192.168.1.1, then that machine will in all likelihood have routing tables that say "for packets destined to 192.168.0.0/16, use NIC 2, for all other packets, use NIC 1". If you want to bypass the routing tables, you can bind the socket to the network interface you want by calling bind() on the socket before the call to connect().
Putting it all together
So, what does that all mean for you?
First, make sure that 192.168.0.1 is in fact the correct destination address you should be connecting to. How is that address determined? Is your computer acting as a DHCP server to assign that address to the other host? Is that host using a static IP configuration?
Next, make sure that your routing tables are correct. If the other machine is assigning itself a static IP, chances are that your Mac isn't aware of how to route to that destination and is probably trying to route through the wrong interface. You can manually adjust the routes on Mac OS X with the route(8) utility, but these get reset every reboot; this blog post shows an example of using a startup item to automate adding the new route on startup. You'll want to use the IP address associated with the ethernet interface connected to the target host.
Alternatively, instead of using routing tables, you could call bind() on your socket before connect() to bind to the local address of the interface you want to use, but this won't work for other programs unless they also provide that functionality. For example, the curl(1) utility lets you pass the --interface <name> command line flag to direct it to bind to a particular interface.
Basically, connect() is failing (check errno for why).
You might consider a implementing some kind of time-out for the connect. To do this, set the socket to non-blocking mode. Then call connect(), and then use select() to wait for a response with a timeout.
SPOILER Example of ConnectWithTimeout() for Linux

bind raw socket to specific address

I am doing raw socket programming in C. I am creating my own ethernet, ip and tcp headers. Then I add data to the packet and send it. Then I need to wait for response from the other program.
I have one computer. So using lo as my interface. I have something like
CreateSocket
BindSocketToInterface
Sendto
recvfrom
Now it is receiving it's own data which is undesirable. So I am thinking to bind socket to specific address.
Can I do it?
Also How to do it??
Can select solve this problem ??
Thanks :)
Like if you want interface then as follows
char *opt;
opt = "eth0";
setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, opt, 4);
But if you want port address then
You cannot bind a raw socket to a specific port because "port" is a concept in TCP and UDP, not IP. Look at the header diagrams for those three protocols and it should become obvious: you are working at a lower level, where the concept of port is not known.
you can use setsockopt to bind it to a specific device.
struct ifreq *ifr = <populate some values>;
setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, (void *)ifr, sizeof(struct ifreq))
Try the above code and it should work. populate the ifreq correctly for the required network device.

How to use select function when you have UDP socket

I want to make a multi client - one server quiz application. In this, firstly, the clients will connect to the server and will registered themselves. Then, the server will multicast a question to every client, who have registered themselves to the server. The clients will then respond with the answer, which will be send only to the server. And then server will send the the score of each client to the respective client.
This is what I am trying to do in the above application-
1. As I have too multicast, thats why I am Making my server socket asSOCK_DGRAM (i.e.,UDP). Then I am using CLASS-D ip address, for making a group( to which server will multicast). Then is using setsockopt, I am adding the clients to this group, so that they can recieve the question.
2.As I want to listen the answers from all the clients, so I was thinking of using select. It uses the socket descriptor to select between various clients, as to know which is ready for reading.
But the problem is, when I am using SOCK_DGRAM socket, it doesnot perform listen and accept functionality. So, I will not get a socket descriptor(which is returned by accept). Thats why, I will not be able to use select(as it uses only file descriptors).
So, how am I to proceed, as I want to use UDP functionality - MULTICASTING, as well as TCP functionality - a socket descriptor for each connection.
Even when using UDP and unconnected sockets, you can still use functions like select. Simply bind the server socket to an address, and use that socket for select. When the socket is readable a client has sent something with e.g. sendto and you can do e.g. recvfrom.
However, I would really recommend you to use TCP sockets, it will make many things simpler, especially when it comes to the communication protocol (remember that UDP packages can be lost or come out of order, you have to handle that yourself).
As you said, select is not usefull here since you only have one socket on the server side.
This socket is used to send datagrams with sendto
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
in dest_addr you specify the destination address for the datagram.
Using recvfrom(2) on a UDP socket is similar :
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
src_addr is the sender address, so you are able to identify the client which has sent the response.
recvfrom calls are blocking until data are available for reading (unless you set the socket to unblocking).
You could just loop to receive all the responses.
You're on the right track but you don't need listen() or accept(). Just select for readability. When the socket becomes readable, read it. You probably don't need select() for this at all, really, just a blocking read, with a timeout if you like.

about setsockopt() and getsockopt() function

for what especially the socket options are used i.e setsockopt() and getsockopt() in socket programming ?
For example you want to set or know receive buffer size
1)
int skt, int sndsize;
err = setsockopt(skt, SOL_SOCKET, SO_RCVBUF, (char *)&sndsize,
(int)sizeof(sndsize));
err = getsockopt(skt, SOL_SOCKET, SO_RCVBUF, (char *)&sockbufsize, &size);
2) Reuse address
int on = 1;
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
For many different things including changing the size of send and receive buffers, length of timeouts, multicasting, keeping the connection alive, disabling Nagel algorithm, etc.
There are levels of options depending on what network layer you what to interact with: socket itself, IP, TCP, and so forth.
As already mentioned they are used for setting/getting various options for a socket.
For example, if you are testing a server application that crashes, you don't wont to wait a certain number of minutes before the kernel let you reuse the port avoiding the "Address already in use" error messages. This can be avoided if you use the SO_REUSEADDR option, letting other sockets to bind to the same port unless there is an active listener bound already.
You can also retrieve data about a socket, such as the number of lost packets / retransmissions etc by using the TCP_INFO on linux machines.
Basically, you can configure all the fine settings.
Options for setsockopt(2) and getsockopt(2).
Superficially, sockets look like a bidirectional pipe which is useful because standard system calls such as write, read, close can be used on them just like on normal pipes or even files. Even if you add socket-specific calls (listen, connect, bind, accept), there is a useful level of abstraction that hides away details in favor of the notion of streaming or datagram sockets.
But as soon as protocol-specific details come into play and specific settings need to be tuned (for example send/receive buffers, timeout settings), a very generic interface is needed to account for the different settings and their specific data formats. getsockopt, setsockopt are part of this generic interface.
int getsockopt(int sockfd, int level, int optname,
void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname,
const void *optval, socklen_t optlen);
The protocol-specific options are selected using level and optname and the protocol-specific data is hidden in a buffer, so the two system calls do not need to know anything about the settings of every protocol the OS may support -- it's enough if your application and the actual protocol implementation know about those details.

Resources