325 lines
7.4 KiB
C
325 lines
7.4 KiB
C
#include "types.h"
|
|
#include "param.h"
|
|
#include "memlayout.h"
|
|
#include "riscv.h"
|
|
#include "spinlock.h"
|
|
#include "proc.h"
|
|
#include "defs.h"
|
|
#include "fs.h"
|
|
#include "sleeplock.h"
|
|
#include "file.h"
|
|
#include "net.h"
|
|
|
|
// xv6's ethernet and IP addresses
|
|
static uint8 local_mac[ETHADDR_LEN] = { 0x52, 0x54, 0x00, 0x12, 0x34, 0x56 };
|
|
static uint32 local_ip = MAKE_IP_ADDR(10, 0, 2, 15);
|
|
|
|
// qemu host's ethernet address.
|
|
static uint8 host_mac[ETHADDR_LEN] = { 0x52, 0x55, 0x0a, 0x00, 0x02, 0x02 };
|
|
|
|
static struct spinlock netlock;
|
|
|
|
void
|
|
netinit(void)
|
|
{
|
|
initlock(&netlock, "netlock");
|
|
}
|
|
#define MAXPORT 10000+1
|
|
#define MAXCNT 16
|
|
static void *udpq[MAXPORT][MAXCNT];
|
|
static char udph[MAXPORT],udpt[MAXPORT],udpc[MAXPORT];
|
|
static int pused[MAXPORT];
|
|
|
|
//
|
|
// bind(int port)
|
|
// prepare to receive UDP packets address to the port,
|
|
// i.e. allocate any queues &c needed.
|
|
//
|
|
uint64
|
|
sys_bind(void)
|
|
{
|
|
//
|
|
// Your code here.
|
|
//
|
|
short port;
|
|
argint(0, (int *)&port);
|
|
pused[port] = 1;
|
|
return -1;
|
|
}
|
|
|
|
//
|
|
// unbind(int port)
|
|
// release any resources previously created by bind(port);
|
|
// from now on UDP packets addressed to port should be dropped.
|
|
//
|
|
uint64
|
|
sys_unbind(void)
|
|
{
|
|
//
|
|
// Optional: Your code here.
|
|
//
|
|
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// recv(int dport, int *src, short *sport, char *buf, int maxlen)
|
|
// if there's a received UDP packet already queued that was
|
|
// addressed to dport, then return it.
|
|
// otherwise wait for such a packet.
|
|
//
|
|
// sets *src to the IP source address.
|
|
// sets *sport to the UDP source port.
|
|
// copies up to maxlen bytes of UDP payload to buf.
|
|
// returns the number of bytes copied,
|
|
// and -1 if there was an error.
|
|
//
|
|
// dport, *src, and *sport are host byte order.
|
|
// bind(dport) must previously have been called.
|
|
//
|
|
uint64
|
|
sys_recv(void)
|
|
{
|
|
//
|
|
// Your code here.
|
|
//
|
|
acquire(&netlock);
|
|
struct proc *p = myproc();
|
|
int dport;
|
|
uint64 src,sport,buf;
|
|
int maxlen;
|
|
argint(0, (int*)&dport);
|
|
argaddr(1, &src);
|
|
argaddr(2, &sport);
|
|
argaddr(3, &buf);
|
|
argint(4, &maxlen);
|
|
while(!udpc[dport]){
|
|
if(p->killed)
|
|
goto err;
|
|
sleep(&udpc[dport], &netlock);
|
|
if(p->killed)
|
|
goto err;
|
|
}
|
|
struct eth *eth=(struct eth *)udpq[dport][(int)udph[dport]];
|
|
struct ip *ip=(struct ip *)(eth+1);
|
|
struct udp *udp=(struct udp *)(ip+1);
|
|
const int srcbuf=ntohl(ip->ip_src);
|
|
const short sportbuf=ntohs(udp->sport);
|
|
if(copyout(p->pagetable,src,(char *)&srcbuf,sizeof(int))<0){
|
|
goto err;
|
|
}
|
|
if(copyout(p->pagetable,sport,(char *)&sportbuf,sizeof(short))<0){
|
|
goto err;
|
|
}
|
|
char *payload=(char *)(udp+1);
|
|
const int len=ntohs(udp->ulen)-sizeof(struct udp);
|
|
|
|
if(len>maxlen){
|
|
goto err;
|
|
}
|
|
if(copyout(p->pagetable,buf,payload,len)<0){
|
|
goto err;
|
|
}
|
|
kfree(eth);
|
|
--udpc[dport];
|
|
udph[dport]=(udph[dport]+1)%MAXCNT;
|
|
release(&netlock);
|
|
return len;
|
|
err:
|
|
release(&netlock);
|
|
return -1;
|
|
}
|
|
|
|
// This code is lifted from FreeBSD's ping.c, and is copyright by the Regents
|
|
// of the University of California.
|
|
static unsigned short
|
|
in_cksum(const unsigned char *addr, int len)
|
|
{
|
|
int nleft = len;
|
|
const unsigned short *w = (const unsigned short *)addr;
|
|
unsigned int sum = 0;
|
|
unsigned short answer = 0;
|
|
|
|
/*
|
|
* Our algorithm is simple, using a 32 bit accumulator (sum), we add
|
|
* sequential 16 bit words to it, and at the end, fold back all the
|
|
* carry bits from the top 16 bits into the lower 16 bits.
|
|
*/
|
|
while (nleft > 1) {
|
|
sum += *w++;
|
|
nleft -= 2;
|
|
}
|
|
|
|
/* mop up an odd byte, if necessary */
|
|
if (nleft == 1) {
|
|
*(unsigned char *)(&answer) = *(const unsigned char *)w;
|
|
sum += answer;
|
|
}
|
|
|
|
/* add back carry outs from top 16 bits to low 16 bits */
|
|
sum = (sum & 0xffff) + (sum >> 16);
|
|
sum += (sum >> 16);
|
|
/* guaranteed now that the lower 16 bits of sum are correct */
|
|
|
|
answer = ~sum; /* truncate to 16 bits */
|
|
return answer;
|
|
}
|
|
|
|
//
|
|
// send(int sport, int dst, int dport, char *buf, int len)
|
|
//
|
|
|
|
uint64
|
|
sys_send(void)
|
|
{
|
|
struct proc *p = myproc();
|
|
int sport;
|
|
int dst;
|
|
int dport;
|
|
uint64 bufaddr;
|
|
int len;
|
|
|
|
argint(0, &sport);
|
|
argint(1, &dst);
|
|
argint(2, &dport);
|
|
argaddr(3, &bufaddr);
|
|
argint(4, &len);
|
|
int total = len + sizeof(struct eth) + sizeof(struct ip) + sizeof(struct udp);
|
|
if(total > PGSIZE)
|
|
return -1;
|
|
char *buf = kalloc();
|
|
if(buf == 0){
|
|
printf("sys_send: kalloc failed\n");
|
|
return -1;
|
|
}
|
|
memset(buf, 0, PGSIZE);
|
|
|
|
struct eth *eth = (struct eth *) buf;
|
|
memmove(eth->dhost, host_mac, ETHADDR_LEN);
|
|
memmove(eth->shost, local_mac, ETHADDR_LEN);
|
|
eth->type = htons(ETHTYPE_IP);
|
|
|
|
struct ip *ip = (struct ip *)(eth + 1);
|
|
ip->ip_vhl = 0x45; // version 4, header length 4*5
|
|
ip->ip_tos = 0;
|
|
ip->ip_len = htons(sizeof(struct ip) + sizeof(struct udp) + len);
|
|
ip->ip_id = 0;
|
|
ip->ip_off = 0;
|
|
ip->ip_ttl = 100;
|
|
ip->ip_p = IPPROTO_UDP;
|
|
ip->ip_src = htonl(local_ip);
|
|
ip->ip_dst = htonl(dst);
|
|
ip->ip_sum = in_cksum((unsigned char *)ip, sizeof(*ip));
|
|
|
|
struct udp *udp = (struct udp *)(ip + 1);
|
|
udp->sport = htons(sport);
|
|
udp->dport = htons(dport);
|
|
udp->ulen = htons(len + sizeof(struct udp));
|
|
|
|
char *payload = (char *)(udp + 1);
|
|
if(copyin(p->pagetable, payload, bufaddr, len) < 0){
|
|
kfree(buf);
|
|
printf("send: copyin failed\n");
|
|
return -1;
|
|
}
|
|
|
|
e1000_transmit(buf, total);
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
void
|
|
ip_rx(char *buf, int len)
|
|
{
|
|
// don't delete this printf; make grade depends on it.
|
|
static int seen_ip = 0;
|
|
if(seen_ip == 0)
|
|
printf("ip_rx: received an IP packet\n");
|
|
seen_ip = 1;
|
|
//
|
|
// Your code here.
|
|
//
|
|
if(len<sizeof(struct eth)+sizeof(struct ip)+sizeof(struct udp))
|
|
return;
|
|
struct eth *eth=(struct eth *)buf;
|
|
struct ip *ip=(struct ip *)(eth+1);
|
|
struct udp *udp=(struct udp *)(ip+1);
|
|
const short dport=ntohs(udp->dport);
|
|
if(!pused[dport])
|
|
goto abandon;
|
|
if(udpc[dport]==MAXCNT)
|
|
goto abandon;
|
|
udpq[dport][(int)udpt[dport]]=buf;//tail
|
|
udpt[dport]=(udpt[dport]+1)%MAXCNT;
|
|
++udpc[dport];
|
|
wakeup(&udpc[dport]);
|
|
return;
|
|
abandon:
|
|
kfree(buf);
|
|
}
|
|
|
|
//
|
|
// send an ARP reply packet to tell qemu to map
|
|
// xv6's ip address to its ethernet address.
|
|
// this is the bare minimum needed to persuade
|
|
// qemu to send IP packets to xv6; the real ARP
|
|
// protocol is more complex.
|
|
//
|
|
void
|
|
arp_rx(char *inbuf)
|
|
{
|
|
static int seen_arp = 0;
|
|
|
|
if(seen_arp){
|
|
kfree(inbuf);
|
|
return;
|
|
}
|
|
printf("arp_rx: received an ARP packet\n");
|
|
seen_arp = 1;
|
|
|
|
struct eth *ineth = (struct eth *) inbuf;
|
|
struct arp *inarp = (struct arp *) (ineth + 1);
|
|
|
|
char *buf = kalloc();
|
|
if(buf == 0)
|
|
panic("send_arp_reply");
|
|
|
|
struct eth *eth = (struct eth *) buf;
|
|
memmove(eth->dhost, ineth->shost, ETHADDR_LEN); // ethernet destination = query source
|
|
memmove(eth->shost, local_mac, ETHADDR_LEN); // ethernet source = xv6's ethernet address
|
|
eth->type = htons(ETHTYPE_ARP);
|
|
|
|
struct arp *arp = (struct arp *)(eth + 1);
|
|
arp->hrd = htons(ARP_HRD_ETHER);
|
|
arp->pro = htons(ETHTYPE_IP);
|
|
arp->hln = ETHADDR_LEN;
|
|
arp->pln = sizeof(uint32);
|
|
arp->op = htons(ARP_OP_REPLY);
|
|
|
|
memmove(arp->sha, local_mac, ETHADDR_LEN);
|
|
arp->sip = htonl(local_ip);
|
|
memmove(arp->tha, ineth->shost, ETHADDR_LEN);
|
|
arp->tip = inarp->sip;
|
|
|
|
e1000_transmit(buf, sizeof(*eth) + sizeof(*arp));
|
|
|
|
kfree(inbuf);
|
|
}
|
|
|
|
void
|
|
net_rx(char *buf, int len)
|
|
{
|
|
struct eth *eth = (struct eth *) buf;
|
|
|
|
if(len >= sizeof(struct eth) + sizeof(struct arp) &&
|
|
ntohs(eth->type) == ETHTYPE_ARP){
|
|
arp_rx(buf);
|
|
} else if(len >= sizeof(struct eth) + sizeof(struct ip) &&
|
|
ntohs(eth->type) == ETHTYPE_IP){
|
|
ip_rx(buf, len);
|
|
} else {
|
|
kfree(buf);
|
|
}
|
|
}
|