-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathicmp.c
135 lines (117 loc) · 4.14 KB
/
icmp.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#include <stdio.h>
#include <netinet/in.h>
#define NETSTACK_LOG_UNIT "ICMP"
#include <netstack/checksum.h>
#include <netstack/inet/icmp.h>
#include <netstack/inet/ipv4.h>
#include <netstack/inet/neigh.h>
bool icmp_log(struct pkt_log *log, struct frame *frame) {
struct log_trans *trans = &log->t;
struct icmp_hdr *hdr = icmp_hdr(frame);
LOGT(trans, "length %hu ", frame_data_len(frame));
frame->data += sizeof(struct icmp_hdr);
// Print and check checksum
uint16_t pkt_csum = hdr->csum;
uint16_t calc_csum = in_csum(frame->head, frame_pkt_len(frame), 0) + hdr->csum;
LOGT(trans, "csum 0x%04x", ntohs(pkt_csum));
if (pkt_csum != calc_csum)
LOGT(trans, " (invalid 0x%04x)", ntohs(calc_csum));
LOGT(trans, ", ");
frame->head = frame->data;
switch (hdr->type) {
case ICMP_T_ECHORPLY: {
struct icmp_echo *echo = (struct icmp_echo *) frame->head;
LOGT(trans, "echoreply id %d, seq %d ", ntohs(echo->id),
ntohs(echo->seq));
break;
}
case ICMP_T_ECHOREQ: {
struct icmp_echo *echo = (struct icmp_echo *) frame->head;
LOGT(trans, "echoreq id %d, seq %d ", ntohs(echo->id),
ntohs(echo->seq));
break;
}
case ICMP_T_DESTUNR:
LOGT(trans, "dest-unreachable ");
break;
default:
LOGT(trans, "type %d ", hdr->type);
break;
}
return true;
}
void icmp_recv(struct frame *frame) {
struct icmp_hdr *hdr = icmp_hdr(frame);
frame->data += sizeof(struct icmp_hdr);
if (in_csum(frame->head, frame_pkt_len(frame), 0) != 0) {
LOG(LWARN, "checksum is invalid!");
return;
}
// Push ICMP into protocol stack
frame_layer_push(frame, PROTO_ICMP);
frame->head = frame->data;
switch (hdr->type) {
case ICMP_T_ECHORPLY: {
break;
}
case ICMP_T_ECHOREQ: {
frame->data += sizeof(struct icmp_echo);
frame_layer_push(frame, PROTO_ICMP_ECHO);
send_icmp_reply(frame);
break;
}
case ICMP_T_DESTUNR:
default:
break;
}
}
/*
* The echo reply is an ICMP message generated in response to an echo request;
* it is mandatory for all hosts, and must include the exact payload received
* in the request.
* Source: https://en.wikipedia.org/wiki/Ping_(networking_utility)#Echo_reply
*/
int send_icmp_reply(struct frame *ctrl) {
// Go up 2 layers as the outer of this is the ICMP header
struct frame_layer *outer = frame_layer_outer(ctrl, 2);
if (outer == NULL) {
LOG(LERR, "echo layer has no parent!");
return -1;
}
// TODO: Don't assume IPv4 parent
struct ipv4_hdr *ip = (struct ipv4_hdr *) outer->hdr;
switch(outer->proto) {
case PROTO_IPV4:
case PROTO_IPV6:
// TODO: Find ICMP route
break;
default:
LOG(LWARN, "echo parent isn't a recognised protocol (%x)",
outer->proto);
}
struct icmp_echo *ping = icmp_echo_hdr(ctrl);
// Allocate and lock new frame
size_t size = intf_max_frame_size(ctrl->intf);
struct frame *reply = intf_frame_new(ctrl->intf, size);
// Mark and copy payload from request packet
uint16_t datalen = frame_data_len(ctrl);
uint8_t *payld = (reply->data -= datalen);
memcpy(payld, ctrl->data, datalen);
// TODO: Fix frame->data pointer head/tail difference
reply->head = reply->data;
struct icmp_echo *echo = frame_head_alloc(reply, sizeof(struct icmp_echo));
struct icmp_hdr *hdr = frame_head_alloc(reply, sizeof(struct icmp_hdr));
echo->id = ping->id;
echo->seq = ping->seq;
hdr->type = ICMP_T_ECHORPLY;
hdr->code = 0;
hdr->csum = 0;
hdr->csum = in_csum(hdr, frame_pkt_len(reply), 0);
frame_unlock(reply);
// Swap source/dest IP addresses
addr_t saddr = { .proto = PROTO_IPV4, .ipv4 = ntohl(ip->saddr) };
addr_t daddr = { .proto = PROTO_IPV4, .ipv4 = ntohl(ip->daddr) };
int ret = neigh_send(reply, IP_P_ICMP, IP_DF, O_NONBLOCK, &saddr, &daddr);
frame_decref(reply);
return ret;
}