Skip to content

Commit

Permalink
finally compiles
Browse files Browse the repository at this point in the history
  • Loading branch information
maalvikabhat authored Feb 28, 2020
1 parent eec8a4b commit 5d455fd
Showing 1 changed file with 305 additions and 0 deletions.
305 changes: 305 additions & 0 deletions exercises/ex04/trout/trout.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,305 @@
#include "trout.h"

/* variables we might want to configure */
Rec *rec = (Rec *) sendbuf;

/* NOTES: system calls beginning with a capital letter are Stevens's
wrapper functions. Each one invokes the method and checks the
return value. If the call fails, it invokes err_sys, which prints
the error message and quits.
Types that begin with a capital letter are usually typedefs
that I added because (1) I hate seeing the word struct all over
the place, and (2) it lets me pretend I am writing Java. */

/* sig_alrm: alarm handler sends a message to the process through
a pipe, causing select to return */

void sig_alrm (int signo)
{
Write (pipefd[1], "", 1); /* write 1 null byte to pipe */
return;
}

/* process_ip: extracts all the info from an incoming ICMP packet
Just for kicks, I changed the BSD-style names of the ICMP
errors to Linux-style names, mostly so that they will be
consistent with the changes I made in the kernel and so my
head won't explode. */

int process_ip (struct ip *ip, int len)
{
int hlen1, hlen2, icmplen;
struct icmp *icmp;
struct ip *hip;
struct udphdr *udp;
seq = 0;
u_short dport = 32768 + 668; /* destination port -- hopefully unused */

hlen1 = ip->ip_hl << 2; /* length of IP header */
icmp = (struct icmp *) (recvbuf + hlen1);
icmplen = len - hlen1;

if (icmplen < 8 + 20 + 8) return 0;

if (icmp->icmp_type != ICMP_TIME_EXCEEDED &&
icmp->icmp_type != ICMP_DEST_UNREACH)
return 0;

/* hip is the header of the enclosed IP packets, supposedly
the header of the packet that caused the error */

hip = (struct ip *) (recvbuf + hlen1 + 8);
if (hip->ip_p != IPPROTO_UDP) return 0;

hlen2 = hip->ip_hl << 2;
udp = (struct udphdr *) (recvbuf + hlen1 + 8 + hlen2);

if (udp->source != htons (sport)) return 0;
if (udp->dest != htons (dport + seq)) return 0;

/* now we know it's an ICMP packet caused by a UDP
datagram sent by us and sent to the port we happen to
be sending to. It's probably one of ours. */

if (icmp->icmp_type == ICMP_TIME_EXCEEDED) {
if (icmp->icmp_code == ICMP_EXC_TTL) {
return -2;
} else {
return 0;
}
}

if (icmp->icmp_type == ICMP_DEST_UNREACH) {
if (icmp->icmp_code == ICMP_PORT_UNREACH) {
return -1;
} else {
return 0;
}
}
return 0;
}

/* recv_dgram: reads all incoming datagrams and checks for
returning ICMP packets.
returns -3 on timeout,
-2 on ICMP time exceeded in transit (we reached a router)
-1 on ICMP port unreachable (we reached the destination)
0 on ICMP that has nothing to do with us */

/* as Stevens points out in Section 18.5 of Unix Network Programming,
many programs with alarms have a race condition, which is that
the alarm might go off before we get to the recvfrom, in which
case it does nothing and the recvfrom might wait indefinitely.
In earlier versions of this code, this problem seemed to pop
up occasionally (although I am not positive about that).
The use of select here solves that problem. When the alarm
goes off, the alarm handler sends a message through the pipe,
which is one of the things select waits for.
When select returns, we know that we have received a datagram
OR the alarm has gone off OR both. We then use rset to find
out which, and deal with it.
According to the specification of select, it should not be possible
to get to the recvfrom unless there is a datagram waiting, and
therefore the recvfrom should never block. Nevertheless, it sometimes
does, which is why, when we opened it, we set the NONBLOCK flag
and why, if it fails (errno = EAGAIN) we just go on. */

int recv_dgram ()
{
int err;
socklen_t len;
ssize_t n;
struct ip *ip;
int maxfdp1 = max (recvfd, pipefd[0]) + 1;
fd_set rset[1];
FD_ZERO (rset);

alarm(3); /* set the timeout alarm to handle dropped packets */

while (1) {
FD_SET (recvfd, rset);
FD_SET (pipefd[0], rset);

n = select (maxfdp1, rset, NULL, NULL, NULL);
if (n < 0 && errno != EINTR) {
err_sys ("select error");
}

if (FD_ISSET (recvfd, rset)) {
len = salen;
n = recvfrom (recvfd, recvbuf, sizeof(recvbuf), 0, sarecv, &len);
err = errno;
Gettimeofday (recvtv, NULL); /* get time of packet arrival */
if (n < 0 && err != EAGAIN) {
err_sys ("recvfrom error");
}
}

if (FD_ISSET (pipefd[0], rset)) {
Read (pipefd[0], &n, 1);
return -3; /* timeout */
}

ip = (struct ip *) recvbuf;
return process_ip (ip, n);
}
}

