xv6-lab/user/nettests.c
Frans Kaashoek 7106151533 Lab net
2022-10-24 07:04:45 -04:00

298 lines
6.0 KiB
C

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