diff options
Diffstat (limited to 'user')
| -rw-r--r-- | user/nettests.c | 297 | ||||
| -rw-r--r-- | user/pingpong.c | 52 | 
2 files changed, 349 insertions, 0 deletions
| diff --git a/user/nettests.c b/user/nettests.c new file mode 100644 index 0000000..2f7d6cd --- /dev/null +++ b/user/nettests.c @@ -0,0 +1,297 @@ +#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); +} diff --git a/user/pingpong.c b/user/pingpong.c new file mode 100644 index 0000000..6ed12e7 --- /dev/null +++ b/user/pingpong.c @@ -0,0 +1,52 @@ +#include "kernel/types.h" +#include "kernel/stat.h" +#include "user/user.h" + +#define N 5 +char buf[N]; + +void +pong(int *parent_to_child, int *child_to_parent) { +  if (read(parent_to_child[0], buf, N) < 0) { +    printf("read failed\n"); +  } +  printf("%d: received %s\n", getpid(), buf); +  if (write(child_to_parent[1], "pong", 4) != 4) { +    printf("write failed\n"); +  } +} + +void +ping(int *parent_to_child, int *child_to_parent) { +   +  if (write(parent_to_child[1], "ping", 4) != 4) { +    printf("write failed\n"); +  } +  if (read(child_to_parent[0], buf, N) < 0) { +    printf("read failed\n"); +  } +  printf("%d: received %s\n", getpid(), buf); +} + +int +main(int argc, char *argv[]) +{ +  int parent_to_child[2]; +  int child_to_parent[2]; + +  int pid; + +  if (pipe(parent_to_child) < 0 || pipe(child_to_parent) < 0) { +    printf("pipe failed\n"); +  } +  if ((pid = fork()) < 0) { +    printf("fork failed\n"); +  } +  if (pid == 0) { +    pong(parent_to_child, child_to_parent); +  } else { +    ping(parent_to_child, child_to_parent); +  } +   +  exit(0); +} | 
