Subversion Repositories shark

Rev

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: udpip.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 and Giuseppe Lipari
 *
 * 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:        1/12/97                                         */

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

/*
   UDP and IP layers. IP doesn't support fragmented packets (fragmentation
   isn't Real-Time!!!). UDP allows to fill some entries of the ARP table
   at the moment of the creation of a socket (see udp_bind).
*/


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

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

/*
   UDP-buffers number and dimension (UDP-buffers are provided by NetBuff
   module
*/

#define UDP_RX_BUFFERS  4
#define UDP_TX_BUFFERS  4
#define UDP_MAX_LEN     1000
#define UDP_MAX_HANDLES 4

/* Only 4 debug */
extern int netlev;

int ipIsInstalled = FALSE;
extern struct ip_addr myIpAddr;
struct ip_table ipTable[IP_MAX_ENTRIES];
extern ARP_TABLE arpTable[ARP_MAX_ENTRIES];

int udpIsInstalled = FALSE;
struct netbuff udp_txbuff;
struct netbuff udp_rxbuff;
struct udp_table udpTable[UDP_MAX_HANDLES];


IP_ADDR *ip_getAddr()
{
    return &myIpAddr;
}

/* TRUE if the IP addresses ip1 and ip2 are equal, FALSE otherwise */
int ip_compAddr(IP_ADDR ip1, IP_ADDR ip2)
{
    int i;
   
    for (i=0; i < 4; i++)
        if (ip1.ad[i] != ip2.ad[i]) return FALSE;
    return TRUE;
}

/* traslate an IP address from text string to 4 bytes */
int ip_str2addr(char *a, IP_ADDR *ip)
{
    int ad[6];
    int i,j;
    char c;

    i = 0;
    for(j = 0; j < 4; j++) {
        ad[j] = 0;
        while((a[i] != '.') && (a[i] != 0)) {
            c = a[i++];
            if (c <= '9') c = c - '0';
            else c = c - 'A' + 10;
            ad[j] = ad[j] * 10 + c;
        }
        i++;
    }

    for (i=0; i<4; i++) ip->ad[i] = ad[i];
    return 1;
}

/* give the body of an IP packet */
void *ip_getFDB(void *pkt)
{
    return((void *)(((BYTE *)eth_getFDB(pkt)) + sizeof(IP_HEADER)));
}

