Does sendmsg system call validate IPv6 source-address added into ancillary data? - linux

I see following behavior with sendmsg in case of IPv4:
Suppose that 10.1.2.3 is the client IP.
And 10.1.2.10 is configured on one of the interfaces of client.
In an UDP message, following control information is added into the packet:
It is just the source-address or interface address that server should use in replying back to the client:
cmsg->cmsg_len = sizeof(struct cmsghdr) + sizeof(sa->sin_addr);
cmsg->cmsg_level = IPPROTO_IP;
cmsg->cmsg_type = IP_SENDSRCADDR_WITH_ERROR;
* (struct in_addr *)CMSG_DATA(cmsg) = sa->sin_addr;
cmsg = (struct cmsghdr *)((caddr_t) cmsg + ALIGN(cmsg->cmsg_len));
And message is sent as:
sendmsg(fd, send_msg, 0);
If I configure 10.1.2.10 as source-ip and once it is added into cmsg, things work fine.
server replies back to 10.1.2.10.
But, if I configure some un-reachable IP address or IP that is not configured on any interface on the client, sendmsg fails with below error:
sendmsg to 10.1.2.3(10.1.2.3).1813 failed: Can't assign
requested address
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
But I do not see the same behavior with IPv6:
Suppose that 2001::1 is the client IP.
And 2001::2001 is configured on one of the interfaces of client.
IPv6 source address is added into control message as below:
cmsg->cmsg_level = IPPROTO_IPV6;
cmsg->cmsg_type = IPV6_PKTINFO;
cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
memcpy((struct in6_addr *)CMSG_DATA(cmsg), &(sa6->sin6_addr),
sizeof(sa6->sin6_addr));
cmsg = (struct cmsghdr *)((caddr_t) cmsg + ALIGN(cmsg->cmsg_len));
It works fine, if I configure 2001::2001 as source-ip and server does reply back to this address.
But If I configure an unreachable IPv6 source address say 1001::1001, there is no error message from sendmsg similar to the one we see in IPv4 case. Message is still sent with original IPv6 which is 2001::1.
Can someone please suggest on what can be the problem?
Thanks.

IP_SENDSRCADDR and IPV6_PKTINFO must be two different implementations. Maybe in the first case it just control errors. Have you tried to set the interface index in the ancillary data for IPV6_PKTINFO? For IPV6_PKTINFO the ancillary data is of type: in6_pktinfo.
struct in6_pktinfo {
struct in6_addr ipi6_addr; /* src/dst IPv6 address */
unsigned int ipi6_ifindex; /* send/recv if index */
};
Hope this helps in some way

I meet the same issue, I set source address as 408:6666:f:f500::1 (not local IP), but I received the packet with 4085:6666:f:fc10::1 (the local IP) as source address, no matter I set ipi6_ifindex or not.
I will get forward to investigate it.

Related

UDP and socket on responce to client

