1/** 2 * \file 3 * \brief Intel e1000 driver fragmentation support 4 * 5 * This file is a driver for the PCI Express e1000 card 6 */ 7 8/* 9 * Copyright (c) 2007, 2008, 2009, ETH Zurich. 10 * All rights reserved. 11 * 12 * This file is distributed under the terms in the attached LICENSE file. 13 * If you do not find this file, copies can be found by writing to: 14 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group. 15 */ 16 17#include <stdlib.h> 18#include <barrelfish/barrelfish.h> 19#include <string.h> 20#include <net_queue_manager/net_queue_manager.h> 21#include "queue_manager_debug.h" 22 23/***************************************************************** 24 * Data types: 25 *****************************************************************/ 26//#define E_IPFRAG_SERVICE_DEBUG 1 27 28#if defined(E_IPFRAG_SERVICE_DEBUG) || defined(GLOBAL_DEBUG) 29#define E_IPFRAG_DEBUG(x...) printf("e_IPFRAG: " x) 30#else 31#define E_IPFRAG_DEBUG(x...) ((void)0) 32#endif 33 34 35struct ip_packet { 36 void *data; 37 size_t len; 38 uint64_t ip_id; 39 uint64_t timeout; 40 struct ip_packet *next; 41 uint64_t flags; 42}; 43 44struct fragment_filter { 45 uint64_t ip_id; 46 struct buffer_descriptor *buffer; 47 struct fragment_filter *next; 48}; 49 50/***************************************************************** 51 * Local states: 52 *****************************************************************/ 53 54struct ip_packet *frag_packets; 55struct fragment_filter *frag_filters; 56 57#define IP_OPTIONS_OFFSET 20 58#define FRAGMENTED_FLAG 0x20 59#define IP_MAGIC_OFFSET 12 60#define IP_MAGIC_1 0x08 61#define IP_MAGIC_2 0x00 62 63static bool is_fragmented(void *packet, size_t len) 64{ 65 if (len <= IP_OPTIONS_OFFSET) { 66 return false; 67 } 68 if ((((uint8_t *) packet)[IP_MAGIC_OFFSET] != IP_MAGIC_1) || 69 (((uint8_t *) packet)[IP_MAGIC_OFFSET + 1] != IP_MAGIC_2)) { 70 return false; 71 } 72 73 unsigned char fragmented = (unsigned char) 74 ((uint8_t *) packet)[IP_OPTIONS_OFFSET]; 75 76 fragmented &= FRAGMENTED_FLAG; 77 if (fragmented) { 78 return true; 79 } else { 80 return false; 81 } 82} 83 84#define IP_ID_OFFSET 18 85 86static uint64_t get_ip_id(void *packet, size_t len) 87{ 88 uint64_t id = 0; 89 90 if (len <= IP_ID_OFFSET + 1) { 91 return 0; 92 } 93 id = (uint64_t) (((uint8_t *) packet)[IP_ID_OFFSET]); 94 id <<= 8; 95 id |= (uint64_t) (((uint8_t *) packet)[IP_ID_OFFSET + 1]); 96 return id; 97} 98 99#define FRAGMENT_OFFSET_OFFSET 20 100#define CLEAN_OPTIONS_FLAGS 0x1F 101 102// checks whether a udp or tcp header is present 103// this assumes that the packet is fragmented 104static bool has_headers(void *packet, size_t len) 105{ 106 if (len <= FRAGMENT_OFFSET_OFFSET + 1) { 107 return false; 108 } 109 unsigned char off1 = (unsigned char) 110 ((uint8_t *) packet)[FRAGMENT_OFFSET_OFFSET]; 111 unsigned char off2 = 112 (unsigned char) ((uint8_t *) packet)[FRAGMENT_OFFSET_OFFSET + 1]; 113 off1 &= 0x1F; 114 return !(off1 | off2); 115} 116 117 118static void add_packet_to_fragment_list(void *packet, size_t len, 119 uint64_t ip_id, uint64_t flags) 120{ 121 struct ip_packet *new_packet = (struct ip_packet *) malloc 122 (sizeof(struct ip_packet)); 123 if (new_packet == NULL) { 124 return; 125 } 126 new_packet->data = malloc(len); 127 if (new_packet->data == NULL) { 128 free(new_packet); 129 return; 130 } 131 memcpy(new_packet->data, packet, len); 132 new_packet->ip_id = ip_id; 133 new_packet->len = len; 134 new_packet->flags = flags; 135 // TODO: set timeout here 136 new_packet->next = frag_packets; 137 frag_packets = new_packet; 138} 139 140static void add_fragment_filter(uint64_t ip_id, 141 struct buffer_descriptor *buffer) 142{ 143 struct fragment_filter *filter = (struct fragment_filter *) malloc 144 (sizeof(struct fragment_filter)); 145 146 if (filter == NULL) { 147 return; 148 } 149 filter->ip_id = ip_id; 150 filter->buffer = buffer; 151 filter->next = frag_filters; 152 frag_filters = filter; 153} 154 155static void remove_fragment_filter(uint64_t ip_id) 156{ 157 struct fragment_filter *filter = frag_filters, *prev = NULL; 158 159 while (filter) { 160 if (filter->ip_id == ip_id) { 161 162 if (prev == NULL) { 163 frag_filters = filter->next; 164 } else { 165 prev->next = frag_filters->next; 166 } 167 free(filter); 168 return; 169 } 170 filter = filter->next; 171 } 172} 173 174static struct buffer_descriptor *execute_all_fragment_filters(uint64_t ip_id) 175{ 176 struct fragment_filter *filter = frag_filters; 177 178 while (filter) { 179 if (filter->ip_id == ip_id) { 180 E_IPFRAG_DEBUG("IP_FRAG: Filter matched with id %lu for buf %lu\n", 181 filter->ip_id, filter->buffer->buffer_id); 182 183 return filter->buffer; 184 } 185 filter = filter->next; 186 } 187 E_IPFRAG_DEBUG("IP_FRAG: no frag filters matched\n"); 188 return NULL; 189} 190 191static struct ip_packet *check_outstanding_fragmented_packets(uint64_t ip_id) 192{ 193 struct ip_packet *packet = frag_packets, *prev_packet = NULL; 194 195 while (packet) { 196 if (packet->ip_id == ip_id) { 197 if (prev_packet == NULL) { 198 frag_packets = packet->next; 199 } else { 200 prev_packet->next = packet->next; 201 } 202 return packet; 203 } else { 204 // TODO: expire it in case of timeout 205 } 206 prev_packet = packet; 207 packet = packet->next; 208 } 209 return NULL; 210} 211 212bool handle_fragmented_packet(void *packet, size_t len, uint64_t flags) 213{ 214 struct buffer_descriptor *buffer = NULL; 215 uint64_t ip_id = get_ip_id(packet, len); 216 struct ip_packet *old_packet; 217 218 if (!ip_id) { // this is not an IP packet 219 return false; 220 } 221 222 if (is_fragmented(packet, len)) { 223 if (has_headers(packet, len)) { 224 struct filter *ret_filter; 225 E_IPFRAG_DEBUG("IP_FRAG: first fragment %lu\n", ip_id); 226 ret_filter = execute_filters(packet, len); 227 if (ret_filter == NULL || ret_filter->buffer == NULL) { 228 E_IPFRAG_DEBUG("IP_FRAG: ERROR: issues with filter %lu\n", 229 ip_id); 230 return true; // we do not want to process this packet anymore 231 } 232 233 buffer = ret_filter->buffer; 234 add_fragment_filter(ip_id, buffer); 235 if (copy_packet_to_user(buffer, packet, len, flags)) { 236// send_packet_received_notification(buffer); 237 E_IPFRAG_DEBUG("IP_FRAG: user copy done %lu\n", ip_id); 238 } else { 239 E_IPFRAG_DEBUG("IP_FRAG: ERROR: usre copy failed %lu\n", ip_id); 240 } 241 242 // we should check for out of order packets here 243 while ((old_packet = 244 check_outstanding_fragmented_packets(ip_id)) != NULL) { 245 E_IPFRAG_DEBUG("out of order\n"); 246 if (copy_packet_to_user(buffer, old_packet->data, len, 247 old_packet->flags)) { 248 E_IPFRAG_DEBUG 249 ("IP_FRAG: user copy done for out of order\n"); 250// send_packet_received_notification(buffer); 251 free(old_packet->data); 252 free(old_packet); 253 } else { 254 E_IPFRAG_DEBUG("IP_FRAG: ERROR: usre copy failed %lu\n", 255 ip_id); 256 } 257 } 258 return true; 259 260 } else { // this is continuation of fragmented packets 261 E_IPFRAG_DEBUG("IP_FRAG: continuation frags %lu\n", ip_id); 262 buffer = execute_all_fragment_filters(ip_id); 263 if (buffer) { 264 E_IPFRAG_DEBUG 265 ("IP_FRAG: copying cont of ip_id %lu to buff %lu\n", ip_id, 266 buffer->buffer_id); 267 if (copy_packet_to_user(buffer, packet, len, flags)) { 268 E_IPFRAG_DEBUG 269 ("IP_FRAG: copying ip_id %lu to buff %lu done\n", ip_id, 270 buffer->buffer_id); 271// send_packet_received_notification(buffer); 272 return true; 273 } else { 274 E_IPFRAG_DEBUG("IP_FRAG: ERROR: usre copy failed %lu\n", 275 ip_id); 276 } 277 } else { // this is a very bad out of order packet 278 E_IPFRAG_DEBUG("IP_FRAG: out of order frag %lu\n", ip_id); 279 add_packet_to_fragment_list(packet, len, ip_id, flags); 280 return true; 281 } 282 } 283 } else { // in case this is the last one 284 // FIXME: there is bug here. it might be that we delete the filter 285 // but an out of order packet comes in later. this is unlikely but 286 // possible. unfortunatly there is not a trivial way to fix this. 287 // we can tag filters for removal and set a timeout and delete them 288 // in a good time. right now, the packet goes into the frag_packets 289 // list and gets removed when it gets a timeout. the packet would 290 // not be constructed successfully at the lwip level 291 E_IPFRAG_DEBUG("IP_FRAG: assuming last frag %lu\n", ip_id); 292 293 buffer = execute_all_fragment_filters(ip_id); 294 if (buffer) { 295 E_IPFRAG_DEBUG("IP_FRAG: sending last frag %lu to buff %lu\n", 296 ip_id, buffer->buffer_id); 297 298 // this is the last of this sequence if there is no out of order 299 if (copy_packet_to_user(buffer, packet, len, flags)) { 300 E_IPFRAG_DEBUG("IP_FRAG: send last frag %lu to buff %lu done\n", 301 ip_id, buffer->buffer_id); 302// send_packet_received_notification(buffer); 303 } else { 304 E_IPFRAG_DEBUG("IP_FRAG: ERROR: usre copy failed %lu\n", ip_id); 305 } 306 // removing the filter, later we should not remove it and only 307 // tag it for removal and set a timeout 308 remove_fragment_filter(ip_id); 309 return true; 310 } 311 } 312 E_IPFRAG_DEBUG("IP_FRAG: non fragmented packet %lu\n", ip_id); 313 return false; 314} 315