/* received IP packet CallBack */
void ip_server_recv(void *pkt)
{
    IP_HEADER *iphd;
    WORD *ptcheck;
    WORD checksum,oldChk;
    int hlen,i;
    int done;
    char dummystr[50];
    BYTE flag;

    UDP_HEADER *udphd;
    struct pseudo_hd ph;
    WORD sum, old;
    WORD *p;
    UDP_MSG usermsg;
    void *b, *s;

/* debug...*/
netlev = 2;

    sprintf(dummystr, "Packet arrived!\n");
    iphd = (IP_HEADER *)eth_getFDB(pkt);
/* compute the checksum */
    ptcheck = (WORD *)iphd;
#ifdef __IP_DBG__
ip_print_header(iphd);
#endif
    hlen = getHlen(iphd->vers_hlen) * 2;
    checksum = oldChk = *ptcheck;
    ptcheck++;
    for (i=1; i<hlen; i++) {
        checksum += *ptcheck;
        if (checksum < oldChk) checksum ++;
        oldChk = checksum;
        ptcheck++;
    }
    if (checksum != 0xffff) {
/* wrong ChkSum */
#ifdef __IP_DBG__
sprintf(msg, "Wrong checksum! %x\n",checksum);
win_puts(&wip,msg);
#endif
    } else if (!ip_compAddr(iphd->dest,myIpAddr)) {
#ifdef __IP_DBG__
win_puts(&wip,"Packet not addressed to this host!\n");
#endif
    } else if (getFlags(ntohs(iphd->flags_frOffset)) & 0x01) {
#ifdef __IP_DBG__
/* fragment? */
win_puts(&wip,"Gotta a fragment!\n");
#endif
    } else if (getFrOffset(ntohs(iphd->flags_frOffset)) != 0) {
#ifdef __IP_DBG__
win_puts(&wip,"Gotta a fragment again!\n");
#endif
    } else {
        /* OK: the packet is good... */
        if (iphd->protocol != IP_UDP_TYPE) {
            /* It isn't an UDP packet */
            done = FALSE;
            i = 0;
            while ((i < IP_MAX_ENTRIES) && !done) {
                if (ipTable[i].protocol == iphd->protocol) done = TRUE;
                else i++;
            }
            if (!done) {
#ifdef __IP_DBG__
/* Unknown transport protocol */
win_puts(&wip,"Wrong protocol\n");
#endif
            } else {
                /* Call the correct transport protocol CallBack */
                ipTable[i].rfun((void *)((BYTE *)iphd + 4*getHlen(iphd->vers_hlen)));
            }
        } else {
            /* UDP packet */
netlev = 4;
            udphd = (UDP_HEADER *)(void *)((BYTE *)iphd + 4*getHlen(iphd->vers_hlen));
            /* compute the UDP checksum */
            for (i = 0; i < 4; i++) {
                ph.source.ad[i]  = iphd->source.ad[i];
                ph.dest.ad[i]  = iphd->dest.ad[i];
            }
            ph.zero = 0;
            ph.protocoll = IP_UDP_TYPE;
            ph.len = udphd->mlen;
            sum = 0;
            old = 0;
            p = (WORD *)&ph;
            for (i = 0; i < (sizeof(ph) >> 1); i++) {
                sum += p[i];
                if (sum < old) sum ++;
                old = sum;
            }
#ifdef __UDP_DBG__
sprintf(str,"half sum: %x\n",sum);
win_puts(&dbg,str);
#endif
            p = (WORD *)udphd;
            ((BYTE *)udphd)[ntohs(udphd->mlen)] = 0;
            for (i = 0; i < ((ntohs(udphd->mlen) + 1) >> 1); i++) {
                sum += p[i];
                if (sum < old) sum ++;
                old = sum;
            }
#ifdef __UDP_DBG__
win_puts(&dbg,"UDP packet received!!!\n");
sprintf(str,"s_port: %x\n",udphd->s_port);
win_puts(&dbg,str);
sprintf(str,"d_port: %x\n",udphd->d_port);
win_puts(&dbg,str);
sprintf(str,"m_len: %x %d\n",udphd->mlen, ntohs(udphd->mlen));
win_puts(&dbg,str);
sprintf(str,"checksum: %x\n",udphd->checksum);
win_puts(&dbg,str);
sprintf(str,"mysum: %x \n", sum);
win_puts(&dbg,str);
#endif
            if(sum != 0xFFFF) {
                /* Wrong UDP ChkSum */
                cprintf("Error %x!!!!\n",sum);
                kern_raise(XUDP_BADCHK_EXC,exec_shadow);
            } else {
                done = FALSE;
                i = 0;
                /* searching for the destination socket...*/
                while((i < UDP_MAX_HANDLES) && !done) {
                    if ((udpTable[i].valid == TRUE) &&
                        (udpTable[i].port == ntohs(udphd->d_port)))
                            done = TRUE;
                    else i++;
                }
                if (done) {
                /*...found! */
                s = (void *)(((BYTE *)udphd) + sizeof(UDP_HEADER));
                    if (udpTable[i].notify == TRUE) {
                        /* notify function associated to the socket: call it */
                        udpTable[i].notify_fun(ntohs(udphd->mlen) - sizeof(UDP_HEADER),
                                                s, udpTable[i].notify_par);
                    } else {
                        /* otherwise, send the packet to the correct port */
                        if((b = netbuff_get(&udp_rxbuff, NON_BLOCK)) != 0) {
                            memcpy(b,s,ntohs(udphd->mlen) - sizeof(UDP_HEADER) + 1);
                            setIpAddr(usermsg.addr.s_addr, iphd->source);
                            usermsg.addr.s_port = ntohs(udphd->s_port);
                            usermsg.mlen = ntohs(udphd->mlen) - sizeof(UDP_HEADER);
                            usermsg.buff = b;
                            flag = port_send(udpTable[i].hport,&usermsg,NON_BLOCK);
                            if (!flag) {
                                netbuff_release(&udp_rxbuff, b);
#ifdef __UDP_DBG__
win_puts(&dbg,"Port is filled up       ");
#endif
                            }
                        }
                    }
                } else {
#ifdef __UDP_DBG__
win_puts(&dbg,"Port not found       ");
#endif
                }
            }
        }
    }
}

