#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(lendport); 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); } }