#include "kernel/types.h" #include "kernel/net.h" #include "kernel/stat.h" #include "user/user.h" // // send a UDP packet to the localhost (outside of qemu), // and receive a response. // static void ping(uint16 sport, uint16 dport, int attempts) { int fd; char *obuf = "a message from xv6!"; uint32 dst; // 10.0.2.2, which qemu remaps to the external host, // i.e. the machine you're running qemu on. dst = (10 << 24) | (0 << 16) | (2 << 8) | (2 << 0); // you can send a UDP packet to any Internet address // by using a different dst. if((fd = connect(dst, sport, dport)) < 0){ fprintf(2, "ping: connect() failed\n"); exit(1); } for(int i = 0; i < attempts; i++) { if(write(fd, obuf, strlen(obuf)) < 0){ fprintf(2, "ping: send() failed\n"); exit(1); } } char ibuf[128]; int cc = read(fd, ibuf, sizeof(ibuf)-1); if(cc < 0){ fprintf(2, "ping: recv() failed\n"); exit(1); } close(fd); ibuf[cc] = '\0'; if(strcmp(ibuf, "this is the host!") != 0){ fprintf(2, "ping didn't receive correct payload\n"); exit(1); } } // Encode a DNS name static void encode_qname(char *qn, char *host) { char *l = host; for(char *c = host; c < host+strlen(host)+1; c++) { if(*c == '.') { *qn++ = (char) (c-l); for(char *d = l; d < c; d++) { *qn++ = *d; } l = c+1; // skip . } } *qn = '\0'; } // Decode a DNS name static void decode_qname(char *qn, int max) { char *qnMax = qn + max; while(1){ if(qn >= qnMax){ printf("invalid DNS reply\n"); exit(1); } int l = *qn; if(l == 0) break; for(int i = 0; i < l; i++) { *qn = *(qn+1); qn++; } *qn++ = '.'; } } // Make a DNS request static int dns_req(uint8 *obuf) { int len = 0; struct dns *hdr = (struct dns *) obuf; hdr->id = htons(6828); hdr->rd = 1; hdr->qdcount = htons(1); len += sizeof(struct dns); // qname part of question char *qname = (char *) (obuf + sizeof(struct dns)); char *s = "pdos.csail.mit.edu."; encode_qname(qname, s); len += strlen(qname) + 1; // constants part of question struct dns_question *h = (struct dns_question *) (qname+strlen(qname)+1); h->qtype = htons(0x1); h->qclass = htons(0x1); len += sizeof(struct dns_question); return len; } // Process DNS response static void dns_rep(uint8 *ibuf, int cc) { struct dns *hdr = (struct dns *) ibuf; int len; char *qname = 0; int record = 0; if(cc < sizeof(struct dns)){ printf("DNS reply too short\n"); exit(1); } if(!hdr->qr) { printf("Not a DNS reply for %d\n", ntohs(hdr->id)); exit(1); } if(hdr->id != htons(6828)){ printf("DNS wrong id: %d\n", ntohs(hdr->id)); exit(1); } if(hdr->rcode != 0) { printf("DNS rcode error: %x\n", hdr->rcode); exit(1); } //printf("qdcount: %x\n", ntohs(hdr->qdcount)); //printf("ancount: %x\n", ntohs(hdr->ancount)); //printf("nscount: %x\n", ntohs(hdr->nscount)); //printf("arcount: %x\n", ntohs(hdr->arcount)); len = sizeof(struct dns); for(int i =0; i < ntohs(hdr->qdcount); i++) { char *qn = (char *) (ibuf+len); qname = qn; decode_qname(qn, cc - len); len += strlen(qn)+1; len += sizeof(struct dns_question); } for(int i = 0; i < ntohs(hdr->ancount); i++) { if(len >= cc){ printf("invalid DNS reply\n"); exit(1); } char *qn = (char *) (ibuf+len); if((int) qn[0] > 63) { // compression? qn = (char *)(ibuf+qn[1]); len += 2; } else { decode_qname(qn, cc - len); len += strlen(qn)+1; } struct dns_data *d = (struct dns_data *) (ibuf+len); len += sizeof(struct dns_data); //printf("type %d ttl %d len %d\n", ntohs(d->type), ntohl(d->ttl), ntohs(d->len)); if(ntohs(d->type) == ARECORD && ntohs(d->len) == 4) { record = 1; printf("DNS arecord for %s is ", qname ? qname : "" ); uint8 *ip = (ibuf+len); printf("%d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]); if(ip[0] != 128 || ip[1] != 52 || ip[2] != 129 || ip[3] != 126) { printf("wrong ip address"); exit(1); } len += 4; } } // needed for DNS servers with EDNS support for(int i = 0; i < ntohs(hdr->arcount); i++) { char *qn = (char *) (ibuf+len); if(*qn != 0) { printf("invalid name for EDNS\n"); exit(1); } len += 1; struct dns_data *d = (struct dns_data *) (ibuf+len); len += sizeof(struct dns_data); if(ntohs(d->type) != 41) { printf("invalid type for EDNS\n"); exit(1); } len += ntohs(d->len); } if(len != cc) { printf("Processed %d data bytes but received %d\n", len, cc); exit(1); } if(!record) { printf("Didn't receive an arecord\n"); exit(1); } } static void dns() { #define N 1000 uint8 obuf[N]; uint8 ibuf[N]; uint32 dst; int fd; int len; memset(obuf, 0, N); memset(ibuf, 0, N); // 8.8.8.8: google's name server dst = (8 << 24) | (8 << 16) | (8 << 8) | (8 << 0); if((fd = connect(dst, 10000, 53)) < 0){ fprintf(2, "ping: connect() failed\n"); exit(1); } len = dns_req(obuf); if(write(fd, obuf, len) < 0){ fprintf(2, "dns: send() failed\n"); exit(1); } int cc = read(fd, ibuf, sizeof(ibuf)); if(cc < 0){ fprintf(2, "dns: recv() failed\n"); exit(1); } dns_rep(ibuf, cc); close(fd); } int main(int argc, char *argv[]) { int i, ret; uint16 dport = NET_TESTS_PORT; printf("nettests running on port %d\n", dport); printf("testing ping: "); ping(2000, dport, 1); printf("OK\n"); printf("testing single-process pings: "); for (i = 0; i < 100; i++) ping(2000, dport, 1); printf("OK\n"); printf("testing multi-process pings: "); for (i = 0; i < 10; i++){ int pid = fork(); if (pid == 0){ ping(2000 + i + 1, dport, 1); exit(0); } } for (i = 0; i < 10; i++){ wait(&ret); if (ret != 0) exit(1); } printf("OK\n"); printf("testing DNS\n"); dns(); printf("DNS OK\n"); printf("all tests passed.\n"); exit(0); }