/* Send an IP packet */
void ip_send(IP_ADDR dest, void *pkt, WORD len)
{
    static WORD ip_ident = 0;
    IP_HEADER *iphd;
    WORD check = 0, oldCheck = 0;
    int i, done;
    WORD *pt;
#ifdef __IP_DBG__
char msg[50];
#endif
   
    iphd = (IP_HEADER *)eth_getFDB(pkt);
    iphd->vers_hlen = (4 << 4) + 5;
    iphd->servType = 8;
    iphd->lenght = htons(len + sizeof(IP_HEADER));
    iphd->ident = htons(ip_ident++);
    iphd->flags_frOffset = 0;
    iphd->ttl = 10;
    iphd->protocol = IP_UDP_TYPE;
    iphd->headChecksum = 0;
    iphd->source = myIpAddr;
    iphd->dest = dest;

    /* Compute the checksum */
    pt = (WORD *)iphd;
    check = oldCheck = *pt;
    pt++;
    for (i = 1; i < 10; i++) {
        check += *pt;
        if (oldCheck > check) check++;
        oldCheck = check;
        pt++;
    }
    check = ~check;
    iphd->headChecksum = check;
#ifdef __IP_DBG__
sprintf(msg,"serv type : %d\n",iphd->servType);
win_puts(&wip,msg);
#endif
    /* Is the destination ethernet address in the ARP table? */
    i = 0; done = 0;
    while (i < ARP_MAX_ENTRIES && !done)
        if (arpTable[i].valid == TRUE) {
            if (ip_compAddr(dest,arpTable[i].ip)) {
                done = TRUE;
            } else i++;
        } else i++;
    if (done == FALSE) {
        /* No: call ARP to get the ethernet address */
        arp_send(pkt,dest,len + sizeof(IP_HEADER));
    }
    else {
        /* Yes: directly send the packet */
        eth_setHeader(pkt,arpTable[i].eth,ETH_IP_TYPE);
        eth_sendPkt(pkt,len + sizeof(IP_HEADER));
        netbuff_release(&udp_txbuff, (void *)pkt);
        arpTable[i].used++;
        if (arpTable[i].used > ARP_MAX_USED) arpTable[i].used = ARP_MAX_USED;
    }
}

/* let IP manage a new transport protocol */
int ip_setProtocol(BYTE proto, void (*recv)(void *m))
{
    BYTE done, i;

    i = 0; done = 0;
    while (i < IP_MAX_ENTRIES && !done)
        if (ipTable[i].rfun == NULL) done = TRUE;
        else i++;
    if (!done) return FALSE;
    else {
        ipTable[i].protocol = proto;
        ipTable[i].rfun = recv;
    }
    return TRUE;
}

//int ip_error(int code)
//{
//    cprintf("IP error\n");
/*    cprintf("Code [%d]\nCause : %s",code,ip_error_msg[code-IP_ERROR_BASE]);*/
//    return(0);
//}

/* Initialize the IP layer: it also call the ARP initialization function */
void ip_init(void *localAddr)
{
    int i;

    if (!ipIsInstalled) {
        arp_init(localAddr);
        //exc_set(IP_INIT_ERROR,ip_error);
        for (i=0; i < IP_MAX_ENTRIES; i++) ipTable[i].rfun = NULL;

        eth_setProtocol(ETH_IP_TYPE, ip_server_recv);
        ipIsInstalled = TRUE;
    } else cprintf("IP: already installed!!!\n");
}

/* Receive an UDP packet from a socket */
int udp_recvfrom(int s, char *buff, UDP_ADDR *from)
{
    UDP_MSG u;

    port_receive(udpTable[s].pport,&u,BLOCK);
    memcpy(buff,u.buff,u.mlen);
    netbuff_release(&udp_rxbuff, u.buff);
    *from = u.addr;
    return (u.mlen);
}

/* Associate a notify function to a socket */
int udp_notify(int s, int (*f)(int len, BYTE *buff, void *p), void *p)
{
    if (f == NULL) {
        udpTable[s].notify = FALSE;
        return 1;
    }

    if (udpTable[s].valid != TRUE) return -1;
    udpTable[s].notify = TRUE;
    udpTable[s].notify_fun = f;
    udpTable[s].notify_par = p;
    return 1;
}

