Free (GPLv2) TCP/IP stack developed by TASS Belgium

Fork of PicoTCP by Daniele Lacamera

Committer:
tass
Date:
Fri May 17 12:09:59 2013 +0000
Revision:
1:cfe8984a32b4
Parent:
libraries/picotcp/modules/pico_icmp4.c@0:d7f2341ab245
Update for smaller SOCKETQ

Who changed what in which revision?

UserRevisionLine numberNew contents of line
daniele 0:d7f2341ab245 1 /*********************************************************************
daniele 0:d7f2341ab245 2 PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
daniele 0:d7f2341ab245 3 See LICENSE and COPYING for usage.
daniele 0:d7f2341ab245 4
daniele 0:d7f2341ab245 5 .
daniele 0:d7f2341ab245 6
daniele 0:d7f2341ab245 7 Authors: Daniele Lacamera
daniele 0:d7f2341ab245 8 *********************************************************************/
daniele 0:d7f2341ab245 9
daniele 0:d7f2341ab245 10
daniele 0:d7f2341ab245 11 #include "pico_icmp4.h"
daniele 0:d7f2341ab245 12 #include "pico_config.h"
daniele 0:d7f2341ab245 13 #include "pico_ipv4.h"
daniele 0:d7f2341ab245 14 #include "pico_eth.h"
daniele 0:d7f2341ab245 15 #include "pico_device.h"
daniele 0:d7f2341ab245 16 #include "pico_stack.h"
daniele 0:d7f2341ab245 17 #include "pico_tree.h"
daniele 0:d7f2341ab245 18
daniele 0:d7f2341ab245 19 /* Queues */
daniele 0:d7f2341ab245 20 static struct pico_queue icmp_in = {};
daniele 0:d7f2341ab245 21 static struct pico_queue icmp_out = {};
daniele 0:d7f2341ab245 22
daniele 0:d7f2341ab245 23
daniele 0:d7f2341ab245 24 /* Functions */
daniele 0:d7f2341ab245 25
daniele 0:d7f2341ab245 26 static int pico_icmp4_checksum(struct pico_frame *f)
daniele 0:d7f2341ab245 27 {
daniele 0:d7f2341ab245 28 struct pico_icmp4_hdr *hdr = (struct pico_icmp4_hdr *) f->transport_hdr;
daniele 0:d7f2341ab245 29 if (!hdr) {
daniele 0:d7f2341ab245 30 pico_err = PICO_ERR_EINVAL;
daniele 0:d7f2341ab245 31 return -1;
daniele 0:d7f2341ab245 32 }
daniele 0:d7f2341ab245 33 hdr->crc = 0;
daniele 0:d7f2341ab245 34 hdr->crc = short_be(pico_checksum(hdr, f->transport_len));
daniele 0:d7f2341ab245 35 return 0;
daniele 0:d7f2341ab245 36 }
daniele 0:d7f2341ab245 37
daniele 0:d7f2341ab245 38 #ifdef PICO_SUPPORT_PING
daniele 0:d7f2341ab245 39 static void ping_recv_reply(struct pico_frame *f);
daniele 0:d7f2341ab245 40 #endif
daniele 0:d7f2341ab245 41
daniele 0:d7f2341ab245 42 static int pico_icmp4_process_in(struct pico_protocol *self, struct pico_frame *f)
daniele 0:d7f2341ab245 43 {
daniele 0:d7f2341ab245 44 struct pico_icmp4_hdr *hdr = (struct pico_icmp4_hdr *) f->transport_hdr;
daniele 0:d7f2341ab245 45 if (hdr->type == PICO_ICMP_ECHO) {
daniele 0:d7f2341ab245 46 hdr->type = PICO_ICMP_ECHOREPLY;
daniele 0:d7f2341ab245 47 /* Ugly, but the best way to get ICMP data size here. */
daniele 0:d7f2341ab245 48 f->transport_len = f->buffer_len - PICO_SIZE_IP4HDR;
daniele 0:d7f2341ab245 49 if (f->dev->eth)
daniele 0:d7f2341ab245 50 f->transport_len -= PICO_SIZE_ETHHDR;
daniele 0:d7f2341ab245 51 pico_icmp4_checksum(f);
daniele 0:d7f2341ab245 52 f->net_hdr = f->transport_hdr - PICO_SIZE_IP4HDR;
daniele 0:d7f2341ab245 53 f->start = f->net_hdr;
daniele 0:d7f2341ab245 54 f->len = f->buffer_len;
daniele 0:d7f2341ab245 55 if (f->dev->eth)
daniele 0:d7f2341ab245 56 f->len -= PICO_SIZE_ETHHDR;
daniele 0:d7f2341ab245 57 pico_ipv4_rebound(f);
daniele 0:d7f2341ab245 58 } else if (hdr->type == PICO_ICMP_UNREACH) {
daniele 0:d7f2341ab245 59 f->net_hdr = f->transport_hdr + PICO_ICMPHDR_UN_SIZE;
daniele 0:d7f2341ab245 60 pico_ipv4_unreachable(f, hdr->code);
daniele 0:d7f2341ab245 61 } else if (hdr->type == PICO_ICMP_ECHOREPLY) {
daniele 0:d7f2341ab245 62 #ifdef PICO_SUPPORT_PING
daniele 0:d7f2341ab245 63 ping_recv_reply(f);
daniele 0:d7f2341ab245 64 #endif
daniele 0:d7f2341ab245 65 pico_frame_discard(f);
daniele 0:d7f2341ab245 66 } else {
daniele 0:d7f2341ab245 67 pico_frame_discard(f);
daniele 0:d7f2341ab245 68 }
daniele 0:d7f2341ab245 69 return 0;
daniele 0:d7f2341ab245 70 }
daniele 0:d7f2341ab245 71
daniele 0:d7f2341ab245 72 static int pico_icmp4_process_out(struct pico_protocol *self, struct pico_frame *f)
daniele 0:d7f2341ab245 73 {
daniele 0:d7f2341ab245 74 dbg("Called %s\n", __FUNCTION__);
daniele 0:d7f2341ab245 75 return 0;
daniele 0:d7f2341ab245 76 }
daniele 0:d7f2341ab245 77
daniele 0:d7f2341ab245 78 /* Interface: protocol definition */
daniele 0:d7f2341ab245 79 struct pico_protocol pico_proto_icmp4 = {
daniele 0:d7f2341ab245 80 .name = "icmp4",
daniele 0:d7f2341ab245 81 .proto_number = PICO_PROTO_ICMP4,
daniele 0:d7f2341ab245 82 .layer = PICO_LAYER_TRANSPORT,
daniele 0:d7f2341ab245 83 .process_in = pico_icmp4_process_in,
daniele 0:d7f2341ab245 84 .process_out = pico_icmp4_process_out,
daniele 0:d7f2341ab245 85 .q_in = &icmp_in,
daniele 0:d7f2341ab245 86 .q_out = &icmp_out,
daniele 0:d7f2341ab245 87 };
daniele 0:d7f2341ab245 88
daniele 0:d7f2341ab245 89 static int pico_icmp4_notify(struct pico_frame *f, uint8_t type, uint8_t code)
daniele 0:d7f2341ab245 90 {
daniele 0:d7f2341ab245 91 struct pico_frame *reply;
daniele 0:d7f2341ab245 92 struct pico_icmp4_hdr *hdr;
daniele 0:d7f2341ab245 93 struct pico_ipv4_hdr *info;
daniele 0:d7f2341ab245 94 if (f == NULL) {
daniele 0:d7f2341ab245 95 pico_err = PICO_ERR_EINVAL;
daniele 0:d7f2341ab245 96 return -1;
daniele 0:d7f2341ab245 97 }
daniele 0:d7f2341ab245 98 reply = pico_proto_ipv4.alloc(&pico_proto_ipv4, 8 + sizeof(struct pico_ipv4_hdr) + PICO_ICMPHDR_UN_SIZE);
daniele 0:d7f2341ab245 99 info = (struct pico_ipv4_hdr*)(f->net_hdr);
daniele 0:d7f2341ab245 100 hdr = (struct pico_icmp4_hdr *) reply->transport_hdr;
daniele 0:d7f2341ab245 101 hdr->type = type;
daniele 0:d7f2341ab245 102 hdr->code = code;
daniele 0:d7f2341ab245 103 hdr->hun.ih_pmtu.ipm_nmtu = short_be(1500);
daniele 0:d7f2341ab245 104 hdr->hun.ih_pmtu.ipm_void = 0;
daniele 0:d7f2341ab245 105 reply->transport_len = 8 + sizeof(struct pico_ipv4_hdr) + PICO_ICMPHDR_UN_SIZE;
daniele 0:d7f2341ab245 106 reply->payload = reply->transport_hdr + PICO_ICMPHDR_UN_SIZE;
daniele 0:d7f2341ab245 107 memcpy(reply->payload, f->net_hdr, 8 + sizeof(struct pico_ipv4_hdr));
daniele 0:d7f2341ab245 108 pico_icmp4_checksum(reply);
daniele 0:d7f2341ab245 109 pico_ipv4_frame_push(reply, &info->src, PICO_PROTO_ICMP4);
daniele 0:d7f2341ab245 110 return 0;
daniele 0:d7f2341ab245 111 }
daniele 0:d7f2341ab245 112
daniele 0:d7f2341ab245 113 int pico_icmp4_port_unreachable(struct pico_frame *f)
daniele 0:d7f2341ab245 114 {
daniele 0:d7f2341ab245 115 /*Parameter check executed in pico_icmp4_notify*/
daniele 0:d7f2341ab245 116 return pico_icmp4_notify(f, PICO_ICMP_UNREACH, PICO_ICMP_UNREACH_PORT);
daniele 0:d7f2341ab245 117 }
daniele 0:d7f2341ab245 118
daniele 0:d7f2341ab245 119 int pico_icmp4_proto_unreachable(struct pico_frame *f)
daniele 0:d7f2341ab245 120 {
daniele 0:d7f2341ab245 121 /*Parameter check executed in pico_icmp4_notify*/
daniele 0:d7f2341ab245 122 return pico_icmp4_notify(f, PICO_ICMP_UNREACH, PICO_ICMP_UNREACH_PROTOCOL);
daniele 0:d7f2341ab245 123 }
daniele 0:d7f2341ab245 124
daniele 0:d7f2341ab245 125 int pico_icmp4_dest_unreachable(struct pico_frame *f)
daniele 0:d7f2341ab245 126 {
daniele 0:d7f2341ab245 127 /*Parameter check executed in pico_icmp4_notify*/
daniele 0:d7f2341ab245 128 return pico_icmp4_notify(f, PICO_ICMP_UNREACH, PICO_ICMP_UNREACH_HOST);
daniele 0:d7f2341ab245 129 }
daniele 0:d7f2341ab245 130
daniele 0:d7f2341ab245 131 int pico_icmp4_ttl_expired(struct pico_frame *f)
daniele 0:d7f2341ab245 132 {
daniele 0:d7f2341ab245 133 /*Parameter check executed in pico_icmp4_notify*/
daniele 0:d7f2341ab245 134 return pico_icmp4_notify(f, PICO_ICMP_TIME_EXCEEDED, PICO_ICMP_TIMXCEED_INTRANS);
daniele 0:d7f2341ab245 135 }
daniele 0:d7f2341ab245 136
daniele 0:d7f2341ab245 137 #ifdef PICO_SUPPORT_IPFILTER
daniele 0:d7f2341ab245 138 int pico_icmp4_packet_filtered(struct pico_frame *f)
daniele 0:d7f2341ab245 139 {
daniele 0:d7f2341ab245 140 /*Parameter check executed in pico_icmp4_notify*/
daniele 0:d7f2341ab245 141 /*Packet Filtered: type 3, code 13 (Communication Administratively Prohibited)*/
daniele 0:d7f2341ab245 142 return pico_icmp4_notify(f, PICO_ICMP_UNREACH, PICO_ICMP_UNREACH_FILTER_PROHIB);
daniele 0:d7f2341ab245 143 }
daniele 0:d7f2341ab245 144 #endif
daniele 0:d7f2341ab245 145
daniele 0:d7f2341ab245 146 /***********************/
daniele 0:d7f2341ab245 147 /* Ping implementation */
daniele 0:d7f2341ab245 148 /***********************/
daniele 0:d7f2341ab245 149 /***********************/
daniele 0:d7f2341ab245 150 /***********************/
daniele 0:d7f2341ab245 151 /***********************/
daniele 0:d7f2341ab245 152
daniele 0:d7f2341ab245 153
daniele 0:d7f2341ab245 154 #ifdef PICO_SUPPORT_PING
daniele 0:d7f2341ab245 155
daniele 0:d7f2341ab245 156
daniele 0:d7f2341ab245 157 struct pico_icmp4_ping_cookie
daniele 0:d7f2341ab245 158 {
daniele 0:d7f2341ab245 159 struct pico_ip4 dst;
daniele 0:d7f2341ab245 160 uint16_t err;
daniele 0:d7f2341ab245 161 uint16_t id;
daniele 0:d7f2341ab245 162 uint16_t seq;
daniele 0:d7f2341ab245 163 uint16_t size;
daniele 0:d7f2341ab245 164 int count;
daniele 0:d7f2341ab245 165 unsigned long timestamp;
daniele 0:d7f2341ab245 166 int interval;
daniele 0:d7f2341ab245 167 int timeout;
daniele 0:d7f2341ab245 168 void (*cb)(struct pico_icmp4_stats*);
daniele 0:d7f2341ab245 169
daniele 0:d7f2341ab245 170 };
daniele 0:d7f2341ab245 171
daniele 0:d7f2341ab245 172 static int cookie_compare(void *ka, void *kb)
daniele 0:d7f2341ab245 173 {
daniele 0:d7f2341ab245 174 struct pico_icmp4_ping_cookie *a = ka, *b = kb;
daniele 0:d7f2341ab245 175 if (a->id < b->id)
daniele 0:d7f2341ab245 176 return -1;
daniele 0:d7f2341ab245 177 if (a->id > b->id)
daniele 0:d7f2341ab245 178 return 1;
daniele 0:d7f2341ab245 179 return (a->seq - b->seq);
daniele 0:d7f2341ab245 180 }
daniele 0:d7f2341ab245 181
daniele 0:d7f2341ab245 182 PICO_TREE_DECLARE(Pings,cookie_compare);
daniele 0:d7f2341ab245 183
daniele 0:d7f2341ab245 184 static int pico_icmp4_send_echo(struct pico_icmp4_ping_cookie *cookie)
daniele 0:d7f2341ab245 185 {
daniele 0:d7f2341ab245 186 struct pico_frame *echo = pico_proto_ipv4.alloc(&pico_proto_ipv4, PICO_ICMPHDR_UN_SIZE + cookie->size);
daniele 0:d7f2341ab245 187 struct pico_icmp4_hdr *hdr;
daniele 0:d7f2341ab245 188
daniele 0:d7f2341ab245 189 hdr = (struct pico_icmp4_hdr *) echo->transport_hdr;
daniele 0:d7f2341ab245 190
daniele 0:d7f2341ab245 191 hdr->type = PICO_ICMP_ECHO;
daniele 0:d7f2341ab245 192 hdr->code = 0;
daniele 0:d7f2341ab245 193 hdr->hun.ih_idseq.idseq_id = short_be(cookie->id);
daniele 0:d7f2341ab245 194 hdr->hun.ih_idseq.idseq_seq = short_be(cookie->seq);
daniele 0:d7f2341ab245 195 echo->transport_len = PICO_ICMPHDR_UN_SIZE + cookie->size;
daniele 0:d7f2341ab245 196 echo->payload = echo->transport_hdr + PICO_ICMPHDR_UN_SIZE;
daniele 0:d7f2341ab245 197 echo->payload_len = cookie->size;
daniele 0:d7f2341ab245 198 /* XXX: Fill payload */
daniele 0:d7f2341ab245 199 pico_icmp4_checksum(echo);
daniele 0:d7f2341ab245 200 pico_ipv4_frame_push(echo, &cookie->dst, PICO_PROTO_ICMP4);
daniele 0:d7f2341ab245 201 return 0;
daniele 0:d7f2341ab245 202 }
daniele 0:d7f2341ab245 203
daniele 0:d7f2341ab245 204
daniele 0:d7f2341ab245 205 static void ping_timeout(unsigned long now, void *arg)
daniele 0:d7f2341ab245 206 {
daniele 0:d7f2341ab245 207 struct pico_icmp4_ping_cookie *cookie = (struct pico_icmp4_ping_cookie *)arg;
daniele 0:d7f2341ab245 208 if(pico_tree_findKey(&Pings,cookie)){
daniele 0:d7f2341ab245 209 if (cookie->err == PICO_PING_ERR_PENDING) {
daniele 0:d7f2341ab245 210 struct pico_icmp4_stats stats;
daniele 0:d7f2341ab245 211 stats.dst = cookie->dst;
daniele 0:d7f2341ab245 212 stats.seq = cookie->seq;
daniele 0:d7f2341ab245 213 stats.time = 0;
daniele 0:d7f2341ab245 214 stats.size = cookie->size;
daniele 0:d7f2341ab245 215 stats.err = PICO_PING_ERR_TIMEOUT;
daniele 0:d7f2341ab245 216 dbg(" ---- Ping timeout!!!\n");
daniele 0:d7f2341ab245 217 cookie->cb(&stats);
daniele 0:d7f2341ab245 218 }
daniele 0:d7f2341ab245 219
daniele 0:d7f2341ab245 220 pico_tree_delete(&Pings,cookie);
daniele 0:d7f2341ab245 221 pico_free(cookie);
daniele 0:d7f2341ab245 222 }
daniele 0:d7f2341ab245 223 }
daniele 0:d7f2341ab245 224
daniele 0:d7f2341ab245 225 static void next_ping(unsigned long now, void *arg);
daniele 0:d7f2341ab245 226 static inline void send_ping(struct pico_icmp4_ping_cookie *cookie)
daniele 0:d7f2341ab245 227 {
daniele 0:d7f2341ab245 228 pico_icmp4_send_echo(cookie);
daniele 0:d7f2341ab245 229 cookie->timestamp = pico_tick;
daniele 0:d7f2341ab245 230 pico_timer_add(cookie->timeout, ping_timeout, cookie);
daniele 0:d7f2341ab245 231 pico_timer_add(cookie->interval, next_ping, cookie);
daniele 0:d7f2341ab245 232 }
daniele 0:d7f2341ab245 233
daniele 0:d7f2341ab245 234 static void next_ping(unsigned long now, void *arg)
daniele 0:d7f2341ab245 235 {
daniele 0:d7f2341ab245 236 struct pico_icmp4_ping_cookie *newcookie, *cookie = (struct pico_icmp4_ping_cookie *)arg;
daniele 0:d7f2341ab245 237
daniele 0:d7f2341ab245 238 if(pico_tree_findKey(&Pings,cookie)){
daniele 0:d7f2341ab245 239 if (cookie->seq < cookie->count) {
daniele 0:d7f2341ab245 240 newcookie = pico_zalloc(sizeof(struct pico_icmp4_ping_cookie));
daniele 0:d7f2341ab245 241 if (!newcookie)
daniele 0:d7f2341ab245 242 return;
daniele 0:d7f2341ab245 243 memcpy(newcookie, cookie, sizeof(struct pico_icmp4_ping_cookie));
daniele 0:d7f2341ab245 244 newcookie->seq++;
daniele 0:d7f2341ab245 245
daniele 0:d7f2341ab245 246 pico_tree_insert(&Pings,newcookie);
daniele 0:d7f2341ab245 247 send_ping(newcookie);
daniele 0:d7f2341ab245 248 }
daniele 0:d7f2341ab245 249 }
daniele 0:d7f2341ab245 250 }
daniele 0:d7f2341ab245 251
daniele 0:d7f2341ab245 252
daniele 0:d7f2341ab245 253 static void ping_recv_reply(struct pico_frame *f)
daniele 0:d7f2341ab245 254 {
daniele 0:d7f2341ab245 255 struct pico_icmp4_ping_cookie test, *cookie;
daniele 0:d7f2341ab245 256 struct pico_icmp4_hdr *hdr = (struct pico_icmp4_hdr *) f->transport_hdr;
daniele 0:d7f2341ab245 257 test.id = short_be(hdr->hun.ih_idseq.idseq_id );
daniele 0:d7f2341ab245 258 test.seq = short_be(hdr->hun.ih_idseq.idseq_seq);
daniele 0:d7f2341ab245 259
daniele 0:d7f2341ab245 260 cookie = pico_tree_findKey(&Pings, &test);
daniele 0:d7f2341ab245 261 if (cookie) {
daniele 0:d7f2341ab245 262 struct pico_icmp4_stats stats;
daniele 0:d7f2341ab245 263 cookie->err = PICO_PING_ERR_REPLIED;
daniele 0:d7f2341ab245 264 stats.dst = cookie->dst;
daniele 0:d7f2341ab245 265 stats.seq = cookie->seq;
daniele 0:d7f2341ab245 266 stats.size = cookie->size;
daniele 0:d7f2341ab245 267 stats.time = pico_tick - cookie->timestamp;
daniele 0:d7f2341ab245 268 stats.err = cookie->err;
daniele 0:d7f2341ab245 269 stats.ttl = ((struct pico_ipv4_hdr *)f->net_hdr)->ttl;
daniele 0:d7f2341ab245 270 if(cookie->cb != NULL)
daniele 0:d7f2341ab245 271 cookie->cb(&stats);
daniele 0:d7f2341ab245 272 /* XXX cb */
daniele 0:d7f2341ab245 273 } else {
daniele 0:d7f2341ab245 274 dbg("Reply for seq=%d, not found.\n", test.seq);
daniele 0:d7f2341ab245 275 }
daniele 0:d7f2341ab245 276 }
daniele 0:d7f2341ab245 277
daniele 0:d7f2341ab245 278 int pico_icmp4_ping(char *dst, int count, int interval, int timeout, int size, void (*cb)(struct pico_icmp4_stats *))
daniele 0:d7f2341ab245 279 {
daniele 0:d7f2341ab245 280 static uint16_t next_id = 0x91c0;
daniele 0:d7f2341ab245 281 struct pico_icmp4_ping_cookie *cookie;
daniele 0:d7f2341ab245 282
daniele 0:d7f2341ab245 283 if((dst == NULL) || (interval == 0) || (timeout == 0) || (count == 0)){
daniele 0:d7f2341ab245 284 pico_err = PICO_ERR_EINVAL;
daniele 0:d7f2341ab245 285 return -1;
daniele 0:d7f2341ab245 286 }
daniele 0:d7f2341ab245 287
daniele 0:d7f2341ab245 288 cookie = pico_zalloc(sizeof(struct pico_icmp4_ping_cookie));
daniele 0:d7f2341ab245 289 if (!cookie) {
daniele 0:d7f2341ab245 290 pico_err = PICO_ERR_ENOMEM;
daniele 0:d7f2341ab245 291 return -1;
daniele 0:d7f2341ab245 292 }
daniele 0:d7f2341ab245 293
daniele 0:d7f2341ab245 294 if (pico_string_to_ipv4(dst, &cookie->dst.addr) < 0) {
daniele 0:d7f2341ab245 295 pico_err = PICO_ERR_EINVAL;
daniele 0:d7f2341ab245 296 pico_free(cookie);
daniele 0:d7f2341ab245 297 return -1;
daniele 0:d7f2341ab245 298 }
daniele 0:d7f2341ab245 299 cookie->seq = 1;
daniele 0:d7f2341ab245 300 cookie->id = next_id++;
daniele 0:d7f2341ab245 301 cookie->err = PICO_PING_ERR_PENDING;
daniele 0:d7f2341ab245 302 cookie->size = size;
daniele 0:d7f2341ab245 303 cookie->interval = interval;
daniele 0:d7f2341ab245 304 cookie->timeout = timeout;
daniele 0:d7f2341ab245 305 cookie->cb = cb;
daniele 0:d7f2341ab245 306 cookie->count = count;
daniele 0:d7f2341ab245 307
daniele 0:d7f2341ab245 308 pico_tree_insert(&Pings,cookie);
daniele 0:d7f2341ab245 309 send_ping(cookie);
daniele 0:d7f2341ab245 310
daniele 0:d7f2341ab245 311 return 0;
daniele 0:d7f2341ab245 312
daniele 0:d7f2341ab245 313 }
daniele 0:d7f2341ab245 314
daniele 0:d7f2341ab245 315 #endif