Subversion Repositories shark

Rev

Rev 1677 | Blame | Compare with Previous | Last modification | View Log | RSS feed

%----------------------------------------------------------------------------
\chapter{The Network Library}
%----------------------------------------------------------------------------

To allow communication among different computers, S.Ha.R.K. provides a Network
Library implementing the UDP/IP stack on an Ethernet network. The library is
organized in three layers:

\begin{itemize}
\item low-level driver: this layer is hardware dependent, since it interacts
with the network card;
\item ethernet layer: this layer allows the upper layer to send and receive
ethernet frames. It is not intended to be used by a user program, but only by
the code implementing the network protocol;
\item high-level layer: this layer implements the network (IP) and the transport
(UDP) protocols. It provides the interface used by a user program to access the
network through the UDP protocol.
\end{itemize}

The low-level driver is implemented in order to respect the system real-time
requirements (avoiding unpredictable delays in sending/receiving frames). This
result is achieved by solving two different problems: the interrupt handling (in
the receive phase) and the mutual exclusion needed for accessing the network
card (in the transmission phase). The first problem is solved by using a SOFT
task to handle the network card interrupts: on a frame arrival, a task handling
the reception is activated. Such a task is guaranteed along with all the other
tasks in the system, thus it cannot jeopardize their schedulability. Since a
minimum interarrival time for the frames cannot be predicted, the receiving task
cannot be a sporadic (HARD) task; therefore the task uses a SOFT\_TASK\_MODEL,
and we have used a Constant Bandwidth Server (CBS) to serve it.

The second problem can be solved using two different methods. The first method
adopts a shared memory programming paradigm: a task willing to transmit is
allowed to access the network card; mutually exclusive accesses are guaranteed
by semaphores. This solution is very simple and the introduced overhead is very
low. The second soultion is based on the utilization of a server task devoted to
send frames on the network on behalf of other tasks. Each task posts its frames
in a mailbox, whence the sender task picks them up. At the moment, only the
first solution is implemented, but in order to provide a good degree of
flexibility, both approaches will be supported as soon as possible.

