Use of IN6ADDR_SETV4MAPPED and dual stack sockets - sockets

This is a continuation of Connecting IPv4 client to IPv6 server: connection refused. I am experimenting with dual stack sockets and trying to understand what setsockopt with IPV6_V6ONLY is useful for. On the linked question I was advised that "Setting IPV6_V6ONLY to 0 can be useful if you also bind the server to an IPv6-mapped IPv4 address". I have done this below, and was expecting my server to be able to accept connections from both an IPv6 and an IPv4 client. But shockingly when I run my client with a V4 and a V6 socket, neither can connect!
Can someone please tell me what I am doing wrong, or have I misunderstood IPv6 dual stack functionality all together?
Server:
void ConvertToV4MappedAddressIfNeeded(PSOCKADDR pAddr)
{
// if v4 address, convert to v4 mapped v6 address
if (AF_INET == pAddr->sa_family)
{
IN_ADDR In4addr;
SCOPE_ID scope = INETADDR_SCOPE_ID(pAddr);
USHORT port = INETADDR_PORT(pAddr);
In4addr = *(IN_ADDR*)INETADDR_ADDRESS(pAddr);
ZeroMemory(pAddr, sizeof(SOCKADDR_STORAGE));
IN6ADDR_SETV4MAPPED(
(PSOCKADDR_IN6)pAddr,
&In4addr,
scope,
port
);
}
}
addrinfo* result, hints;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
int nRet = getaddrinfo("powerhouse", "82", &hints, &result);
SOCKET sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
int no = 0;
if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&no, sizeof(no)) != 0)
return -1;
ConvertToV4MappedAddressIfNeeded(result->ai_addr);
if (bind(sock, result->ai_addr, 28/*result->ai_addrlen*/) == SOCKET_ERROR)
return -1;
if (listen(sock, SOMAXCONN) == SOCKET_ERROR)
return -1;
SOCKET sockClient = accept(sock, NULL, NULL);
printf("Got one!\n");
Client:
addrinfo* result, *pCurrent, hints;
char szIPAddress[INET6_ADDRSTRLEN];
memset(&hints, 0, sizeof hints); // Must do this!
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
const char* pszPort = "82";
if (getaddrinfo("powerhouse", "82", &hints, &result) != 0)
return -1;
SOCKET sock = socket(AF_INET, result->ai_socktype, result->ai_protocol);
int nRet = connect(sock, result->ai_addr, result->ai_addrlen);

My C skills are a bit rusty, so here is a counter-example written in Python. My local IPv4 address is 37.77.56.75, so that is what I will bind to. I kept it as simple as possible to focus on the concepts.
This is the server side:
#!/usr/bin/env python
import socket
# We bind to an IPv6 address, which contains an IPv6-mapped-IPv4-address,
# port 5000 and we leave the flowinfo (an ID that identifies a flow, not used
# a lot) and the scope-id (basically the interface, necessary if using
# link-local addresses)
host = '::ffff:37.77.56.75'
port = 5000
flowinfo = 0
scopeid = 0
sockaddr = (host, port, flowinfo, scopeid)
# Create an IPv6 socket, set IPV6_V6ONLY=0 and bind to the mapped address
sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, 0)
sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
sock.bind(sockaddr)
# Listen and accept a connection
sock.listen(0)
conn = sock.accept()
# Print the remote address
print conn[1]
Here we bind to an IPv6 address in the code, but the address is actually an IPv6-mapped IPv4 address, so in reality we are binding to an IPv4 address. This can be seen when looking at i.e. netstat:
$ netstat -an | fgrep 5000
tcp4 0 0 37.77.56.75.5000 *.* LISTEN
We can then use an IPv4 client to connect to this server:
#!/usr/bin/env python
import socket
# Connect to an IPv4 address on port 5000
host = '37.77.56.75'
port = 5000
sockaddr = (host, port)
# Create an IPv4 socket and connect
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
conn = sock.connect(sockaddr)
And the server will show us who connected, using IPv6 address representation:
('::ffff:37.77.56.76', 50887, 0, 0)
In this example I connected from IPv4 host 37.77.56.76, and it choose port 50887 to connect from.
In this example we are only listening on an IPv4 address (using IPv6 sockets, but it is still an IPv4 address) so IPv6-only clients will not be able to connect. A client with both IPv4 and IPv6 could of course use IPv6 sockets with IPv6-mapped-IPv4-addresses, but then it would not really be using IPv6, just an IPv6 representation of an IPv4 connection.
A dual-stack server has to either:
listen on the wildcard address, which will make the OS accept connections on any address (both IPv4 and IPv6)
listen on both an IPv6 address and an IPv4 address (either by creating an IPv4 socket, or by creating an IPv6 socket and listening to an IPv6-mapped-IPv4-address as shown above)
Using the wildcard address is the most simple. Just use the server example from above and replace the hostname:
# We bind to the wildcard IPv6 address, which will make the OS listen on both
# IPv4 and IPv6
host = '::'
port = 5000
flowinfo = 0
scopeid = 0
sockaddr = (host, port, flowinfo, scopeid)
My Mac OS X box shows this as:
$ netstat -an | fgrep 5000
tcp46 0 0 *.5000 *.* LISTEN
Notice the tcp46 which indicates that it listens on both address families. Unfortunately on Linux it only shows tcp6, even when listening on both families.
Now for the most complicated example: listening on multiple sockets.
#!/usr/bin/env python
import select
import socket
# We bind to an IPv6 address, which contains an IPv6-mapped-IPv4-address
sockaddr1 = ('::ffff:37.77.56.75', 5001, 0, 0)
sock1 = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, 0)
sock1.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
sock1.bind(sockaddr1)
sock1.listen(0)
# And we bind to a real IPv6 address
sockaddr2 = ('2a00:8640:1::224:36ff:feef:1d89', 5001, 0, 0)
sock2 = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, 0)
sock2.bind(sockaddr2)
sock2.listen(0)
# Select sockets that become active
sockets = [sock1, sock2]
readable, writable, exceptional = select.select(sockets, [], sockets)
for sock in readable:
# Accept the connection
conn = sock.accept()
# Print the remote address
print conn[1]
When running this example both sockets are visible:
$ netstat -an | fgrep 5000
tcp6 0 0 2a00:8640:1::224.5000 *.* LISTEN
tcp4 0 0 37.77.56.75.5000 *.* LISTEN
And now IPv6-only clients can connect to 2a00:8640:1::224:36ff:feef:1d89 and IPv4-only clients can connect to 37.77.56.75. Dual stack clients can choose which protocol they want to use.