I got the UDP communication up and running.
In a simple striped down version the client is basically doing this: (Yes, there are a lot of ifs and error checking in the code and No, without a lot of definitions it will not work as is. But I am more on the conseptual here than actually code)
sock = socket(AF_INET, SOCK_DGRAM, 0);
server_length = sizeof(struct sockaddr_in);
sendto(sock, "Hi", 3, 0, (struct sockaddr *)&myaddr, server_length);
numRes = recvfrom(sock, (char *)buff, (int)sizeof(buff), 0, (struct sockaddr *)&myaddr, &server_length);
And the server is basically the same:
sock=socket(AF_INET, SOCK_DGRAM, 0);
bind(sock,(struct sockaddr *)&server,length);
resBytes=recvfrom(sock,buff,(int)sizeof(buff),0,(struct sockaddr *)&from, &fromlen);
sendto(sock, "Test",5, 0, (struct sockaddr *)&from, fromlen);
This works. Both ends receive the data the other end sends.
Here is my problem: The server should be handling e lot of requests at the same time. I made a queue for the messages so that another thread could handle them, before I send them back to the client in yet another thread. When I do this I created a new socket for the sending, used the same "from" address that follows the message, but the client never receive it.
Is it correct to understand that replying on a UDP message has to be done with the same socket?
If I use the same socket for sending and receiving, what happens if I have three clients sending in messages, then after those are processed I will answer them in a random order. Will this work?
I can make a "server" on the client, but my guess is that any NAT would kill that idea fast.
What I try to attempt is more or less this:
sock=socket(AF_INET, SOCK_DGRAM, 0);
sock2=socket(AF_INET, SOCK_DGRAM, 0);
bind(sock,(struct sockaddr *)&server,length);
resBytes=recvfrom(sock,buff,(int)sizeof(buff),0,(struct sockaddr *)&from, &fromlen);
sendto(sock2, "Test",5, 0, (struct sockaddr *)&from, fromlen);
If this kind of code should work, I will go back to my debugging, but right now it seems like this is not the case.
Edit:
Someone added "Multithreading" as a tag. Forget the multithread thing Its more to explain why I don't respond promptebly to the same request. I can have all the communication in one thread if thats whats needed. Sorry if this confuse people, but my main question here is: a) Can I send response down another socked without defining a "server" on the client. b) If not, how will it handle random order of reply on the same socked (IE Client A sends, Client B sends, answer to Client B, then to Client A)
It is possible for a single UDP socket to receive data from multiple sources and to send data to multiple peers, as long as the socket is not connected. This means if you explicitly or implicitly bind the socket and then use sendto and recvfrom (or recv) then you can use the same socket to send and receive data from multiple peers. But, if you first connect the socket then you will only be able to receive data from a socket which is bound to the connected ip:port, i.e. usually the same socket which received the data.
To test this you can use a simply Python server (would work the same with C). This server creates two sockets: sock1 and sock2. It will receive a message on sock1 and then send a message back using first sock2 (the one which did not receive the original message) and then sock1 (the one which received the original message):
from socket import *
sock1 = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
sock2 = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
sock1.bind(('127.0.0.1',9999))
data,addr = sock1.recvfrom(1024)
print("got '{}' from {}".format(data,addr))
sock2.sendto(b"send from sock2", addr)
sock1.sendto(b"send from sock1", addr)
As the peer we have a client which depending on the configuration uses a connected socket (i.e. connect + send) or an unconnected socket (i.e. no connect and sendto):
from socket import *
connected=False
sock = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP)
if connected:
sock.connect(('127.0.0.1',9999))
sock.send('send from connected client')
else:
sock.sendto('send from unconnected client',('127.0.0.1',9999))
while True:
data,addr = sock.recvfrom(1024)
print("got '{}' from {}".format(data,addr))
If you run the client with the connected socket (connected=True) you'll see that it will only get the message from sock1 back, because sock1 is (explicitly) bound to the ip:port the client is connected to and sock2 is (implicitly) bound to some other address:
got 'send from sock1' from ('127.0.0.1', 9999)
If instead the client is using an unconnected socket (connected=False) then it will receive both the messages from sock1 and sock2:
got 'send from sock2' from ('127.0.0.1', 44596)
got 'send from sock1' from ('127.0.0.1', 9999)

UDP Packet forwarding getting EINVAL from sendmsg

