1/**
2 * \file ping.c
3 * \brief
4 */
5
6
7/*
8 * Copyright (c) 2017 ETH Zurich.
9 * All rights reserved.
10 *
11 * This file is distributed under the terms in the attached LICENSE file.
12 * If you do not find this file, copies can be found by writing to:
13 * ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group.
14 */
15
16
17#include <barrelfish/barrelfish.h>
18
19#include <lwip/ip.h>
20#include <lwip/udp.h>
21#include <lwip/pbuf.h>
22
23#include <lwip/mem.h>
24#include <lwip/raw.h>
25#include <lwip/icmp.h>
26#include <lwip/netif.h>
27#include <lwip/sys.h>
28#include <lwip/timeouts.h>
29#include <lwip/inet_chksum.h>
30#include <lwip/prot/ip4.h>
31
32#include <net/net.h>
33
34static ip_addr_t ping_addr;
35
36static struct raw_pcb *ping_pcb;
37
38#define PING_TIMEOUT   1000
39#define PING_DELAY     1000
40#define PING_ID        0xBFBF
41
42uint16_t ping_data_size = 64;
43
44static uint16_t ping_seq_num = 0;
45
46
47struct result
48{
49    cycles_t t_start;
50    cycles_t t_end;
51    uint8_t received;
52};
53
54#define PING_RESULT_MAX 512
55
56struct result results[PING_RESULT_MAX];
57
58
59
60
61#include <barrelfish/sys_debug.h>
62static cycles_t tsc_per_us = 0;
63static inline uint64_t cycles_to_us(struct result *res)
64{
65    if (tsc_per_us == 0) {
66        sys_debug_get_tsc_per_ms(&tsc_per_us);
67        tsc_per_us /= 1000;
68    }
69
70    return (res->t_end - res->t_start) / (tsc_per_us);
71}
72
73static uint8_t
74ping_recv(void *arg, struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *addr)
75{
76    struct icmp_echo_hdr *iecho;
77
78    if ((p->tot_len >= (PBUF_IP_HLEN + sizeof(struct icmp_echo_hdr))) &&
79            pbuf_header(p, -PBUF_IP_HLEN) == 0) {
80        iecho = (struct icmp_echo_hdr *)p->payload;
81
82        uint16_t seq = lwip_ntohs(iecho->seqno);
83
84        if ((iecho->id == PING_ID) && (iecho->seqno == lwip_htons(ping_seq_num))) {
85            if (results[seq & (PING_RESULT_MAX - 1)].t_end != 0) {
86                debug_printf("t_end is non null???");
87            }
88            results[seq & (PING_RESULT_MAX - 1)].t_end = rdtsc();
89            results[seq & (PING_RESULT_MAX - 1)].received = 1;
90
91            char ip_str[16] = {0};
92
93            strncpy(ip_str, ip4addr_ntoa(addr), sizeof(ip_str));
94
95            printf("%u bytes from %s: icmp_seq=%u, ttl=%u time=%lu us (%lu)\n",
96                    ping_data_size, ip_str, seq,
97                    42, cycles_to_us(&results[seq & (PING_RESULT_MAX - 1)]),
98                    results[seq & (PING_RESULT_MAX - 1)].t_end - results[seq & (PING_RESULT_MAX - 1)].t_start);
99
100
101
102            pbuf_free(p);
103            return 1; /* eat the packet */
104        }
105        /* not eaten, restore original packet */
106        pbuf_header(p, PBUF_IP_HLEN);
107    }
108
109    return 0; /* don't eat the packet */
110}
111
112static void
113ping_send(struct raw_pcb *raw, ip_addr_t *addr)
114{
115  struct pbuf *p;
116  struct icmp_echo_hdr *iecho;
117  size_t ping_size = sizeof(struct icmp_echo_hdr) + ping_data_size;
118
119  p = pbuf_alloc(PBUF_IP, (u16_t)ping_size, PBUF_RAM);
120  if (!p) {
121    return;
122  }
123  if ((p->len == p->tot_len) && (p->next == NULL)) {
124    iecho = (struct icmp_echo_hdr *)p->payload;
125
126    ICMPH_TYPE_SET(iecho, ICMP_ECHO);
127    ICMPH_CODE_SET(iecho, 0);
128    iecho->chksum = 0;
129    iecho->id     = PING_ID;
130    iecho->seqno  = lwip_htons(++ping_seq_num);
131
132    /* fill the additional data buffer with some data */
133    for(uint16_t i = 0; i < ping_data_size; i++) {
134      ((char*)iecho)[sizeof(struct icmp_echo_hdr) + i] = (char)i;
135    }
136
137    iecho->chksum = inet_chksum(iecho, ping_size);
138
139    results[ping_seq_num & (PING_RESULT_MAX - 1)].t_end = 0;
140    results[ping_seq_num & (PING_RESULT_MAX - 1)].received = 0;
141    results[ping_seq_num & (PING_RESULT_MAX - 1)].t_start = rdtsc();
142
143    raw_sendto(raw, p, addr);
144  }
145  pbuf_free(p);
146}
147
148static void
149ping_timeout(void *arg)
150{
151  struct raw_pcb *pcb = (struct raw_pcb*)arg;
152
153  LWIP_ASSERT("ping_timeout: no pcb given!", pcb != NULL);
154
155  if (!results[ping_seq_num & (PING_RESULT_MAX - 1)].received) {
156      printf("Timeout.\n");
157  }
158
159  ping_send(pcb, &ping_addr);
160
161  sys_timeout(PING_DELAY, ping_timeout, pcb);
162}
163
164
165int main(int argc, char *argv[])
166{
167    errval_t err;
168
169    debug_printf("PING started.\n");
170
171    /* parse ip */
172
173    if (argc != 2 || !ip4addr_aton(argv[1], &ping_addr)) {
174        USER_PANIC("Invalid address supplied: %s\n", argv[1]);
175    }
176
177    debug_printf("PING with IP %s.\n", argv[1]);
178
179    /* connect to the network */
180    err = networking_init_default();
181    if (err_is_fail(err)) {
182        USER_PANIC_ERR(err, "Failed to initialize the network");
183    }
184
185    debug_printf("PING network initialized.\n");
186
187    ping_pcb = raw_new(IP_PROTO_ICMP);
188    if (ping_pcb == NULL) {
189        USER_PANIC("could not get a new raw pcb");
190    }
191
192    debug_printf("PING pcb created.\n");
193
194    raw_recv(ping_pcb, ping_recv, NULL);
195    raw_bind(ping_pcb, IP_ADDR_ANY);
196    sys_timeout(PING_DELAY, ping_timeout, ping_pcb);
197
198
199
200    while(1) {
201        //event_dispatch_non_block(get_default_waitset());
202        networking_poll();
203    }
204
205    debug_printf("UDP ECHO termiated.\n");
206
207    return 0;
208}
209
210
211