Subversion Repositories shark

Rev

Go to most recent revision | Blame | Last modification | View Log | RSS feed

/*
 * Project: S.Ha.R.K.
 *
 * Coordinators:
 *   Giorgio Buttazzo    <giorgio@sssup.it>
 *   Paolo Gai           <pj@gandalf.sssup.it>
 *
 * Authors     :
 *   Paolo Gai           <pj@gandalf.sssup.it>
 *   Massimiliano Giorgi <massy@gandalf.sssup.it>
 *   Luca Abeni          <luca@gandalf.sssup.it>
 *   (see the web pages for full authors list)
 *
 * ReTiS Lab (Scuola Superiore S.Anna - Pisa - Italy)
 *
 * http://www.sssup.it
 * http://retis.sssup.it
 * http://shark.sssup.it
 */


/**
 ------------
 CVS :        $Id: arp.c,v 1.1.1.1 2002-03-29 14:12:50 pj Exp $

 File:        $File$
 Revision:    $Revision: 1.1.1.1 $
 Last update: $Date: 2002-03-29 14:12:50 $
 ------------
**/


/*
 * Copyright (C) 2000 Luca Abeni
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */



/* Author:      Giuseppe Lipari & Luca Abeni                    */
/* Date:        4/12/97                                         */

/* File:        ArpDrv.C                                        */
/* Revision:    2.0                                             */

/*
   ARP layer. It associates, the correct ethernet addresses to IP addresses
   this is done using an ARP table, created dinamicaly. This mechanism can
   introduce unpredictability (it is impossible to predict how much time is
   required to obtain an ethernet address from the network). To solve this
   problem, ARP is used only the first time that a computer is addressed.
*/


#include <kernel/kern.h>
#include <semaphore.h>

#include "eth_priv.h"
#include "netbuff.h"
#include <drivers/udpip.h>
#include "arp.h"
#include <signal.h>

#define FALSE   0
#define TRUE    1
#define PENDING 2
#define ARP_TIMEOUT     5
#define ARP_MAX_RETRANS 4
#define ARP_PRIORITY    10

#define ARP_LEN         70

/*
   Structure used to enqueue the packets destinated to an host whose ethernet
   address is still unknown. This structure overwrites the firsts fields of
   the ehternet header, but it isn't a problem because it is used only
   locally
*/

typedef struct {
    int len;
    void *next;
} PKT_NXT;

typedef struct arp_pkt{
    WORD htype;
    WORD ptype;
    BYTE hlen;
    BYTE plen;
    WORD operation;
    struct eth_addr sha;
    IP_ADDR sip;
    struct eth_addr tha;
    IP_ADDR tip;
} ARP_PKT;

struct netbuff arp_txbuff;
struct eth_addr broadcast,nulladdr;

BYTE reply[ETH_MAX_LEN];
extern struct netbuff udp_txbuff;

ARP_TABLE arpTable[ARP_MAX_ENTRIES];
sem_t arpMutex;

int arpIsInstalled = FALSE;

struct eth_addr myEthAddr;
IP_ADDR myIpAddr;

/*
   ARP Exceptions Handler: in particular, it is called when the ARP table
   is full. The default action is to free the less used entry in the table
   (it's the simpler thing to do!!!). An hard Real-Time task should have
   to disable the handler in this case. (for an hard task, the full table
   condition is an error condition, 'cause it can generate unpredictability).
*/

void arp_exc(int err)
{
    int i, j, minused;

    minused = ARP_MAX_USED; j = -1;
    for (i = 0; i < ARP_MAX_ENTRIES; i++) {
        if ((arpTable[i].valid != PENDING) && (arpTable[i].used <= minused)) {
            j = i;
            minused = arpTable[i].used;
        }
    }
    if (j == -1) {
        cprintf("ARP table overflow!!!\n");
        sys_abort(AARPFULL);
    }
    arpTable[j].valid = FALSE;
}

/*
   Send an ARP request: if there aren't free buffers, do nothing (there will
   be a retry)
*/

void arp_sendRequest(int i)
{
    ARP_PKT *pkt;
    BYTE *arpBuff;

    if ((arpBuff= netbuff_get(&arp_txbuff, NON_BLOCK)) != NULL) {
        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(struct 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));
        netbuff_release(&arp_txbuff, (void *)arpBuff);
    }
}

/* Retry task (periodic) */
TASK arp_retry(void)
{
    int i;
    PKT_NXT *p, *p1;
#ifdef __ARP_DBG__
char msg[100];
#endif

    while (1) {
        /* mutual exclusion on the ARP table */
        sem_xwait(&arpMutex, 1, BLOCK);
        for (i = 0; i < ARP_MAX_ENTRIES; i++) {
            if (arpTable[i].valid == PENDING) {
                arpTable[i].time--;
                if (arpTable[i].time <= 0) {
                    arpTable[i].ntrans++;
                    if (arpTable[i].ntrans > ARP_MAX_RETRANS) {
                        /* N. Max Retry? If yes, discard all the packets */
                        p = (PKT_NXT *)arpTable[i].pkt;
                        while (p != NULL) {
                            p1 = p->next;
                            netbuff_release(&udp_txbuff, (void *)p);
#ifdef __ARP_DBG__
sprintf(msg,"Pacchetto : %lp scartato\n",p);
win_puts(&w1,msg);
#endif
                            p = p1;
                        }
                        arpTable[i].valid = FALSE;
                    }
                    else {
                        arp_sendRequest(i);
                        arpTable[i].time = ARP_TIMEOUT;
                    }
                }
            }
        }
        sem_post(&arpMutex);
        task_endcycle();
    }
}