I'm writing my own DNS black hole to block advertising and malware on my home network. I realize that this type of program already exists, but I would like to learn the process and write my own. From the Linux documentation, it appears that sendmsg() can be told to use a different return address so that a UDP packet can be forwarded and the receiving server will send the response to the original requester instead of my server. From the sparse documentation, I set up my socket bound to port 53 (DNS). I'm receiving DNS requests, interpreting them and responding when the site is blacklisted. For 'good' domain names, I'm getting 2 different results. On MacOS my forwarding request is sent, but the response comes back to my program instead of the original requester. On Linux (Armbian 4.13 kernel), I get EINVAL from the sendmsg() call and nothing is sent. Can anyone shed some light on what I'm doing wrong? (all error checking has been removed for brevity)
Additional info... a wrinkle in this scheme is that the original DNS request is bound to a socket port other than 53 (obviously). How do I tell sendmsg() to return the response to the correct port number of the original request?
My 'proxy' version of this works. I store the transaction ID and requesting port number of the original request and am able to return the response successfully
Latest news - I was able to successfully accomplish what I wanted, but not with sendmsg(). By using RAW sockets and 'spoofing' the return address in the IP header, I was able to get the packet sent back to a different return address and port number (using sendto). nslookup sees this as a problem, but browsers are okay with it. I guess we can call this 'case closed'
struct sockaddr_in addr, addrfrom, fwaddr;
socklen_t addrLen = sizeof(struct sockaddr_in);
int rc, listen_sock;
listen_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
rc = 1;
setsockopt(listen_sock, IPPROTO_IP, IP_PKTINFO, &rc, sizeof(rc));
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(53); // port address of DNS
addr.sin_addr.s_addr = INADDR_ANY;
bind(listen_sock, (struct sockaddr*)&addr, sizeof(addr));
memset(&fwaddr, 0, sizeof(fwaddr));
fwaddr.sin_family = AF_INET;
fwaddr.sin_port = htons(53);
fwaddr.sin_addr.s_addr = 0x08080808; // Google DNS server
<... Receive DNS request from client...>
if (bForward)
{
struct msghdr msg;
struct iovec iov[1];
struct {
struct cmsghdr cm; /* this ensures alignment */
struct in_pktinfo ipi;
} cmsg;
memset(&cmsg, 0, sizeof(cmsg));
memset(&msg, 0, sizeof(msg));
iov[0].iov_base = request_bufffer;
iov[0].iov_len = request_len;
msg.msg_flags = 0;
msg.msg_name = &fwaddr; // dest address of packet
msg.msg_namelen = addrLen;
msg.msg_iov = &iov[0];
msg.msg_iovlen = 1;
msg.msg_control = &cmsg;
msg.msg_controllen = sizeof(cmsg); //sizeof(struct in_pktinfo);
cmsg.cm.cmsg_len = sizeof(cmsg);
cmsg.cm.cmsg_level = IPPROTO_IP;
cmsg.cm.cmsg_type = IP_PKTINFO;
cmsg.ipi.ipi_ifindex = 0;
cmsg.ipi.ipi_spec_dst = addrfrom.sin_addr; // original source address
rc= sendmsg(listen_sock, &msg, 0);
} // bForward
Issues
I see 2 potential issues in this code.
You are not using the CMSG macros to allocate and manipulate your ancillary data (i.e. your cmsg structure).
Even if that is fixed, you are trying to spoof the source IP address for the sent packet - i.e. you are trying to declare it is from an address not owned by this box. I have never tried to do this but wouldn't be surprised if you find the kernel security prevents you from doing that.
So what to do?
First I would fix up your meassage structures, using code like this SO answer. I suspect you should look closely at the offset of in_pktinfo and the length you're passing in to cmsg_len as these could cause EINVAL errors. A possible strategy might be to cut the code back to the point it's just sending packets normally to see which data structures are being rejected.
If that doesn't do it, I guess you could then consider re-writing your send logic to use raw sockets to bypass any IP validation your kernel might be doing. For example, this code looks like it should be roughly what you need.
If that's still not enough, I suspect you need to route around in the kernel security systems.

GetAddrInfoW to get only the global ipv6 address

I need only my global IPv6 address and not local link address. I have set ai_flags to AI_ADDRCONFIG as mentioned in msdn. But GetAddrInfoW returns both global and local link addresses. Is there any way i can find out from ADDRINFOW result structure the type of address?
std::wstring whostname = L"hostname";
ADDRINFOW hints;
memset(&hints, 0, sizeof(ADDRINFOW));
hints.ai_family = AF_UNSPEC; // IPv4 and IPv6
hints.ai_socktype = SOCK_STREAM; // TCP only, no UDP
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_ADDRCONFIG; // Get only global IPv6 address
ADDRINFOW *list = NULL;
result = GetAddrInfoW(
whostname.c_str(),
NULL,
&hints,
&list
);
I guess there isnt something that the API provides to get only the global IPv6 address. I have managed to identify the global address based on the IPv6 address format. From Wiki, The scope can be used to identify Link-local address.

UDP Based Server Can't Distinguish Different Users

