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.
Related
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).
I'm trying to implement my own transport layer protocol, but I'm perfectly happy to leave the network layer as-is and not need to mess with the actual IP header information.
But of course, when calling recvfrom() on a raw socket, you are given the raw IP datagram, while the sockaddr struct is not filled in.
Is there anyway to coax the stack to fill in those structs and leave the ip header out of the data portion, or does that need to be implemented by hand?
Receiver:
struct sockaddr_in sender;
int sender_len;
raw_socket = socket(AF_INET, SOCK_RAW, 56);
...
if((n = recvfrom(raw_socket, buf, 1024, 0, (struct sockaddr*)&sender, &sender_len)) == -1){
perror("recvfrom");
return -1;
}
The IP header will always be included when receiving on a SOCK_RAW socket.
Per raw(7):
The IPv4 layer generates an IP header when sending a packet unless the IP_HDRINCL socket option is enabled on the socket. When it is enabled, the packet must contain an IP header. For receiving the IP header is always included in the packet.
Reference:
ip(7) man page
SOCK_RAW Demystified
Advanced TCP/IP - THE RAW SOCKET PROGRAM EXAMPLES
Use recvmsg() with the msg[] buffers initialized so that the first one receives the IP header, then the second one will only contain data.
I have a created a UDP socket and bind that socket to inaddr_any (0.0.0.0) and and some
well known port number. As per my understanding this socket can receive data over all the interfaces of the machine over the specified port number.
But When i will call send() it will use the default IP address as the source address.
How is the default IP address chosen?
If I want to use some other interface (other than the default) for sending the data, how can this be done?
Context of the problem:
I am implementing LDP protocol. It can have many hello adjacencies. Thus i am creating a server to recv data from the other interfaces of the router. Once the hello adjacency is formed, then hello messages are to sent be on the specific interface over UDP over which the hello adjacency is created.
The default IP address is chosen based on the network the packet is sent to. For example if you have two interfaces, one connected to network A and the other connected to network B, if you send a packet to network B the packet will be sent with the IP address of the second interface. For this reason, most of the time you don't have to worry about it.
If you have two network interfaces connected to the same network, you can bind the socket to the address of one of them, and the packet will go out with that address. For example, this will bind an IP socket to 192.168.122.1, if allowed by the network stack:
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("192.168.122.1");
addr.sin_port = 0;
if (bind(s, (struct sockaddr*) &addr, sizeof addr) == -1) {
perror("bind");
}
I try to do bind a multicast port in my app. Previously the code always worked, but on this server it (often, but not always) fails...
The error message is Address already in use, which I don't quite understand, as it's possible to bind the same address from multiple applications (and even from the same application)...
What could cause this? I know someone would ask for it, so here is the code:
int fd = socket(PF_INET, SOCK_DGRAM, 0);
/* yes, that's a valid socket, verified.... */
u_int val = 1;
if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) {
perror("Reusing ADDR failed");
exit(1);
}
struct sockaddr_in saddr;
saddr.sin_family = PF_INET;
saddr.sin_addr.s_addr = inet_addr(multicast_group_ip);
saddr.sin_port = htons(port);
/* yes, valid multicast ip address and port, verified */
if(bind(fd, (struct sockaddr *) &saddr, sizeof(struct sockaddr_in)) == -1)
//FAILS....
You can actually have multiple datagram sockets bound to the same multicast group and port. However, all of these sockets should set the SO_REUSEADDR option. Based on your code you seem to be doing this, but it sounds like there is another process on your server that has bound a socket to the same multicast group and port without setting that option. The solution would be to ensure that all the processes on this particular server which are binding sockets to that multicast group and port set the SO_REUSEADDR option.
EDIT:
To respond to your recent comments above, one way you can actually reproduce this is to create two simple multicast listening programs that both bind a datagram socket to the same port and group. Have one of the programs set the SO_REUSEADDR option but not the other. Run the program that does not have SO_REUSEADDR set and you should see multicast data coming through. While leaving this program up, run the second program that does have SO_REUSEADDR set and you should see that it will not receive any multicast data while the first program is still receiving it (this should replicate the problem you originally described).
Finally, shut down both programs, then modify the first program to set SO_REUSEADDR, and repeat the steps above. You should now see both programs receiving the multicast traffic.
It's possible to have multiple open sockets on the same host:port from the one process, it's impossible to have more than process to be listening (i.e., bound) to the same host:port.
Perhaps, you have previous instance of your server process unkilled.
Another option mentioned by Wug is that you trying to bind to port lower than 1024 while not being root. Range of low ports 1-1024 is reserved for applications with effective UID == 0, e.g., started by root. seems to be wrong assumption since you'd get different error in such case, not already in use.
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.