/* Search for a free entry in the ARP table (if there isn't any, return -1 */
int arp_req(IP_ADDR dest)
{
    int j, done;

    done = 0; j = 0;
    while ((j < ARP_MAX_ENTRIES) && !done) {
        if (arpTable[j].valid == FALSE) {
            done = 1;
            arpTable[j].valid = PENDING;
        } else j++;
    }

    if (!done) return -1;
    /* Fill the entry */
    setIpAddr(arpTable[j].ip, dest);
#ifdef __ARP_DBG__
sprintf(msg,"Indirizzo : %d.%d.%d.%d\n",dest.ad[0],
                                      dest.ad[1],
                                      dest.ad[2],
                                      dest.ad[3]);
win_puts(&w1,msg);
sprintf(msg,"Indirizzo : %d.%d.%d.%d\n",arpTable[j].ip.ad[0],
                                      arpTable[j].ip.ad[1],
                                      arpTable[j].ip.ad[2],
                                      arpTable[j].ip.ad[3]);
win_puts(&w1,msg);
#endif
    arpTable[j].time = ARP_TIMEOUT;
    arpTable[j].ntrans = 0;
    return j;
}

/*
   Send an IP packet. If the ethernet address isn't in the ARP table, send
   a request
*/

void arp_send(void *pkt, IP_ADDR dest, int len)
{
    int i,j;
    PKT_NXT *p,*p1;
    int caso;
#ifdef __ARP_DBG__
char msg[50];
#endif

    sem_xwait(&arpMutex, 1, BLOCK);
    caso = 0;
    j = -1;
    for (i = 0; i < ARP_MAX_ENTRIES; i++) {
        if (ip_compAddr(dest,arpTable[i].ip)) {
            /* found: CASE 1 */
            if (arpTable[i].valid == TRUE) {
                caso = 1;
                j = i;
            }
            else if (arpTable[i].valid == PENDING) {
                /* Entry found, but the ethernet address is still unknown: CASE 2 */
                caso = 2;
                j = i;
            }
        }
    }
   
    if (caso == 1) {
        /* Send the IP packet */
        eth_setHeader(pkt,arpTable[j].eth,ETH_IP_TYPE);
        eth_sendPkt(pkt,len);
        netbuff_release(&udp_txbuff, (void *)pkt);
        arpTable[j].used++;
        if (arpTable[j].used > ARP_MAX_USED) arpTable[j].used = ARP_MAX_USED;
    }
    else if (caso == 2) {
        /* Enqueue the packet until the ethernet address arrives */
        p = arpTable[j].pkt;
        while (p != NULL) {
            p1 = p;
            p = p1->next;
        }
        p1->next = pkt;
        ((PKT_NXT *)pkt)->next = NULL;
        ((PKT_NXT *)pkt)->len = len;
#ifdef __ARP_DBG__
sprintf(msg,"Pacchetto : %lp accodato\n",pkt);
win_puts(&w1,msg);
#endif
    }
    else {
        /* Search for a free entry in the ARP table...*/
        j = -1;
        while (j == -1) {
            j = arp_req(dest);
            if (j == -1) {
                cprintf("FULL!!!\n");
                kern_raise(XARP_TABLE_FULL,NIL);
            }
        }
        /*...fill it...*/
        arpTable[j].pkt = pkt;
        ((PKT_NXT *)arpTable[j].pkt)->next = NULL;
        ((PKT_NXT *)pkt)->len = len;
#ifdef __ARP_DBG__
sprintf(msg,"Pacchetto : %lp accodato\n",pkt);
win_puts(&w1,msg);
#endif
        /*...and send the request!!! */
        arp_sendRequest(j);
    }
    sem_post(&arpMutex);
}