This is a C++ server application which communicates with all clients based on UDP protocol. When a user logs into the server from client, the client application registers a UDP channel to the server and this channel is in fixed format: IP+Port, which means if the IP keeps unchanged, then no matter what user logged in the client registers a same channel.
The server's socket layer maintains a heartbeat mechanism which will remove the channel if it doesn't receive any heartbeat packets from the channel in 3 minutes. Everything works fine until the client is down, e.g. the network wire is plugged off. Look at below scene:
1. User-A logs into server. The Client registers channel (IP:Port)
to the server. Because the UDP channel is alive, so the Server
sets the User status of User-A as Online.
2. Kill the client process, and within 3 minutes(before the channel
timeouts in server), let User-B logs into server from the same
computer. Because the IP remains unchanged, so actually the client
registers a same (IP:PORT) pair to the server as it did when User-A
logs in.
3. Since the Server receives packets from (IP:PORT), so it considers
User-A is still alive, thus setting the user status of User-A as
Online which is not right anymore.
In above scenario, the Server is not able distinguish different users logged from a same computer, which results in wrong user states. Does anybody know how to solve this problem?
I see no reason to presume that the origin port number for any two users will be identical unless the client application is explicitly binding the UDP socket. Clients which initiate the communication can often use ephemeral ports just as effectively. Ephemeral ports may or may not be sufficiently random for your particular use case, code below shows how to access client ports from inbound UDP data. If they are not sufficiently random, it may be wise to encode session cookies or user-cookies into the protocol.
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
int
main() {
/*- setup UDP socket on 8500 */
int rc;
int server_socket;
struct sockaddr_in server_address;
server_socket = socket(AF_INET, SOCK_DGRAM, 0);
if (server_socket < 0) {
perror("failed to init socket");
return 1;
}
memset(&server_address, 0, sizeof(server_address));
server_address.sin_family = AF_INET;
server_address.sin_port = htons(8500);
server_address.sin_addr.s_addr = inet_addr("127.0.0.1");
rc = bind(server_socket, (struct sockaddr*) &server_address
, sizeof(server_address));
if (rc < 0) {
perror("failed to bind");
return 2;
}
/* - receive from UDP socket and print out origin port - */
char buffer[4096];
struct sockaddr_in client_address;
int client_address_len = sizeof(client_address);
rc = recvfrom(server_socket
, buffer
, sizeof(buffer)
, 0
, (struct sockaddr*) &client_address
, &client_address_len);
if (rc < 0)
return 3;
fprintf(stderr, "%s %d\n", buffer, ntohs(client_address.sin_port));
return 0;
}

How to broadcast Video using UDPCLient Class in C# over internet?

I am trying to develop a Video Client/functionality that captures video using webcam and transfers to other servent (server-client) somewhere on the internet. I am using UDPCLient Class to do that.
I want my application to be able to listen and tarnsmit video captured from webcam. The capturing, transmission and receiving works fine when i do that on local network.
But when i test the application from behind router (across two differnt networks/internet) after forwarding respective ports, the internet connectivity is lost on both routers (They hang up or something) and i need to restart the routers or switch to an alternate connection. The configuration is as follows:
Servent 1 <--> Router1 <--> Internet Connection#01
Servent 02 <---> Router2 <---> Internet Connection#02
Both connections are on separate DSL Line. One of the routers is ZTE brand and the other is of Netgear.
Code for listenning/transmission is as follows:
private void StartSockets()
{
//For testing across internet i use IPAddress obtained via different function
var IPAddress = getMyIpAddress();
this.udpSender = new UdpClient(IpAddress, 4000);
this.udpListener = new UdpClient(4000);
}
private IPAddress getMyIpAddress()
{
IPAddress localIP ;//= AddressAr[0];
localIP = IPAddress.Parse(GetPublicIP());
return localIP;
}
public string GetPublicIP()
{
String direction = "";
WebRequest request = WebRequest.Create("http://checkip.dyndns.org/");
using (WebResponse response = request.GetResponse())
{
using (StreamReader stream = new StreamReader(response.GetResponseStream()))
{
direction = stream.ReadToEnd();
}
}
//Search for the ip in the html
int first = direction.IndexOf("Address: ") + 9;
int last = direction.LastIndexOf("</body>");
direction = direction.Substring(first, last - first);
return direction;
}
Code for receiving response is as follows:
private void ReceiveData()
{
//For testing across internet i use IPAddress obtained via different function
var IPAddress = getMyIpAddress();
IPEndPoint ep = new IPEndPoint(IPAddress, myPort);
try
{
byte[] receiveBytes = this.udpListener.Receive(ref ep);
this.OnReadImage(new ImageEventArgs(this.ByteToImage(receiveBytes)));
}
catch (Exception)
{
}
}
If i test on local network , i use DNSHostname to get ip address (private ip addresses) and video works fine on local network. That does not work over internet so i switch to live Ip Address and thus i use the method of getPublicIpAddress().
I know there is something seriously wrong with my approach? What would be right approach?
Should i switch to TCP Listenner? I intend to have multiple receiver of same video in future. So would that affect?
Can UDP clients cause routers to crash, hang up and restart? How can i avoid that?
Lastly, if were to avoid port-forwarding what would be the best strategy?
Please help.
Thanks
Steve

Resources