This document specifies the operation of the NoTA High Interconnect (H_IN) and its interfaces. H_IN is a part of NoTA Interconnect stack. The Interconnect stack consists of H_IN and L_IN. The H_IN handles service registration, discovery, access and security. The L_IN is responsible for conencting the subsystems together.
Each subsystem has one H_IN and one L_IN. L_IN is divided to L_INup and L_INdown. One L_INdown is responsible for one network technology and there can be multiple L_INdowns. H_IN uses the L_IN with L_IF interface. The L_IN provides address resolution, H_IN to H_IN message transport and number of sockets that can be connected. The sockets are used to transfer data between nodes (ANs and SNs).
The nodes (ANs and SNs) use the H interface (H_IF) to use the H_IN. The H_IN interface can be different in different kind of subsystems. There is a reference H_IF that is the BSD Socket API, that can be used as such or slighly modified. The modified BSD Socket API prefixes each function with capital H and adds instance. For example connect(args) function call is modified to be Hconnect(h_in_t* h_in, args) function call. The arguments, return values and semantics are exactly same as in BSD Socket API. There is also an API extension that adds some functions to BSD Socket API. Usage of the API Extension is never compulsory.
The service repository in H_IN is centralized. One of the H_INs act as a HManager (old term H-level Resource Manager). The special node is responsible for having a list of the services in the network and acting as authorization authority. There is also a higher level Resource Manager (also called RMSN) that can be connected by other NoTA nodes with normal H_IN level connection. For implementing service connection authentication there is a special H_IN to RM interface. The usage of that interface is optional and as the interface is local, it can be also modified.
The H_INs communicate between each other with H_IN peer protocols. Those protocols are defined and conforming the H_IN peer protocols is mandatory. The peer protocols are the only mandatory part of the specification.
H_IN provides streaming type and message based sockets. The streaming type socket does not preserve message boundaries whereas the packet type socket preserves the boundaries. Otherwise there is no differences in sockets.
The services can have multiple ports when they register. Port 0 refers to standard port that typically accepts service requests. The SN may allocate other ports to whatever purposes. Any port can support either message based or streaming based sockets.
The H_IN Interface will be as default the BSD Socket Interface. There can be also other interfaces and the interface can be freely decided by subsystem vendor. The reference implementation will provide the standard H_IN interface. The BSD Socket Interface does not have all functionalities and therefore it need to be extended. Below is a list of extensions.
New definitions, additions to BSD socket API, the BSD Socket API extension.
#define AF_NOTA 34
#define PF_NOTA 34
typedef unsigned int sid_t;
typedef unsigned int vdid_t;
struct _nota_addr_t {
sid_t sid; /* service identifier */
unsigned int port; /* port of the sid */
vdid_t device; /* virtual device id */
};
typedef struct _nota_addr_t nota_addr_t;
The functions presented are the H variants, meaning that they are BSD socket API calls except that they have been prefixed with capital H and instance reference is added.
h_in_t* Hgetinstance()
Returns instance to H_IN.
| Return value | Description |
|---|---|
| non-NULL | Successfull |
| NULL | Error |
int Hsocket(h_in_t* h_in, int domain, int type, int protocol)
Creates a new socket.
| Parameter | Description |
|---|---|
| h_in | H_IN Instance |
| domain | Domain of the socket. The AF_NOTA is the NoTA socket |
| type | NoTA supports SOCK_STREAM & SOCK_SEQPACKET |
| protocol | Protocol to use, use 0 |
| Return value | Description |
|---|---|
| >=0 | Descriptor of socket that was created (success) |
| -1 | Error, check errno |
Available error codes: EINVAL, EAFNOSUPPORT, EMFILE, ENFILE, EPROTONOSUPPORT, EPROTOTYPE, EACCES, ENOMEM, ENOBUFS, ENONET
int Hbind(h_in_t* h_in, int sockfd, struct sockaddr* my_addr, socklen_t addrlen)
Binds a created socket for given SID and port.
| Parameter | Description |
|---|---|
| h_in | H_IN Instance |
| my_addr | Address to use in binding |
| addrlen | Length of the address struct |
| Return value | Description |
|---|---|
| 0 | Success |
| -1 | Error (see errno) |
Available error codes: EBADF, EINVAL, EACCES, ENOTSOCK
int Hlisten(h_in_t* h_in, int socket, int backlog)
Sets bound socket to listening mode for incoming connections.
| Parameter | Description |
|---|---|
| h_in | H_IN Instance |
| socket | Socket descriptor |
| backlog | Number of connections to keep in the backlog for accepting |
| Return value | Description |
|---|---|
| 0 | Success |
| -1 | Error (see errno) |
Available error codes: EBADF, EDESTADDRREQ, EINVAL, ENOTSOCK, EOPNOTSUPP, EACCESS, EFAULT, ENOBUFS
int Haccept(h_in_t* h_in, int socket, struct sockaddr* addr, socklen_t* addrlen)
Accepts an incoming connection.
| Parameter | Description |
|---|---|
| h_in | H_IN Instance |
| addr | Address of the remote. The function will fill up if not NULL |
| addrlen | Length of target address struct. The function will fill up if not NULL |
| Return value | Description |
|---|---|
| >=0 | Success. Returns a socket descriptor. |
| -1 | Error (see errno) |
Available error codes: EAGAIN, EBADF, ENOTSOCK, EOPNOTSUPP, EINTR, ECONNABORTED, EINVAL, EMFILE, ENFILE, EFAULT, ENOBUFS, ENOMEM, EPROTO
int Hconnect(h_in_t* h_in, int socket, const struct sockaddr* address, socklen_t address_len)
Connects a socket to the given address.
| Parameter | Description |
|---|---|
| h_in | H_IN Instance |
| address | Target address |
| address_len | Length of target address struct |
| Return value | Description |
|---|---|
| 0 | Success |
| -1 | Error (see errno) |
Available error codes: EADDRNOTAVAIL, EAFNOSUPPORT, EALREADY, EBADF, ECONNREFUSED, EINPROGRESS, EINTR, EISCONN, ENETUNREACH, ENOTSOCK, EPROTOTYPE, ETIMEDOUT, EACCES, EADDRINUSE, ECONNRESET, EHOSTUNREACH, EINVAL, ENETDOWN, ENOBUFS, EOPNOTSUPP, EFAULT, EBUSY
ssize_t Hsend(h_in_t* h_in, int socket, const void* buffer, size_t length, int flags)
Sends data through socket. The send shall block until all data is sent unless MSG_DONTWAIT flag has been set.
The function sendmsg behaves same way.
| Parameter | Description |
|---|---|
| h_in | H_IN Instance |
| buffer | The send buffer to be sent |
| length | Length of the data to be sent |
| flags | Send flags |
| Return value | Description |
|---|---|
| 0>= | Success, returns number of bytes sent |
| -1 | Error (see errno) |
Available error codes: EAGAIN, EBADF, ECONNRESET, EDESTADDRREQ, ENOTCONN, ENOTSOCK, EOPNOTSUPP, EACCESS, ENETDOWN, ENETUNREACH, ENOBUFS
ssize_t Hrecv(h_in_t* h_in, int socket, void* buffer, size_t length, int flags)
Receives data from the socket. The recv shall block until some data is received unless MSG_DONTWAIT has been set. If the MSG_DONTWAIT has been set and the socket is stream based, the function will return some data if there is any data to be received. If the socket is message based, the function will return with contents of a message if there is any available. If the socket is message based, the function will discard the remaining data of the message that does not fit into the buffer. The MSG_PEEK option can be used to check the message size.
The function recvmsg behaves same way.
| Parameter | Description |
|---|---|
| h_in | H_IN Instance |
| buffer | The receive buffer |
| length | Length of the receive buffer |
| flags | Receive flags (flags can be OR'd) |
| Flag name | Description |
|---|---|
| MSG_WAITALL | Blocks untill whole buffer is filled up |
| MSG_PEEK | Peeks on the received data. Same data will be returned again. In case of message based socket the return value will be the size of the message (can be higher than buffer length). |
| Return value | Description |
|---|---|
| >=0 | Success, number of bytes received to the buffer |
| -1 | Error (see errno) |
Available error codes: EAGAIN, EBADF, ECONNRESET, EINTR, EINVAL, ENOTCONN, ENOTSOCK, EOPNOTSUPP, ETIMEDOUT, ENOBUFS, ENOMEM
int Hselect(h_in_t* h_in, int nfds, fd_set* readfds, fd_set* writefds, fd_set* errorfds, const struct timespec* timeout)
Monitors a given set of sockets for state changes. The sockets can be monitored when there is a data available for reading, when data can be sent and when socket state becomes to an error state. The socket sets should be manipulated with macros. The same function is used to implement Hpselect and Hpoll.
| Parameter | Description |
|---|---|
| h_in | H_IN Instance |
| nfds | Highest socket descriptor number |
| readfds | Sockets to be monitored for reading |
| writefds | Sockets to be monitored for writing |
| errorfds | Sockets to be monitored for errors |
| timeout | Timeout value |
| Macro | Description |
|---|---|
| FD_ZERO(fd_set* fdset) | Initialize fd_set |
| FD_CLR(int fd, fd_set* fdset) | Clear socket active flag |
| FD_ISSET(int fd, fd_set* fdset) | Check whether socket is active |
| FD_SET(int fd, fd_set* fdset) | Mark socket active |
| Return value | Description |
|---|---|
| >=0 | Success, number of sockets active (0 means timeouted) |
| -1 | Error (see errno) |
Available error codes: EBADF, EINTR, EINVAL
int Hclose(h_in_t* h_in, int socket)
Close a socket connection.
| Parameter | Description |
|---|---|
| h_in | H_IN Instance |
| socket | Socket descriptor |
| Return value | Description |
|---|---|
| 0 | Success |
| -1 | Error (see errno) |
Available error codes: EBADF, EINTR, EINVAL, EFAULT, ENOTSOCK, EBUSY
int Hsetsockopt(h_in_t* h_in, int socket, int level, int option_name, const void* option_value, socklen_t option_len)
Sets socket option.
| Parameter | Description |
|---|---|
| h_in | H_IN Instance |
| socket | Which socket to modify |
| level | Which level option to set (SOL_SOCKET) |
| option_name | Which option to set |
| option_value | New value of the option |
| option_len | Length of the value |
| Socket option | Parameter type |
|---|---|
| SO_PASSCRED | bytearray |
| Return value | Description |
|---|---|
| 0 | Success |
| -1 | Error (see errno) |
Available error codes: EBADF, EDOM, EINVAL, EISCONN, ENOPROTOOPT, ENOTSOCK, ENOMEM, ENOBUFS, EFAULT
int Hgetsockopt(h_in_t* h_in, int socket, int level, int option_name, void* option_value, socklen_t* option_len)
Gets socket option.
| Parameter | Description |
|---|---|
| h_in | H_IN Instance |
| socket | Which socket |
| option_name | Name of the option (see SO_*) |
| option_value | Pointer to memory where value will be saved |
| option_len | Pointer to a variable that shows the memory length. Will be updated to match the real length |
| Return value | Description |
|---|---|
| 0 | Success |
| -1 | Error (see errno) |
Available error codes: EBADF, EINVAL, ENOPROTOOPT, ENOTSOCK, EACCES, EINVAL, ENOBUFS
#define SID 1
int sock, err;
nota_addr_t addr = { SID, DEFAULT_PORT };
sock = Hsocket(AF_NOTA, SOCK_STREAM, 0); /* create a socket */
if(sock < 0)
return ERROR;
/* the certificate is optional */
err = Hsetsockopt(sock, SOL_SOCKET, SO_PASSCRED, ptr_to_certificate, certificate_length);
if(err != 0) {
Hclose(sock);
return ERROR;
}
err = Hbind(sock, &addr, sizeof(addr)); /* bind to service id 1 */
if(err != 0) {
Hclose(sock);
return ERROR;
}
err = Hlisten(sock, 1); /* start listening with backlog = 1 */
if(err != 0) {
Hclose(sock);
return ERROR;
}
return sock;
#define SID 1
int sock, err;
nota_addr_t addr = { SID, DEFAULT_PORT };
sock = Hsocket(AF_NOTA, SOCK_STREAM, 0); /* create a socket */
if(sock < 0)
return ERROR;
/* the certificate is optional */
err = Hsetsockopt(sock, SOL_SOCKET, SO_PASSCRED, ptr_to_certificate, certificate_length);
if(err != 0) {
Hclose(sock);
return ERROR;
}
err = Hconnect(sock, &addr, sizeof(addr)); /* connect to a service */
if(err != 0) {
Hclose(sock);
return ERROR;
}
return sock;
int fd;
int err;
char data[4] = "abcd";
char buffer[4];
/* connect to a service first */
fd = connect_to_a_service();
if(fd < 0)
return ERROR;
/* send the data */
err = Hsend(fd, data, 4, 0);
if(err != 4) {
Hclose(fd);
return ERROR;
}
/* receive some data */
err = Hrecv(fd, buffer, 4, MSG_WAITALL);
if (err != 4) {
Hclose(fd);
return ERROR;
}
/* done */
Hclose(fd);
int serverfd, new_connection;
/* the connection can be accepted after a service is activated */
serverfd = activate_service();
if(new_connection < 0)
return ERROR;
/* the accept call blocks, normally the peer information is not usefull */
new_connection = Haccept(serverfd, NULL, NULL);
if(new_connection < 0) {
Hclose(serverfd);
return ERROR;
}
/* now we can send / receive data */
err = Hsend(new_connection, "abcd", 4, 0);
if(err != 4) {
Hclose(serverfd);
return ERROR;
}
/* Sockets can be monitored with select function call
* In this example we are monitoring one socket for incoming
* connections and one when there is a data or when data can
* be sent and all for error conditions.
*/
int serverfd, connection, maxfd;
struct timeval tv, err;
fd_set readlist, writelist, errorlist;
/* Connect to a service */
connection = connect_to_a_service();
/* Activate a service */
serverfd = activate_service();
/* set timeout to 10 sec */
tv.m_sec = 10;
tv.m_usec = 0;
maxfd = serverfd > connection ? serverfd : connection;
while(serverfd >= 0 || connection >= 0) {
/* zero current lists */
FD_ZERO(&readlist);
FD_ZERO(&writelist);
FD_ZERO(&errorlist);
/* set sockets to be monitored */
if(connection >= 0) {
FD_SET(connection, &readlist);
FD_SET(connection, &writelist);
FD_SET(connection, &errorlist);
}
if(serverfd >= 0) {
FD_SET(connection, &readlist); }
/* do the select */
err = Hselect(maxfd, &readlist, &writelist, &errorlist, &tv);
if(err == -1)
break; /* error */
else if(err == 0)
continue; /* timeout */
if(FD_ISSET(serverfd, &readlist))
accept_new_connection(serverfd);
if(FD_ISSET(connection, &readlist))
read_data(connection);
if(FD_ISSET(connection, &writelist))
send_data(connection);
if(FD_ISSET(connection, &errorlist))
close_connection(connection);
}