/* ARP packet received CallBack*/
void arp_server_recv(void *pk)
{
    ARP_PKT *pkt,*rpkt;
    PKT_NXT *p1,*q1;
    int len;
    int i,j;
#ifdef __ARP_DBG__
char msg[100];
#endif
    BYTE found;
 
        pkt = (ARP_PKT *)eth_getFDB(pk);

#if 0
        {int ii;
                        cprintf("Arp PKT...\n");
cprintf ("source...");
for (ii=0; ii<4; ii++) cprintf("%d ", pkt->sip.ad[ii]);
cprintf ("\ndest...");
for (ii=0; ii<4; ii++) cprintf("%d ", pkt->tip.ad[ii]);
        }
#endif

        /* Check if the packet is directed to this host...*/
        if (ip_compAddr(pkt->tip,myIpAddr)) {
            sem_xwait(&arpMutex, 1, BLOCK);
            /* 1 : Search an entry with his IP address */
            found = FALSE;
            for (i = 0; (i < ARP_MAX_ENTRIES) && !found; i++) {
                if ((arpTable[i].valid != FALSE) &&
                        ip_compAddr(arpTable[i].ip,pkt->sip)) {
                    setEthAddr(arpTable[i].eth,pkt->sha);
                    found = TRUE;
                }
            }
            /*
               If there isn't any, fill a new entry (if the table is
               not full)
            */

            if (!found) {
                for (i = 0; (i < ARP_MAX_ENTRIES) && !found; i++)
                    if (arpTable[i].valid == FALSE) {
                        j = i;
                        found = TRUE;
                    }
                if (found) {
                    setIpAddr(arpTable[j].ip,pkt->sip);
                    setEthAddr(arpTable[j].eth,pkt->sha);
                    arpTable[j].valid = TRUE;
                }
            }
            /* If it is a request, send the reply */
            if (ntohs(pkt->operation) == ARP_REQUEST) {
                rpkt = (ARP_PKT *)eth_getFDB(reply);
                rpkt->htype = htons(ARP_ETH_TYPE);
                rpkt->ptype = htons(ARP_IP_TYPE);
                rpkt->hlen = sizeof(struct eth_addr);
                rpkt->plen = sizeof(IP_ADDR);
                rpkt->operation = htons(ARP_REPLY);
                setEthAddr(rpkt->sha,myEthAddr);
                setIpAddr(rpkt->sip,myIpAddr);
                setEthAddr(rpkt->tha,pkt->sha);
                setIpAddr(rpkt->tip,pkt->sip);
                eth_setHeader(reply, pkt->sha, ETH_ARP_TYPE);
                eth_sendPkt(reply,sizeof(ARP_PKT));
            }
            /* If it is a reply, search for his pending request */
            else {
                for (i = 0; i < ARP_MAX_ENTRIES; i++) {
                    if ((arpTable[i].valid == PENDING) &&
                        ip_compAddr(arpTable[i].ip,pkt->sip)) {
                            /*
                               the eth field in the ARP table was
                               filled previously
                            */

                            arpTable[i].valid = TRUE;
                            /* Send pending packets */
                            p1 = (PKT_NXT *)arpTable[i].pkt;
                            while (p1 != NULL) {
                                q1 = p1;
                                p1 = q1->next;
                                len = q1->len;
                                eth_setHeader((struct ETH_HEADER *)q1,arpTable[i].eth,ETH_IP_TYPE);
                                eth_sendPkt(q1,len);
                                netbuff_release(&udp_txbuff, (void *)q1);
#ifdef __ARP_DBG__
sprintf(msg,"Pacchetto : %lp inviato\n",q1);
win_puts(&w1,msg);
#endif
                            }
                    }
                }
            }
            sem_post(&arpMutex);
        }
}

void arp_init(char *localAddr)
{
    int i;
    PID s;
    SOFT_TASK_MODEL m;

    struct sigaction action;

    if (!arpIsInstalled) {
        for (i = 0; i < ARP_MAX_ENTRIES; i++) arpTable[i].valid = FALSE;

        /* ARP table mutex semaphore */
        sem_init(&arpMutex, 0, 1);

        netbuff_init(&arp_txbuff, 1, ARP_LEN);

        ip_str2addr(localAddr,&myIpAddr);

        eth_getAddress(&myEthAddr);

        /* Retry task */
        soft_task_default_model(m);
        soft_task_def_wcet(m,1000);
        soft_task_def_period(m,1000000);
        soft_task_def_met(m,1000);
        soft_task_def_periodic(m);
        soft_task_def_system(m);
        soft_task_def_nokill(m);
        s = task_create("ArpRetry",arp_retry,&m,NULL);
        if (s == NIL) {
            kern_printf("Cannot create ArpRetry\n");
            sys_end();
            l1_exit(-1);
        }

        eth_setProtocol(ETH_ARP_TYPE,arp_server_recv);
        eth_str2Addr("FF:FF:FF:FF:FF:FF",&broadcast);
        eth_str2Addr("00:00:00:00:00:00",&nulladdr);

//      for (i = ARP_ERROR_BASE; i <= XARP_TABLE_FULL; i++)
//          exc_set(i, arp_exc);
        action.sa_flags = 0;
        action.sa_handler = arp_exc;
        sigfillset(&action.sa_mask); /* we block all the other signals... */

        if (sigaction(SIGARPFULL, &action, NULL) == -1) {
          perror("Error initializing ARP signal...");
          sys_abort(AARPFULL);
        }

        task_activate(s);
        arpIsInstalled = TRUE;
    } else cprintf("Arp: already installed!!!!");
}