/* sub_tv: subtract minus from plus and put the result in res */

void sub_tv (Timeval *plus, Timeval *minus, Timeval *res)
{
res->tv_sec = plus->tv_sec - minus->tv_sec;
res->tv_usec = plus->tv_usec - minus->tv_usec;

if (res->tv_usec < 0) {
res->tv_sec--;
res->tv_usec += 1000000;
}
}

/* time_to_double: convert a Timeval to a double. This only
works with Timevals that are small (like the difference between
two real Timevals) */

double time_to_double (Timeval *time)
{
return time->tv_sec * 1000.0 + time->tv_usec / 1000.0;
}

/* print_report: prints all the information about a successful round trip */

void print_report ()
{
int stat;
char str[NI_MAXHOST];

stat = sock_cmp_addr (sarecv, salast, salen);

/* if this reply comes from source different from the previous
one, print the full host information */

if (stat != 0) {
stat = getnameinfo (sarecv, salen, str, sizeof(str), NULL, 0, 0);
if (stat == 0) {
printf (" %s (%s)", str, Sock_ntop_host (sarecv, salen));
} else {
printf (" %s", Sock_ntop_host (sarecv, salen));
}
memcpy (salast, sarecv, salen);
}

/* calculate and print the round trip time using user-level timestamps */

sub_tv (recvtv, sendtv, difftv);

printf (" %.3f", time_to_double (difftv));
}

/* send_dgram: generate an outgoing UDP packet */

/* the second effort send is a kludge to handle a funny
thing, which is that the last host seems to refuse the
second or third connection consistently, which might
might mean that something breaks when we get the
ICMP_DEST_UNREACH error. The second attempt seems
to succeed consistently. */

void send_dgram (int ttl)
{
int n;
datalen = sizeof (Rec); /* length of the data in a datagram */
seq = 0;
u_short dport = 32768 + 668; /* destination port -- hopefully unused */

rec->seq = seq++;
sock_set_port (sasend, salen, htons(dport+seq));

Gettimeofday (sendtv, NULL);
n = sendto(sendfd, sendbuf, datalen, 0, sasend, salen);

if (n==-1 && errno == ECONNREFUSED) {
Gettimeofday (sendtv, NULL);
n = sendto(sendfd, sendbuf, datalen, 0, sasend, salen);
}

if (n != datalen) {
err_sys("sendto error");
}
}

/* send_probes: sends a set of probes with the given ttl and
then waits for the replies. The weird TOS options are there
as a signal to the kernel to identify clink packets so it can
fill in the timestamps. I am assuming that they don't have
any actual effect. */

int send_probes (int ttl)
{
int probe, code, done;

Setsockopt (sendfd, IPPROTO_IP, IP_TTL, &ttl, sizeof(int));
bzero (salast, salen);
int nprobes = 2;

printf ("%2d ", ttl);
fflush (stdout);

done = 0; /* count the number of probes that generate an ICMP_DEST_UNREACH */

for (probe = 0; probe < nprobes; probe++) {
send_dgram (ttl);
code = recv_dgram ();

if (code == -3) {
printf (" *");
} else {
print_report ();
}

if (code == -1) done++;
fflush (stdout);
}
printf ("ms\n");
return done;
}

/* loop_ttl: starting with ttl=1, gradually increase ttl until
we start getting ICMP_DEST_UNREACH instead of ICMP_TIME_EXCEEDED */

void loop_ttl ()
{
int ttl, done;

Pipe (pipefd); /* the pipe for the alarm handler */
max_ttl = 30;


recvfd = socket (sasend->sa_family, SOCK_RAW, IPPROTO_ICMP);
if (recvfd == -1) {
if (errno == EPERM) {
printf ("\nclink was unable to open a raw socket. The most\n");
printf ("likely cause is that you are not running it as root.\n");
exit (1);
} else {
err_sys ("opening raw socket in clink");
}
}

fcntl (recvfd, F_SETFL, O_NONBLOCK);
setuid (getuid ());

sendfd = socket (sasend->sa_family, SOCK_DGRAM, 0);

sabind->sa_family = sasend->sa_family;
sport = (getpid() & 0xffff) | 0x8000; /* source UDP port # */
sock_set_port (sabind, salen, htons(sport));
Bind (sendfd, sabind, salen);

Signal (SIGALRM, sig_alrm);

for (ttl = 1; ttl <= max_ttl; ttl++) {
done = send_probes (ttl);
if (done > 0) break;
}
}

0 comments on commit 5d455fd

Please sign in to comment.