/* Create a new socket binding it to a specified IP port */
int udp_bind(UDP_ADDR *local, IP_ADDR *bindlist)
{
    int i, j;
    BYTE done;
    char str[30];

    /* Search for a free entry in the socket table */
    i = 0; done = FALSE;
    while ((i < UDP_MAX_HANDLES) && !done) {
        kern_cli();
        if ((udpTable[i].valid == FALSE)){
            done = TRUE;
            udpTable[i].valid = 2;
        } else i++;
        kern_sti();
    }
    /* No free entries: bind fail! */
    if (!done) return -1;
    /* Create a receive port for the socket */
    udpTable[i].port = local->s_port;
    sprintf(str,"UDP%d",i);
    udpTable[i].hport = port_create(str,sizeof(UDP_MSG),4,STREAM,WRITE);
    udpTable[i].pport = port_connect(str,sizeof(UDP_MSG),STREAM,READ);
    udpTable[i].valid = TRUE;
   /*
   Request for the ethernet addresses associated to the IP addressed
   given in the bindlist
   */

    if (bindlist != NULL) {
        while (*(int*)bindlist != 0) {
            j = arp_req(*bindlist);
            arp_sendRequest(j);
            bindlist += sizeof(IP_ADDR);
        }
    }

    return i;
}

/* Send an UDP packet */
int udp_sendto(int s, char *buff, int nbytes, UDP_ADDR *to)
{
    void *pkt;
    UDP_HEADER *udphd;
    char *msg;
#ifdef __UDP_DBG__
static int num_pack = 0;
char str[50];
#endif
    WORD sum, old;
    int i;
    struct pseudo_hd ph;
    WORD *p;
    IP_ADDR *source;

    pkt = netbuff_get(&udp_txbuff, BLOCK);
    udphd = (UDP_HEADER *)ip_getFDB(pkt);
    udphd->s_port = htons(udpTable[s].port);
    udphd->d_port = htons(to->s_port);
    udphd->mlen = htons((WORD)nbytes + sizeof(UDP_HEADER));
    msg = (char *)(((BYTE *)udphd) + sizeof(UDP_HEADER));
    if (nbytes > UDP_MAX_LEN) nbytes = UDP_MAX_LEN;
    memcpy(msg,buff,nbytes);

    source = ip_getAddr();
    /* Compute the CheckSum */
    udphd->checksum = 0;
    for (i = 0; i < 4; i++) {
        ph.source.ad[i]  = source->ad[i];
        ph.dest.ad[i]  = to->s_addr.ad[i];
    }
    ph.zero = 0;
    ph.protocoll = 17;
    ph.len = udphd->mlen;
    sum = 0; old = 0;
    p = (WORD *)&ph;
    for (i = 0; i < (sizeof(ph) >> 1); i++) {
        sum += p[i];
        if (sum < old) sum ++;
        old = sum;
    }
    p = (WORD *)udphd;
    ((BYTE *)udphd)[ntohs(udphd->mlen)] = 0;
    for (i = 0; i < ((ntohs(udphd->mlen) + 1) >> 1); i++) {
        sum += p[i];
        if (sum < old) sum++;
        old = sum;
    }
    udphd->checksum = ~sum;
     
    ip_send(to->s_addr, pkt, ((WORD)nbytes + sizeof(UDP_HEADER)));
#ifdef __UDP_DBG__
sprintf(str,"Packet sent %d;  ",num_pack++);
win_puts(&dbg,str);
#endif
   
    return nbytes;
}

void udp_init(void *dummy)
{
    int i;

    if (!udpIsInstalled) {
        netbuff_init(&udp_rxbuff, UDP_RX_BUFFERS, UDP_MAX_LEN);
        netbuff_init(&udp_txbuff, UDP_TX_BUFFERS, ETH_MAX_LEN);

        for (i = 0; i < UDP_MAX_HANDLES; i++) {
            udpTable[i].valid = FALSE;
            udpTable[i].notify = FALSE;
        }
        udpIsInstalled = TRUE;
    } else cprintf("UDP: Already installed!!!\n");
}