Related

Raw socket: unexpected IP header added when sending self-made IP/TCP packets

I want to use raw socket to send TCP packets which is a full IP packet(so the packet has IP header, TCP header and TCP payload, but has no ethernet header. The IP source and destination addresses are in a WLAN, 192.168.0.105 and 192.168.0.103), with the following codes
int on;
on = 0;
if ((sendfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
perror("raw socket");
exit(1);
}
if (setsockopt(sendfd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0) {
perror("setsockopt");
exit(1);
}
nr_bytes = sendto(sendfd, packet, ip_len, 0, (struct sockaddr*)&client_addr, addr_len);
I use TCPdump to capture the sent-out packet and notice it has added an additional IP header to the IP packet, and the IP protocol number is 255(ip->ip_p is 255). So it has two IP headers(with same pair of src and dst IP), which is unexpected.
what are the problems? thank you!
Using IPPROTO_RAW implicitly enables the option IP_HDRINCL.
In your call to setsockopt() you disable IP_HDRINCL again because you set on = 0.
Try removing the setsockopt() or setting on = 1.

Destination Unreachable (Port Unreachable) using Haskell

Using Wireshark to debug, I receive the following error when sending UDP packets on localhost:
Destination Unreachable (Port Unreachable)
Checksum: 0x0000 (Illegal)
I am constructing my server first on a port between 10000 - 15000 using
startServer :: Port -> IO Server
startServer port = withSocketsDo $ do
-- Look up the server address and port information.
addrs <- getAddrInfo (Just $ defaultHints { addrFlags = [AI_PASSIVE] }) Nothing (Just port)
let serverAddress = head addrs
-- Bind to the socket.
sock <- socket (addrFamily serverAddress) Datagram defaultProtocol
bindSocket sock (addrAddress serverAddress)
-- Create the server and run the client send and receive threads.
clients <- newMVar $ createEmptyClients
let server = Server sock port clients
_ <- forkIO $ forever $ receiveClientJoin server
return server
I am listening for new clients connecting via UDP using
-- | Connected a client to the server.
receiveClientJoin :: Server -> IO ()
receiveClientJoin server = do
print "Receiving"
(msg, _, clSockAddr) <- recvFrom (sSocket server) 4096
print $ "Server received client join message: " ++ msg
And I am connecting to the server with clients using
connectToServer port = do
-- Get the server's address and port information.
addrInfo <- getAddrInfo Nothing (Just "localhost") (Just port)
let serverAddr = head addrInfo
sock <- socket (addrFamily serverAddr) Datagram defaultProtocol
sendTo sock "Hello from this client!" (addrAddress serverAddr)
Why are my clients' packets not finding the server?
The problem is you are listening on an IPv6 address and trying to connect to an IPv4 address. This is actually a slightly common problem. For example, I ran across this issue when working with commsec.
Consider the fragments where you discover your AddrInfo:
import Network.Socket
main :: IO ()
main = do
let port = "2474"
addrs <- getAddrInfo (Just $ defaultHints { addrFlags = [AI_PASSIVE] }) Nothing (Just port)
let serverAddress = head addrs
print serverAddress
addrInfo <- getAddrInfo Nothing (Just "localhost") (Just port)
let serverAddr = head addrInfo
print serverAddr
Now the output will vary by machine, but on one of my CentOS systems with both IPv4 and IPv6 addresses the output clearly shows the second (connect) address is IPv6 while the first (listen) address is IPv4:
AddrInfo {addrFlags = [AI_PASSIVE], addrFamily = AF_INET, addrSocketType = Stream, addrProtocol = 6, addrAddress = 0.0.0.0:2474, addrCanonName = Nothing}
AddrInfo {addrFlags = [AI_ADDRCONFIG,AI_V4MAPPED], addrFamily = AF_INET6, addrSocketType = Stream, addrProtocol = 6, addrAddress = [::1]:2474, addrCanonName = Nothing}
One solution is to force a particular version of IP via a hint or an address (ex. an IPv4 address as in my comment). The hint solution is probably more desirable:
-- For servers:
addrs <- getAddrInfo (Just defaultHints { addrFamily = AF_INET6
, addrFlags = [AI_PASSIVE] })
Nothing (Just port)
-- For clients:
addrInfo <- getAddrInfo (Just defaultHints { addrFamily = AF_INET6 })
(Just "localhost") (Just port)

Is it possible to use the same port and ip address?

I created a TCP server program which binds, listen and accepting a connection from the specific ip address and port number.
During the first connection : Server is accepting a SYN packet from the client and sending an ACK back to the client. Later getting a ACK from the client. Finally Client is RST with the server.
During the second connection the client is sending a SYN packet to the slave but there is no ACK from the server.
I think there is no binding is possible during the second connection with the same ip address and port number.
Is it possible to bind with the SAME ip address and port number in the second connection ?
server :
SOCKET sock;
SOCKET fd;
uint16 port = 52428;
// I am also using non blocking mode
void CreateSocket()
{
struct sockaddr_in server, client; // creating a socket address structure: structure contains ip address and port number
WORD wVersionRequested;
WSADATA wsaData;
int len;
int iResult;
u_long iMode = 1;
printf("Initializing Winsock\n");
wVersionRequested = MAKEWORD (1, 1);
iResult = WSAStartup (wVersionRequested, &wsaData);
if (iResult != NO_ERROR)
printf("Error at WSAStartup()\n");
// create socket
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock < 0) {
printf("Could not Create Socket\n");
//return 0;
}
printf("Socket Created\n");
iResult = ioctlsocket(sock, FIONBIO, &iMode);
if (iResult < 0)
printf("\n ioctl failed \n");
// create socket address of the server
memset( &server, 0, sizeof(server));
// IPv4 - connection
server.sin_family = AF_INET;
// accept connections from any ip adress
server.sin_addr.s_addr = htonl(INADDR_ANY);
// set port
server.sin_port = htons(52428);
//Binding between the socket and ip address
if(bind (sock, (struct sockaddr *)&server, sizeof(server)) < 0)
{
printf("Bind failed with error code: %d", WSAGetLastError());
}
//Listen to incoming connections
if(listen(sock, 10) == -1){
printf("Listen failed with error code: %d", WSAGetLastError());
}
printf("Server has been successfully set up - Waiting for incoming connections");
for(;;){
len = sizeof(client);
fd = accept(sock, (struct sockaddr*) &client, &len);
if (fd < 0){
printf("Accept failed");
closesocket(sock);
}
//echo(fd);
printf("\n Process incoming connection from (%s , %d)", inet_ntoa(client.sin_addr),ntohs(client.sin_port));
//closesocket(fd);
}
}
TCP connections are identified by four parameters:
Local IP
Local port
Remote IP
Remote port
The server normally uses the same Local IP and port for all its connections (e.g. an HTTP server listens on port 80 for all connection). Each connection from a client will have a different Remote IP and/or Remote port, and these resolve the ambiguity.
When the server closes all of its connected sockets, the TCB sticks around for several minutes in a TIME_WAIT state. This normally prevents a process from binding to the port, because you can't bind to a local IP/port that has any associated TCBs. If you want to restart the server and bind to the same port and address that it just used for connections, you need to use the SO_REUSEADDR socket option to get around this. See:
Socket options SO_REUSEADDR and SO_REUSEPORT, how do they differ? Do they mean the same across all major operating systems?
for details of this.

How to force client in UDP to open port when sending with sendto

I have simple server and client in UDP (WinSocks/C++).
I send datagram client -> server via sendto, and reply from server to client using the ip and port obtained from recvfrom function.
I found out that:
Every sendto from client is being sent from different port
When trying to reply from server Windows returns WSAECONNRESET (which mean that port is closed - http://support.microsoft.com/kb/263823)
How can I properly answer client from server (ie force port binding on client when sending using sendto?)
Edit: Adding some source code:
bool InitClient()
{
internal->sock = socket(PF_INET, SOCK_DGRAM, 0);
char8 yes = 1;
setsockopt(internal->sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int32));
return internal->sock != -1;
}
void Send(const IpAddress & target, const uint16 port, const char8 * data, int32 size )
{
sockaddr_in trgt;
memset(&trgt, 0, sizeof(trgt));
trgt.sin_family = AF_INET;
trgt.sin_port = htons(port);
trgt.sin_addr.s_addr = target.GetRaw();
if(sendto(internal->sock, (const char8 *)data, size, 0, (PSOCKADDR)&trgt, sizeof(trgt)) == SOCKET_ERROR)
{
LOG("Network sending error: %d", WSAGetLastError());
}
}
Call the "bind" function to specify a local port to send from. Example of using port 4567 below. Make sure to check the return value from bind.Call this code after you create the socket.
sockaddr_in local = {};
local.family = AF_INET;
local.port = htons(4567);
local.addr = INADDR_ANY;
bind(internal->sock,(sockaddr*)&local, sizeof(local));
If you bind to port zero instead of 4567 then the os will pick a random port for you and use it for all subsequent send and receives. You can call getsockname to discover which port the os picked for you after calling bind.

Solaris - My process listening on all the interfaces but other processes listening on localhost:6011

My server program is designed to listen on all the interfaces and it is listening working also.
One strange behavior is happening. It means port conflict with sshd process. Because I need to make my application and sshd to listen on same port range.(ex: 6000 - 6100)
MyProg code:
MyPort = 6011;
#if TARGET_HAS_IPV6_SUPPORT
/* IPv6 socket */
{
struct addrinfo *ai = NULL;
r = ipw_getaddrinfo("::", NULL, AF_INET6, SOCK_STREAM, IPPROTO_TCP, AI_PASSIVE, &ai);
if (r == 0)
{
Create_listen_socket(&ai, IpcMyPort);
}
}
#endif
{
struct addrinfo ailocal = { 0 };
struct sockaddr_in sin = { 0 };
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_ANY);
ailocal.ai_family = AF_INET;
ailocal.ai_socktype = SOCK_STREAM;
ailocal.ai_protocol = IPPROTO_TCP;
ailocal.ai_flags = AI_PASSIVE;
ailocal.ai_addrlen = sizeof(struct sockaddr_in);
ailocal.ai_addr = (struct sockaddr *)&sin;
Create_listen_socket(&ailocal, MyPort);
}
Create_listen_socket(struct addrinfo *ai,
unsigned int MyPort)
{
SOCKET fd = INVALID_SOCKET;
while (ai != NULL)
{
fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (fd == INVALID_SOCKET)
{
Exit()
break;
}
switch (switch (sa->sa_family)
{
case AF_INET:
{
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
sin->sin_port = htons((unsigned short)port);
}
break;
#if TARGET_HAS_IPV6_SUPPORT
case AF_INET6:
{
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
sin6->sin6_port = htons((unsigned short)port);
}
break;
#endif
}
if (ipw_bind(fd, ai->ai_addr, ai->ai_addrlen) < 0)
{
exit();
Break;
}
else
{
r = ipw_listen(fd, IPC_MAX_LISTEN_COUNT);
}
ai = ai->ai_next;
}
}
Following is the output of "sudo lsof -i -P | grep :60"
:: :: ::
sshd 23038 fin22495 11u IPv4 0x60026f7c740 0t0 TCP
localhost:6011 (LISTEN)
myProg 23108 root 4u IPv6 0x60026e46000 0t0 TCP *:6011
(LISTEN)
myProg 23108 root 5u IPv4 0x6002658e3c0 0t0 TCP *:6011
(LISTEN)
If you see, the sshd daemon is listening on same port 6011 but on local host interface. but my program is listening on *.6011.
How to make sure myProg listening on "localhost:6011" along with *:6011 and if already some process is listening on "localhost:6011" how to detect it. It happens in Solaris.
Please let me know your views.
Thanks,
Naga
I'm not sure what you have done here because sshd should normally be listening on port 22, not 6011. The first process opening the port will get exclusive access. There are good, security-related reasons for this being so. See the tcp(7P) manpage for details - look for SO_REUSEADDR and SO_EXECLBIND.
If you see, the sshd daemon is listening on same port 6011 but on local host interface. but my program is listening on *.6011.
How to make sure myProg listening on "localhost:6011" along with *:6011 and if already some process is listening on "localhost:6011" how to detect it. It happens in Solaris.
Your application's port numbers conflict with ports used by ssh X-forwarding. Change your port numbers and be done with that.