The most diffused higher level protocols have been implemented upon the ethernet
level. In order to use them within a S.Ha.R.K. application, the
\texttt{drivers/udpip.h} file, containing the functions prototypes and the data
structures, has to be included in the application program. As a first step, the
network drivers have to be initialized. This is done by the \texttt{net\_init()}
primitive that requires the machine's IP address. After initialization, the
program has to bind itself to a port by a socket (in UNIX's fashion) by the
\texttt{udp\_bind()} primitive. Afterwards, it is possible eihter to receive
packets by using \texttt{udp\_recvfrom()}, or to send them by using
\texttt{udp\_sendto()}.

\vspace{10mm}

\begin{intest}
NET\_INIT\index{net\_init()}
\end{intest}

\begin{description}
\item [\textbf{void net\_init(NET\_MODEL {*}m)}]
\item [\textbf{Description:}] It is an interface function used for calling the different
layers initializing functions. The \texttt{m} parameter specifies the protocols
that are going to be activated along with the parameters to be passed to their
initializing fucntions. The predefined \texttt{net\_base} value, if used as
\texttt{net\_init} parameter, causes the ethernet level to be solely intialized
by using a mutex semaphore for enforcing mutual exclusion. Moreover, the
\texttt{net\_setudpip(m, addr)} macro is defined to select the UDP/IP protocols
stack with local IP address \texttt{addr}, expressed in string format. The task
used to handle network card interrupts has a SOFT\_TASK\_MODEL obtained
initializing such a model with these arguments: \\
\texttt{soft\_task\_default\_model(m);} \\
\texttt{soft\_task\_def\_wcet(m, 1000);} \\
\texttt{soft\_task\_def\_period(m,20000);} \\
\texttt{soft\_task\_def\_met(m, 1000);} \\
\texttt{soft\_task\_def\_aperiodic(m);} \\
\texttt{soft\_task\_def\_system(m);} \\
\texttt{soft\_task\_def\_nokill(m);}
\end{description}

\begin{description}
\item [Example:]
\end{description}

\begin{tt}
\begin{verbatim}
int main(int argc, char **argvvoid) {
    NET_MODEL m = net_base;
    char talk_myipaddr[50];
    ...
    strcpy(talk_myipaddr, "193.205.82.47");
    net_setudpip(m, talk_myipaddr, "255.255.255.255");
    net_init(&m);
    ...
\end{verbatim}
\end{tt}

\begin{intest}
IP\_STR2ADDR\index{ip\_str2addr()}
\end{intest}

\begin{description}
\item [\textbf{int ip\_srt2addr(char {*}str, IP\_ADDR {*}ip)}]
\item [\textbf{Description:}] It converts the IP address, contained in the \texttt{str}
string parameter, into \texttt{IP\_ADDR} format. The result is returned in the
variable pointed by \texttt{ip}. The function returns \texttt{TRUE} if the
operation has been succesful, \texttt{FALSE} otherwise.
\end{description}

\begin{intest}
UDP\_BIND\index{udp\_bind()}
\end{intest}

\begin{description}
\item [\textbf{int udp\_bind(UDP\_ADDR {*}a, IP\_ADDR {*}bindlist);}]
\item [\textbf{Description:}] It binds the receiving program on the specified UDP port. A
socket is created and its identifier is returned. Moreover the host addresses
specified through the \texttt{bindlist} parameter are loaded into the ARP table.
The port is identified by the \texttt{a} parameter which is composed of the
fields named \texttt{s\_addr}, having \texttt{IP\_ADDR} type, and
\texttt{s\_port}, having \texttt{WORD} type. The \texttt{s\_port} parameter is
the most meaningful since it specifies the port the function binds to. Further
details can be found in any UNIX manual. The possibility of specifying the hosts
that will be accessed (through the \texttt{bindlist} parameter), permits to add
ARP table entries in the network initialization phase. In this way, the timing
impredictability introduced by ARP can be reduced. This possibility can be
discarded by chosing a \texttt{NULL} value for the \texttt{bindlist} parameter.
Otherwise such a parameter has to be a pointer to a \texttt{NULL} terminated
\texttt{IP\_ADDR} array. As we said earlier, the returned socket identifier can
be fed into the \texttt{udp\_sendto()} primitive.
\end{description}

\begin{description}
\item [Example:]
\end{description}

\begin{tt}
\begin{verbatim}
void *txsessiontask(void *arg) {
    UDP_ADDR local;
    IP_ADDR bl[5];
    int sock;
   
    /* The periodic txsessiontask, upon its creation, */
    /* creates a socket befor entering */
    /* the infinite cycle typical of all */
    /* periodic tasks */
    local.s_port = 1030; /* local port */
   
    /* It loads the eth address of the hosts */
    /* the task will communicate with */
    /* into the ARP table */
    ip_str2addr("193.205.82.47", bl);
   
    /* terminates bind list by NULL */
    *((DWORD *)&bl[1]) = NULL;
    sock = udp_bind(&local, bl);
    ...
}
\end{verbatim}
\end{tt}

\begin{intest}
UDP\_SENDTO\index{udp\_sendto()}
\end{intest}

\begin{description}
\item [\textbf{int udp\_sendto(int s, char {*}buf, int nbytes, UDP\_ADDR {*}to);}]
\item [\textbf{Description:}] It sends an UDP packet, with size \texttt{nbytes}, whose
body is pointed by \texttt{buf} to an address specified by \texttt{to}. The last
parameter has \texttt{UDP\_ADDR} type and identifier either the destination IP
address or the port (see \texttt{udp\_bind} for further information on
\texttt{UDP\_ADDR}). In order to send a packet a local socket has to be created,
by calling the \texttt{udp\_bind()} primitive; its identifier shall be passed by
the \texttt{s} parameter.
\end{description}

\begin{description}
\item [Example:]
\end{description}

\begin{tt}
\begin{verbatim}
void *txsessiontask(void *arg) {
    int sock;
    UDP_ADDR local, to;
    ...
    /* socket creation */
    ...
   
    for (;;) {
        ...

        /* prepares the destination address */
        to.s_port = 1030;
        ip_str2Addr("127.0.0.1", &(to.s_addr));
        udp_sendto(sock, msg, msglen, &to);
        ...
        task_endcycle();
    }
}
\end{verbatim}
\end{tt}

\begin{intest}
UDP\_RECVFROM\index{udp\_recvfrom()}
\end{intest}

\begin{description}
\item [\textbf{int udp\_recvfrom(int s, char {*}buf, UDP\_ADDR {*}from);}]
\item [\textbf{Description:}] It receives a UDP packet from the socket identified by
\texttt{s}; the packet body is copied into the buffer pointed by \texttt{buf};
the sender address (composed of the pair port-IPaddress) is copied into the
variable pointed by \texttt{from}. The primitive returns the number of bytes
composing the packet.
\end{description}

\begin{description}
\item [Example:]
\end{description}

\begin{tt}
\begin{verbatim}
TASK rxsessiontask() {
    int sock;
    UDP_ADDR local, from;
   
    /* The non real-time rxsessiontask */
    /* creates a receiving socket and */
    /* enters an infinite loop waiting */
    /* for the incoming packets */
    ...
    /* During initialization the */
    /* socket is created */
    ...
    for (;;) {
        ...
        udp_recvfrom(sock, inmsg, &from);
        /* from contains the sender address */
        ...
        task_endcycle();
    }
}
\end{verbatim}
\end{tt}

\begin{intest}
UDP\_NOTIFY\index{udp\_notify()}
\end{intest}

\begin{description}
\item [\textbf{int udp\_notify(int s, int({*}f)(int len, BYTE {*}buf, void {*}p))}]
\item [Desription:]the notifying function \texttt{f} is associated with the
\texttt{s} socket. When a packet addressed to the port the socket is bound to
arrives, such a function is invoked. Upon its invocation, the function receives
as arguments a pointer to the received packet (\texttt{buf}), the packet size
(\texttt{len}), and a pointer specified along with the \texttt{udp\_notify()}
call (\texttt{p}). The notifying function is executed within the context of the
receiving task; therefore, it should not consume too much time.
\end{description}

\begin{description}
\item [Example:]
\end{description}

\begin{tt}
\begin{verbatim}
int hrtp_recvfun(int len, BYTE *b, void *p) {
    struct HRTP_SESSION *s;
    struct HRTP_HDR *h;
    ...
   
    /* Notifying function used for receiving */
    /* packets from a session level protocol */
    ...
    /* p is a pointer to the descriptor of */
    /* the session involved in the transmission */
    s = p;
   
    /* the received packet is copied into a private */
    /* buffer belonging to the session */
    h = (struct HRTP_HDR *) & (s->b[s->recvd * instance_dim]);
    memcpy(h, b, len);
    ...
   
    return 0;
}
...

void hrtp_recv(struct HRTP_SESSION *s, void (*f)(void)) {
    udp_notify(s->sock, hrtp_recvfun, (void *)s);
    s->notifyparm = f;
    s->active = HRTP_RCVIN;
}
...
\end{verbatim}
\end{tt}

A programmer that wants to implement a new transport/network level stack
different from UDP/IP needs to directly access the Ethernet services. This can
be done using the Ethernet layer, accessible through the ``eth.h'' header
file.

In order to receive Ethernet frames, a callback function has to be associated to
a frame type (the frame type is a field of a frame): each time that a frame of
the specified type will arrive, the callback function will be called. A callback
can be bound to a frame type using the \texttt{eth\_setHeader} library call.

In order to transmit Ethernet frames, a transmission buffer must be allocated
and filled: the header can be filled using \texttt{eth\_setHeader()}, while the
body must be explicitly filled after obtaining a pointer to it through
\texttt{eth\_getFDB()}. At this point the frame can be sent using
\texttt{eth\_sendPKT()}. Transmission buffers can be allocated using the
\texttt{netbuff} module: this module (usable including the ``netbuff.h''
header file) permits to manage pools of pre-allocated buffers, in order to
minimize the unpredictability due to dynamic allocation.

\begin{intest}
NETBUFF\_INIT\index{netbuff\_init()}
\end{intest}

\begin{description}
\item [\textbf{void netbuff\_init(NETBUFF {*}netb, BYTE nbuffs, WORD buffdim);}]
\item [\textbf{Description:}] It initializes a pool composed of \texttt{nbuffs} buffers,
each of them have \texttt{buffdim} size. The pool is identified by the
\texttt{netb} descriptor.
\end{description}

\begin{intest}
NETBUFF\_GET\index{netbuff\_get()}
\end{intest}

\begin{description}
\item [\textbf{void {*}netbuff\_get(NETBUFF {*}netb, BYTE flag);}]
\item [\textbf{Description:}] It returns a pointer to the first free buffer in the pool
identified by \texttt{netb}. The \texttt{flag} parameter, which can assume
values \texttt{BLOCK} or \texttt{NON\_BLOCK}, indicates whether the operation is
a blocking or non-blocking allocation. If a non-blocking \texttt{netbuff\_get()}
is performed when the pool does not contain any free buffer, a NULL pointer is
returned.
\end{description}

\begin{intest}
NETBUFF\_RELEASE\index{netbuff\_release()}
\end{intest}

\begin{description}
\item [\textbf{void netbuff\_release(NETBUFF {*}netb, void {*}buff);}]
\item [\textbf{Description:}] It marks the buffer pointed by \texttt{buff} as free in the
\texttt{netb} pool.
\end{description}

\begin{intest}
ETH\_INIT\index{eth\_init()}
\end{intest}

\begin{description}
\item [\textbf{void eth\_init(int mode, TASK\_MODEL {*}m);}]
\item [\textbf{Description:}] It initializes the Ethernet layer, in order to transmit or
receive ethernet frames. If the Ethernet layer has already been initialized, it
does nothing.

Each network level protocol that needs to access the network card must pass
through the Ethernet layer, which must be initialized before receiving or
sending anything. The Ethernet layer will search for a supported network card,
enable it and initalize some internal structures.

At the moment, the \texttt{mode} parameter is not used, in the future it will be
used to select an operating mode (mutual exclusion on send operation through a
dedicated server task or through mutexes).

This library function is called by \texttt{net\_init()} in the standard UDP/IP
configuration.

The task model used for the task that handles the network card interrupts can be
passed with the m parameter. If it is NULL, a SOFT\_TASK\_MODEL obtained
initializing such a model with these arguments will be used: \\
\texttt{soft\_task\_default\_model(m);}\\
\texttt{soft\_task\_def\_wcet(m,1000);}\\
\texttt{soft\_task\_def\_period(m,20000);}\\
\texttt{soft\_task\_def\_met(m,1000);}\\
\texttt{soft\_task\_def\_aperiodic(m);}\\
\texttt{soft\_task\_def\_system(m);}\\
\texttt{soft\_task\_def\_nokill(m);}
\end{description}

\begin{intest}
HTONS \& NTOHS \index{htons()}\index{ntohs()}
\end{intest}

\begin{description}
\item [\textbf{WORD htons(WORD host);}]
\item [\textbf{WORD ntohs(WORD net);}]
\item [\textbf{Description:}] These two utility functions convert a WORD from the host
format to the net format (\texttt{htons}) and vice-versa (\texttt{ntohs}).
\end{description}

\begin{intest}
ETH\_GETADDRESS\index{eth\_getAddress()}
\end{intest}

\begin{description}
\item [\textbf{void eth\_getAddress(ETH\_ADDR {*}eth);}]
\item [\textbf{Description:}] It returns the net card Ethernet address in the
\texttt{ETH\_ADDR} structure pointed by \texttt{eth}.
\end{description}

\begin{intest}
ETH\_STR2ADDR\index{eth\_str2addr()}
\end{intest}

\begin{description}
\item [\textbf{void eth\_str2addr(char {*}add, struct ETH\_ADDR {*}ds);}]
\item [\textbf{Description:}] It converts an Ethernet address from the string format
({}``xx:xx:xx:xx:xx:xx'') to the byte (\texttt{ETH\_ADDR}) format. The
\texttt{add} string contains the address in text format, while \texttt{ds} is a
pointer to the \texttt{ETH\_ADDR} structure where the output is placed.
\end{description}

\begin{intest}
ETH\_SETPROTOCOL\index{eth\_setProtocol()}
\end{intest}

\begin{description}
\item [\textbf{int eth\_setProtocol(WORD type, void ({*}recv)(void {*}frame))}]
\item [\textbf{Description:}] It is used to specify the callback function \texttt{recv} to
be called when a frame of type \texttt{type} is received. It returns
\texttt{TRUE} in the case of success, \texttt{FALSE} otherwise. When the
Ethernet layer will call the \texttt{recv} callback, it will pass to the
function a pointer to the received frame.

The callback function runs in the context of the driver task, served by a CBS.

Each high-level protocol must use this library call to register itself in order
to process incoming packets.
\end{description}

\begin{description}
\item [Example:]
\end{description}

\begin{tt}
\begin{verbatim}
void ip_server_recv(void *pkt) {
    IP_HEADER *iphd;
    ...
   
    /* This callback is invoked when an IP packet is received */
    iphd = (IP_HEADER *)eth_getFDB(pkt);
    ...
}
...

void ip_init(char *localAddr) {
    int i;
 
    /* Initializes IP */
    ...
    /* Registers the protocol to the ethernet layer */
    eth_setProtocol(ETH_IP_TYPE,ip_server_recv);
    ...
}
\end{verbatim}
\end{tt}

\begin{intest}
ETH\_SETHEADER\index{eth\_setHeader()}
\end{intest}

\begin{description}
\item [\textbf{void {*}eth\_setHeader(void {*}b, ETH\_ADDR dest, WORD type);}]
\item [\textbf{Description:}] It fills the header of the frame pointed by \texttt{b} with
the destination address \texttt{dest} and the frame type \texttt{type}. It also
returns a pointer to the body of the ethernet frame.
\end{description}

\begin{intest}
ETH\_GETFDB\index{eth\_getFDB()}
\end{intest}

\begin{description}
\item [\textbf{void {*}eth\_getFDB(void {*}p)}]
\item [\textbf{Description:}] Get First Data Byte. It returns a pointer to the body of the
frame pointed by the \texttt{p} parameter.
\end{description}

\begin{intest}
ETH\_SENDPKT\index{eth\_sendPkt()}
\end{intest}

\begin{description}
\item [\textbf{int eth\_sendPkt(void {*}p, int len);}]
\item [\textbf{Description:}] It transmits the Ethernet frame pointed by \texttt{p},
having lenght \texttt{len}. The destination and the frame type must be
previously specified using \texttt{eth\_setHeader}.
\end{description}

\begin{description}
\item [Example:]
\end{description}

\begin{tt}
\begin{verbatim}
void arp_sendRequest(int i) {
    ARP_PKT *pkt;
    ETH_ADDR broadcast, nulladdr;

    eth_str2Addr("FF:FF:FF:FF:FF:FF", &broadcast);
    eth_str2Addr("00:00:00:00:00:00", &nulladdr);
    eth_setHeader(arpBuff, broadcast, ETH_ARP_TYPE);
    pkt = (ARP_PKT *)eth_getFDB(arpBuff);
    pkt->htype = htons(ARP_ETH_TYPE);
    pkt->ptype = htons(ARP_IP_TYPE);
    pkt->hlen = sizeof(ETH_ADDR);
    pkt->plen = sizeof(IP_ADDR);
    pkt->operation = htons(ARP_REQUEST);
    setEthAddr(pkt->sha, myEthAddr);
    setEthAddr(pkt->tha, nulladdr);
    setIpAddr(pkt->sip, myIpAddr);
    setIpAddr(pkt->tip, arpTable[i].ip);
    eth_sendPkt(arpBuff, sizeof(ARP_PKT));
}
\end{verbatim}
\end{tt}

\begin{intest}
ETH\_CLOSE\index{eth\_close()}
\end{intest}

\begin{description}
\item [\textbf{int eth\_close(void);}]
\item [\textbf{Description:}] It closes the Ethernet protocol. If it is not explicitly
called by the user, it is automatically executed through \texttt{sys\_atexit}.
\end{description
}