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

Fork of PicoTCP by Daniele Lacamera

Committer:
daniele
Date:
Sun Jun 16 20:19:44 2013 +0000
Revision:
29:1a47b7151851
Child:
46:c35c84e301e0
Updated from masterbranch;

Who changed what in which revision?

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