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