Go to most recent revision |
Blame |
Compare with Previous |
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.5 2004-05-11 14:30:51 giacomo Exp $
File: $File$
Revision: $Revision: 1.5 $
Last update: $Date: 2004-05-11 14:30:51 $
------------
**/
/*
* 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"
//#define __UDP_DBG__
//#define __IP_DBG__
#define UDP_INFO "[UDP] "
#define IP_INFO "[IP] "
/*
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
];
IP_ADDR IPbroadcastaddress
;
int udpIsInstalled
= FALSE
;
struct netbuff udp_txbuff
;
struct netbuff udp_rxbuff
;
struct udp_table udpTable
[UDP_MAX_HANDLES
];
struct eth_addr broadcast
;
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__
printk
(KERN_DEBUG IP_INFO
"Wrong checksum: %x.\n", checksum
);
#endif
} else if (!ip_compAddr
(iphd
->dest
,myIpAddr
)) {
#ifdef __IP_DBG__
printk
(KERN_DEBUG IP_INFO
"Packet not addressed to this host.\n");
#endif
} else if (getFlags
(ntohs
(iphd
->flags_frOffset
)) & 0x01) {
#ifdef __IP_DBG__
/* fragment? */
printk
(KERN_DEBUG IP_INFO
"Gotta a fragment!\n");
#endif
} else if (getFrOffset
(ntohs
(iphd
->flags_frOffset
)) != 0) {
#ifdef __IP_DBG__
printk
(KERN_DEBUG IP_INFO
"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 */
printk
(KERN_DEBUG IP_INFO
"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__
printk
(KERN_DEBUG UDP_INFO
"Half sum: %x\n",sum
);
#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__
printk
(KERN_DEBUG UDP_INFO
"Packet received.\n");
printk
(KERN_DEBUG UDP_INFO
"s_port: %x\n",udphd
->s_port
);
printk
(KERN_DEBUG UDP_INFO
"d_port: %x\n",udphd
->d_port
);
printk
(KERN_DEBUG UDP_INFO
"m_len: %x %d\n",udphd
->mlen
, ntohs
(udphd
->mlen
));
printk
(KERN_DEBUG UDP_INFO
"checksum: %x\n",udphd
->checksum
);
printk
(KERN_DEBUG UDP_INFO
"mysum: %x \n", sum
);
#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__
printk
(KERN_DEBUG
"Port is filled up.\n");
#endif
}
}
}
} else {
#ifdef __UDP_DBG__
printk
(KERN_DEBUG UDP_INFO
"Port not found.\n");
#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
;
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__
printk
(KERN_DEBUG IP_INFO
"Serv type : %d\n", iphd
->servType
);
#endif
/* Is the destination IP address the broadcast address?*/
if (ip_compAddr
(dest
,IPbroadcastaddress
)) {
/* Send the packet*/
eth_setHeader
(pkt
,broadcast
,ETH_IP_TYPE
);
eth_sendPkt
(pkt
,len
+ sizeof(IP_HEADER
));
netbuff_release
(&udp_txbuff
, (void *)pkt
);
} else {
/* 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, pass a struct ip_params* */
void ip_init
(void *p
)
{
int i
;
if (!ipIsInstalled
) {
arp_init
(((struct ip_params
*)p
)->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
);
ip_str2addr
(((struct ip_params
*)p
)->broadcastAddr
,&IPbroadcastaddress
);
ipIsInstalled
= TRUE
;
eth_str2Addr
("FF:FF:FF:FF:FF:FF",&broadcast
);
} else
cprintf
("IP: already installed.\n");
}
/* Receive an UDP packet from a socket */
int udp_recvfrom
(int s
, void *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) {
/* Ignore broadcast IP address */
if (!ip_compAddr
(*bindlist
,IPbroadcastaddress
)) {
j
= arp_req
(*bindlist
);
arp_sendRequest
(j
);
}
bindlist
++;
}
}
return i
;
}
/* Send an UDP packet */
int udp_sendto
(int s
, void *buff
, int nbytes
, UDP_ADDR
*to
)
{
void *pkt
;
UDP_HEADER
*udphd
;
char *msg
;
#ifdef __UDP_DBG__
static int num_pack
= 0;
#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__
printk
(KERN_DEBUG UDP_INFO
"Packets sent: %d.\n", num_pack
++);
#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
printk
(KERN_WARNING UDP_INFO
": Already installed.\n");
}