I have a Linux system with 2 NICs, one of which (eth0) is the default gateway. The other (eth1) is not a default gateway. Both have network connections with internet connectivity - so the link state is up.
I want to periodically check on eth1 to see if Internet traffic can be routed through it. Whilst I am checking eth1, I need to keep my Internet traffic routing through eth0 uninterrupted. If I can make a connection through eth1, I will then switch my default gateway (as that is my cheaper connection).
I am trying to check the connection by opening a socket, binding it to eth1 and connecting to a port. I have used 8.8.8.8:443 for my example.
bool canConnect(const string interface)
{
bool result = false;
sockaddr_in host;
timeval timeout;
int sock = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
if (sock == -1)
return result;
struct ifreq ifr;
::strncpy(ifr.ifr_name, interface.c_str(), IFNAMSIZ);
int res = setsockopt(_sock, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr));
if (res < 0)
{
::close(sock);
return result;
}
int reuse = 1;
if (::setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int)) < 0)
{
::close(sock);
return result;
}
::inet_pton(AF_INET, "8.8.8.8", host.sin_addr);
host.sin_family = AF_INET;
host.sin_port = htons(443);
if (::connect(sock, (struct sockaddr *)&host , sizeof(host)) < 0)
{
int error = errno;
if (error != EINPROGRESS) // socket is non-blocking
{
::close(sock);
return result;
}
}
fd_set writeSet;
fd_set readSet;
FD_ZERO(&readSet);
FD_ZERO(&writeSet);
FD_SET(sock, &readSet);
FD_SET(sock, &writeSet);
timeout.tv_sec = 5;
timeout.tv_usec = 0;
int res = ::select(sock + 1, &readSet, &writeSet, NULL, &timeout);
if (res < 0)
{
::close(sock);
return result;
}
else if (res == 0)
{
// function always exits here
::close(sock);
return result;
}
else if (FD_ISSET(sock, &writeSet) || FD_ISSET(sock, &readSet))
{
int val;
socklen_t len = sizeof(val);
::getsockopt(sock, SOL_SOCKET, SO_ERROR, &val, &len);
if (val == 0)
result = true;
}
::close(sock);
return result;
}
When I run the code, select() always times out. I never manage to make a connection to 8.8.8.8:443, even though eth1 has Internet connectivity (I have proven this by switching the default gateway to eth1 temporarily.
Am I missing some code, or do I need kernel options to make this work?
I have IP forwarding turn on.
If connect() is successful and waiting, you should be checking select() for writable only, not readable at all. This is stated in the connect() mangage:
EINPROGRESS
The socket is nonblocking and the connection cannot be
completed immediately. It is possible to select(2) or poll(2)
for completion by selecting the socket for writing. After
select(2) indicates writability, use getsockopt(2) to read the
SO_ERROR option at level SOL_SOCKET to determine whether
connect() completed successfully (SO_ERROR is zero) or
unsuccessfully (SO_ERROR is one of the usual error codes
listed here, explaining the reason for the failure).
Besides, a socket is readable only if there is inbound data to be read, but you are connecting to an HTTPS port, so there will never be any data to read in your example, as you are not sending an HTTPS handshake request.
Also, you should not be calling select() unless connect() actually fails with EINPROGRESS. If connect() returns 0, the connection was successful right way without blocking. So, you should move your select() call inside your if (connect < 0) block after checking errno.
Also, you are not passing the correct socket to setsockopt(SO_BINDTODEVICE).
Also, you are not passing the correct value to the dst parameter of inet_pton(). You are passing it an in_addr, but it expects an in_addr* instead.
Also, there is no need to use SO_REUSEADDR in this example, since you are not bind()'ing to a specific local IP/port pair before calling connect().
Try something more like this:
bool canConnect(const string &interface)
{
bool result = false;
int sock = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
if (sock == -1)
return result;
struct ifreq ifr;
::strncpy(ifr.ifr_name, interface.c_str(), IFNAMSIZ);
int res = setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr));
if (res < 0)
{
::close(sock);
return result;
}
sockaddr_in host;
::inet_pton(AF_INET, "8.8.8.8", &(host.sin_addr));
host.sin_family = AF_INET;
host.sin_port = htons(443);
result = (::connect(sock, (struct sockaddr *)&host, sizeof(host)) == 0);
if (!result)
{
if (errno != EINPROGRESS) // socket is non-blocking
{
::close(sock);
return result;
}
fd_set writeSet;
timeval timeout;
FD_ZERO(&writeSet);
FD_SET(sock, &writeSet);
timeout.tv_sec = 5;
timeout.tv_usec = 0;
res = ::select(sock + 1, NULL, &writeSet, NULL, &timeout);
if (res <= 0)
{
::close(sock);
return result;
}
int val;
socklen_t len = sizeof(val);
::getsockopt(sock, SOL_SOCKET, SO_ERROR, &val, &len);
result = (val == 0);
}
::close(sock);
return result;
}
That being said, 8.8.8.8 is a DNS server, so why would you think it is running an HTTPS server as well? Make sure you are connecting to an actual HTTPS server (why HTTPS and not HTTP?). You might consider using getaddrinfo() instead to get the IP address for www.google.com (or any other commonly used webserver) instead of hard-coding 8.8.8.8.
Related
I am implementing a client-server TCP socket application. Client is on an OpenWRT Linux router (C based) and writes some data on the socket repeatedly and in a loop at some frequency rate. The Server is on a Linux Ubuntu machine (C/C++ based) and reads data in a loop according to data arrival speed.
Problem: Running the Server and then Client, server keeps reading new data. Both sides work well until the number of data deliveries (# of connections) reaches 1013. After that, the Client stuck at socket(AF_INET,SOCK_STREAM,0) with socket creation failed...: Too many open files. Apparently, the number of open fd approaches ulimit -n = 1024 on client.
I put the snippets of the code which shows the loop structures for Server.cpp and Client.c:
Server.c:
// TCP Socket creation stuff over here (work as they should):
// int sock_ = socket() / bind() / listen()
while (1)
{
socklen_t sizeOfserv_addr = sizeof(serv_addr_);
fd_set set;
struct timeval timeout;
int connfd_;
FD_ZERO(&set);
FD_SET(sock_, &set);
timeout.tv_sec = 10;
timeout.tv_usec = 0;
int rv_ = select(sock_ + 1, &set, NULL, NULL, &timeout);
if(rv_ == -1){
perror("select");
return 1;
}
else if(rv_ == 0){
printf("Client disconnected.."); /* a timeout occured */
close (connfd_);
close (sock_);
}
else{
connfd_ = accept (sock_,(struct sockaddr*)&serv_addr_,(socklen_t*)&sizeOfserv_addr);
if (connfd_ >= 0) {
int ret = read (connfd_, &payload, sizeof(payload)); /* some payload */
if (ret > 0)
printf("Received %d bytes !\n", ret);
close (connfd_); /* Keep parent socket open (sock_) */
}else{
printf("Server acccept failed..\n");
close (connfd_);
close (stcp.sock_);
return 0;
}
}
}
Client.cpp:
while (payload_exist) /* assuming payload_exist is true */
{
struct sockaddr_in servaddr;
int sock;
if (sock = socket(AF_INET, SOCK_STREAM, 0) == -1)
perror("socket creation failed...\n");
int one = 1;
int idletime = 2;
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one));
setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &idletime, sizeof(idletime));
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = inet_addr("192.168.100.12");
servaddr.sin_port = htons(PORT); /* some PORT */
if (connect (sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) != 0){
perror("connect failed...");
return 1;
}
write(sock, (struct sockaddr*)&payload, sizeof(payload)); /* some new payload */
shutdown(sock,SHUT_WR);
bool serverOff = false;
while (!serverOff){
if(read(sock, &res, sizeof(res)) < 0){
serverOff = true;
close(sock);
}
}
}
NOTE: payload is 800 bytes and always gets fully transmitted per one write action. Having both codes defined under int main(), the client keeps creating sockets and sending data, on the other side, server receives all and would automatically close() and leave if client terminates, due to using select(). If I don't terminate the Client, however, by checking some print logs, it is evident that Server successfully receives 1013 payloads before client crashes with socket creation failed...: Too many open files.
Update:
Following the point mentioned by Steffen Ullrich, it turned out that, the client socket fd has no leak, and the existence of a second fd in the original loop (which was left open) was making the ulimit exceed the limit.
if(read(sock, &res, sizeof(res)) < 0){
serverOff = true;
close(sock); /********* Not actually closing sock *********/
}
Your check for end of connection is wrong.
read returns 0 if the other side has shut down the connection and <0 only on error.
if (sock = socket(AF_INET, SOCK_STREAM, 0) == -1)
perror("socket creation failed...\n");
Given the precedence of operators in C this basically says:
sock = ( socket(AF_INET, SOCK_STREAM, 0) == -1) )
if (sock) ...
Assuming that socket(...) will not return an error but a file descriptor (i.e. >=0) the comparison will be false and thus this essentially says sock = 0 while leaking a file descriptor if the fd returned by socket was >0.
I'm writing a TCP communication script in c++ to communicate between my computer and an Aldebaran Nao robot.
In general my script is working. However, the trouble I am having is that when I call connect from the client (when the server application is closed or the ethernet connection removed) I get an error that the operation is in progress.
However, once the server application is restarted / ethernet cable reconnected, I still cannot call connect to successfully reestablish a connection. I still get an error that the operation is in progress.
As a note, whenever my client determines that a connection cannot be made, the socket descriptor is closed before reattempting a connection. Here is my code for connecting on the client side:
If there is any more information that would be useful, I would be happy to provide it. This project is relatively large, so I didn't want to include too much irrelevant information here.
TCPStream* TCPConnector::connect(const char* serverIP, int port, int timeoutSec)
{
if (timeoutSec == 0)
{
return connect(serverIP, port);
}
struct sockaddr_in address;
// Store all zeros for address struct.
memset(&address, 0, sizeof(address));
// Configure address struct.
address.sin_family = AF_INET;
address.sin_port = htons(port); // Convert from host to TCP network byte order.
inet_pton(PF_INET, serverIP, &(address.sin_addr)); // Convert IP address to network byte order.
// Create a socket. The socket signature is as follows: socket(int domain, int type, int protocol)
int sd = socket(AF_INET, SOCK_STREAM, 0);
int optval = 1;
if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval) == -1)
{
std::cout << "failed to set socket option" << std::endl;
}
// Set socket to be non-blocking.
int arg;
arg = fcntl(sd, F_GETFL, NULL);
arg |= O_NONBLOCK;
fcntl(sd, F_SETFL, arg);
// Connect with time limit.
fd_set set;
FD_ZERO(&set); // Clear the set.
FD_SET(sd, &set); // Add our file descriptor to the set.
struct timeval timeout;
timeout.tv_sec = timeoutSec;
timeout.tv_usec = 0;
// If the connect call returns 0, then the connection was established. Otherwise,
// check if the three-way handshake is underway.
if (::connect(sd, (struct sockaddr *)&address, sizeof(address)) < 0)
{
// If the handshake is underway.
if (errno == EINPROGRESS)
{
std::cout << "handshake in progress" << std::endl;
// Designate timeout period.
int ret = select(sd + 1, NULL, &set, NULL, &timeout);
std::cout << "return value from select : " << ret << std::endl;
// Check if timeout or an error occurred.
if (ret <= 0)
{
return NULL;
}
else
{
// Check if select returned 1 due to an error.
int valopt;
socklen_t len = sizeof(int);
getsockopt(sd, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &len);
if (valopt)
{
char * errorMessage = strerror( errno); // get string message from errn
std::string msg (errorMessage);
std::cout << msg << std::endl;
return NULL;
}
}
}
else
{
return NULL;
}
}
// Return socket to blocking mode
arg = fcntl(sd, F_GETFL, NULL);
arg &= (~O_NONBLOCK);
fcntl(sd, F_SETFL, arg);
// Create stream object.
return new TCPStream(sd, &address);
}
Your socket is non-blocking mode (you do it explicitly).
As a result, your connect will return immediately with 'connection is in progress'. When socket is non-blocking, you would need than to poll on this socket and wait for it to become readable and/or writeable - this would mean connection is completed (either successfully or not).
A better option in my view would be to use blocking sockets - I see no reason for you to use non-blocking call here.
I have two peers. Peer A(MAC OSX) has two sockets bound on same port. One operating on active mode and one on passive mode. Peer B(any OS) has only one socket acting on Active mode. Peer A starts listening in a separate thread. And after that calls Connect from a different thread. But Peer B is not listening so the connect call of A is failed. Then Peer B call Connect but this connect also fails.
In wireshark I see the SYN leaving B and entering on A's PC. But Accept API call of A doesn't return valid FD which means A is not being able to process the SYN. So no SYN-ACK is also leaving A's PC.
If peer A is any OS (e.g windows,ubuntu, centos) other than MAC than this issue is not reproduced.
EDIT:
While creating the socket we set some socket options. They are O_NONBLOCK, TCP_NODELAY, SO_REUSEADDR, SO_LINGER and for OSX, iOS SO_NOSIGPIPE.
CODE:
This is the listening part of A.
int iAcceptResult = -1;
if (::listen(currentTcpSocket,1)< 0)
{
Log("Listen error: " + itoa(LastNetworkError());
}
unsigned int i = 0;
while ((iAcceptResult < 0 )
{
sleep(80);
iAcceptResult = Accept(currentTcpSocket);
if (0 == iAcceptResult)
{
//success
break;
}
}
This is connecting part for both A and B.
int iConnect = Connect(currentTcpSocket, uAddr, uPort);
if (iConnect < 0)
{
return false;
}
int connectTimeout = 750; //milliseconds
int nWaitResult = UseSelect(currentTcpSocket,connectTimeout);
if (nWaitResult != 0)
{
// timeout or error
}
Connect , Accept and UseSelect functions
int Connect(SOCKET sock, const IpAddress& uAddr, u_int16_t uPort)
{
struct sockaddr_in sin;
memset((char *)&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = uPort;
sin.sin_addr = uAddr.addr4;
return ::connect(sock, (struct sockaddr *)&sin, sizeof(sin));
}
int Accept(SOCKET m_sock)
{
sock_accept = ::accept(m_sock, NULL, NULL);
if (INVALID_SOCKET == sock_accept)
return -1;
return 0;
}
int UseSelect(SOCKET fd, int iTimeoutMs)
{
fd_set readEvents;
fd_set writeEvents;
fd_set exceptEvents;
FD_ZERO(&writeEvents);
FD_ZERO(&readEvents);
FD_ZERO(&exceptEvents);
FD_SET(fd, &exceptEvents);
if(true == read)
{
FD_SET(fd, &readEvents);
}
else
{
FD_SET(fd, &writeEvents);
}
timeval timeout;
timeout.tv_sec = iTimeoutMs / 1000;
timeout.tv_usec = (iTimeoutMs % 1000) * 1000;
return ::select(fd + 1, &readEvents, NULL, &exceptEvents, &timeout);
}
I am trying to enumerate local SQL instances using SQLBrowseConnect. Generally speaking, this is working fine, but we have one set up which results in an SQLExpress instance not being discovered. Here is the code in question:
SQLSetConnectAttr(hSQLHdbc,
SQL_COPT_SS_BROWSE_SERVER,
_T("(local)"),
SQL_NTS);
CString inputParam = _T("Driver={SQL Server}");
SQLBrowseConnect(hSQLHdbc,
inputParam,
SQL_NTS,
szConnStrOut,
MAX_RET_LENGTH,
&sConnStrOut);
In the failed instance, the code is running on a domain controller. The missing local instance of SQL is an SQLExpress instance (version 9). However, the puzzling thing is that running sqlcmd -L shows the missing instance without any problems.
Am I missing something really silly? Please remember that on other systems and set ups there is no issue.
After much investigation, I couldn't really find out what the problem was specifically. This one machine just would not discover its own instances of SQL using SQLBrowseConnect. I therefore decided to write my own version. Discovering SQL instances turns out to be pretty easy. You just send a broadcast UDP packet to port 1434 containing the payload 0x02 (1 byte) and wait for SQL servers to respond. They respond with one packet per server which details all the instances on that machine. The code required to do this is shown below:
// to enumerate sql instances we simple send 0x02 as a broadcast to port 1434.
// Any SQL servers will then respond with a packet containing all the information
// about installed instances. In this case we only send to the loopback address
// initialise
WSADATA WsaData;
WSAStartup( MAKEWORD(2,2), &WsaData );
SOCKET udpSocket;
struct sockaddr_in serverAddress;
if ((udpSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
{
return;
}
// set up the address
serverAddress.sin_family = AF_INET;
serverAddress.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
serverAddress.sin_port = htons(1434);
// the payload
char payload = 0x02;
// config the port for broadcast (not totally necessary right now but maybe in the future)
BOOL broadcast = TRUE;
setsockopt(udpSocket, SOL_SOCKET, SO_BROADCAST, reinterpret_cast<const char*>(&broadcast), sizeof(BOOL));
// receive address info
sockaddr_in RecvAddr;
RecvAddr.sin_family = AF_INET;
RecvAddr.sin_addr.s_addr = htonl(INADDR_ANY);
sockaddr_in SenderAddr;
int SenderAddrSize = sizeof (SenderAddr);
// bind the socket to the receive address info
int iResult = bind(udpSocket, (SOCKADDR *) & RecvAddr, sizeof (RecvAddr));
if (iResult != 0)
{
int a = WSAGetLastError();
return;
}
if (sendto(udpSocket, &payload, 1, 0, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) < 0)
{
int a = WSAGetLastError();
return;
}
// set up a select so that if we don't get a timely response we just bomb out.
fd_set fds ;
int n ;
struct timeval tv ;
// Set up the file descriptor set.
FD_ZERO(&fds) ;
FD_SET(udpSocket, &fds) ;
// Set up the struct timeval for the timeout.
tv.tv_sec = 5 ;
tv.tv_usec = 0 ;
// Wait until timeout or data received.
n = select ( (int)udpSocket, &fds, NULL, NULL, &tv ) ;
if ( n == 0)
{
// timeout
return;
}
else if( n == -1 )
{
// error
return;
}
// receive buffer
char RecvBuf[1024];
int BufLen = 1024;
memset(RecvBuf, 0, BufLen);
iResult = recvfrom(udpSocket,
RecvBuf,
BufLen,
0,
(SOCKADDR *) & SenderAddr,
&SenderAddrSize);
if (iResult == SOCKET_ERROR)
{
int a = WSAGetLastError();
return;
}
// we have received some data. However we need to parse it to get the info we require
if (iResult > 0)
{
// parse the string as required here. However, note that in my tests, I noticed
// that the first 3 bytes always seem to be junk values and will mess with string
// manipulation functions if not removed. Perhaps this is why SQLBrowseConnect
// was having problems for me???
}
I'm looking for an example of how to use libssh2 to setup ssh port forwarding. I've looked at the API, but there is very little in the way of documentation in the area of port forwarding.
For instance, when using PuTTY's plink there is the remote port to listen on, but also the local port that traffic should be sent to. Is it the developers responsibility to set this up? Can someone give an example of how to do this?
Also, an example where remote port is brought to a local port would be useful. Do I use libssh2_channel_direct_tcpip_ex()?
I'm willing to put up a bounty if need be to get a couple of working examples of this.
The key to making libssh2 port forwarding work was discovering that it basically just gives you the data that came in to that port. You have to actually send the data onto a local port that you open:
(Note, this code is not yet complete, there is no error checking, and the thread yielding isn't correct, but it gives a general outline of how to accomplish this.)
void reverse_port_forward(CMainDlg* dlg, addrinfo * hubaddr, std::string username, std::string password, int port)
{
int iretval;
unsigned long mode = 1;
int last_socket_err = 0;
int other_port = 0;
fd_set read_set, write_set;
SOCKET sshsock = socket(AF_INET, SOCK_STREAM, 0);
iretval = connect(sshsock, hubaddr->ai_addr, hubaddr->ai_addrlen);
if (iretval != 0)
::PostQuitMessage(0);
LIBSSH2_SESSION * session = NULL;
session = libssh2_session_init();
iretval = libssh2_session_startup(session, sshsock);
if (iretval)
::PostQuitMessage(0);
iretval = libssh2_userauth_password(session, username.c_str(), password.c_str());
dlg->m_track_status(dlg, 1, 0, "Authorized");
LIBSSH2_LISTENER* listener = NULL;
listener = libssh2_channel_forward_listen_ex(session, "127.0.0.1", port, &other_port, 1);
if (!listener)
::PostQuitMessage(0);
LIBSSH2_CHANNEL* channel = NULL;
ioctlsocket(sshsock, FIONBIO, &mode);
libssh2_session_set_blocking(session, 0); // non-blocking
int err = LIBSSH2_ERROR_EAGAIN;
while (err == LIBSSH2_ERROR_EAGAIN)
{
channel = libssh2_channel_forward_accept(listener);
if (channel) break;
err = libssh2_session_last_errno(session);
boost::this_thread::yield();
}
if (channel)
{
char buf[MAX_BUF_LEN];
char* chunk;
long bytes_read = 0;
long bytes_written = 0;
int total_set = 0;
timeval wait;
wait.tv_sec = 0;
wait.tv_usec = 2000;
sockaddr_in localhost;
localhost.sin_family = AF_INET;
localhost.sin_addr.s_addr = inet_addr("127.0.0.1");
localhost.sin_port = htons(5900);
SOCKET local_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
ioctlsocket(local_sock, FIONBIO, &mode);
iretval = connect(local_sock, (sockaddr*) &localhost, sizeof(localhost) );
if (iretval == SOCKET_ERROR)
iretval = WSAGetLastError();
while (1)
{
bytes_read = libssh2_channel_read(channel, buf, MAX_BUF_LEN);
if (bytes_read >= 0){
FD_ZERO(&read_set);
FD_ZERO(&write_set);
FD_SET(local_sock, &write_set);
// wait until the socket can be written to
while (select(0, &read_set, &write_set, NULL, &wait) < 1)
boost::this_thread::yield();
if (FD_ISSET(local_sock, &write_set))
{
FD_CLR(local_sock, &write_set);
chunk = buf;
// everything may not get written in this call because we're non blocking. So
// keep writing more data until we've emptied the buffer pointer.
while ((bytes_written = send(local_sock, chunk, bytes_read, 0)) < bytes_read)
{
// if it couldn't write anything because the buffer is full, bytes_written
// will be negative which won't help our pointer math much
if (bytes_written > 0)
{
chunk = buf + bytes_written;
bytes_read -= bytes_written;
if (bytes_read == 0)
break;
}
FD_ZERO(&read_set);
FD_ZERO(&write_set);
FD_SET(local_sock, &write_set);
// wait until the socket can be written to
while (select(0, &read_set, &write_set, NULL, &wait) < 1)
boost::this_thread::yield();
}
}
}
FD_ZERO(&read_set);
FD_ZERO(&write_set);
FD_SET(local_sock, &read_set);
select(0, &read_set, &write_set, NULL, &wait);
if (FD_ISSET(local_sock, &read_set))
{
FD_CLR(local_sock, &read_set);
bytes_read = recv(local_sock, buf, MAX_BUF_LEN, 0);
if (bytes_read >= 0)
{
while ((bytes_written = libssh2_channel_write_ex(channel, 0, buf, bytes_read)) == LIBSSH2_ERROR_EAGAIN)
boost::this_thread::yield();
}
}
boost::this_thread::yield();
} // while
} // if channel
}
P.S. To make this work requires the latest SVN builds of libssh2. There were bugs in prior versions that kept port forwarding from being usable.
The libssh2 source code includes since a few years a direct_tcpip.c example which demonstrates how to create direct-tcpip SSH channels, and since last week a forward-tcpip.c example which demonstrates how to create forward-tcpip SSH channels.
direct-tcpip is what ssh -L uses, and forward-tcpip is what ssh -R uses.
It is always the responsibility of libssh2 users to deal with the actual data. libssh2 takes care of SSH channels and nothing else. You can benefit significantly from studying the SSH RFCs, in particular RFC 4254, to find more about what exactly each channel type promises you, and thus what you can expect from libssh2.