iPXE
fragment.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
19  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23 
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25 
26 #include <stdint.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <ipxe/retry.h>
30 #include <ipxe/timer.h>
31 #include <ipxe/ipstat.h>
32 #include <ipxe/fragment.h>
33 
34 /** @file
35  *
36  * Fragment reassembly
37  *
38  */
39 
40 /**
41  * Expire fragment reassembly buffer
42  *
43  * @v timer Retry timer
44  * @v fail Failure indicator
45  */
46 static void fragment_expired ( struct retry_timer *timer, int fail __unused ) {
47  struct fragment *fragment =
48  container_of ( timer, struct fragment, timer );
49 
50  DBGC ( fragment, "FRAG %p expired\n", fragment );
51  free_iob ( fragment->iobuf );
52  list_del ( &fragment->list );
54  free ( fragment );
55 }
56 
57 /**
58  * Find fragment reassembly buffer
59  *
60  * @v fragments Fragment reassembler
61  * @v iobuf I/O buffer
62  * @v hdrlen Length of non-fragmentable potion of I/O buffer
63  * @ret fragment Fragment reassembly buffer, or NULL if not found
64  */
66  struct io_buffer *iobuf,
67  size_t hdrlen ) {
68  struct fragment *fragment;
69 
72  return fragment;
73  }
74  return NULL;
75 }
76 
77 /**
78  * Reassemble packet
79  *
80  * @v fragments Fragment reassembler
81  * @v iobuf I/O buffer
82  * @v hdrlen Length of non-fragmentable potion of I/O buffer
83  * @ret iobuf Reassembled packet, or NULL
84  *
85  * This function takes ownership of the I/O buffer. Note that the
86  * length of the non-fragmentable portion may be modified.
87  */
88 struct io_buffer * fragment_reassemble ( struct fragment_reassembler *fragments,
89  struct io_buffer *iobuf,
90  size_t *hdrlen ) {
91  struct fragment *fragment;
92  struct io_buffer *new_iobuf;
93  size_t new_len;
94  size_t offset;
95  size_t expected_offset;
96  int more_frags;
97 
98  /* Update statistics */
99  fragments->stats->reasm_reqds++;
100 
101  /* Find matching fragment reassembly buffer, if any */
102  fragment = fragment_find ( fragments, iobuf, *hdrlen );
103 
104  /* Drop out-of-order fragments */
105  offset = fragments->fragment_offset ( iobuf, *hdrlen );
106  expected_offset = ( fragment ? ( iob_len ( fragment->iobuf ) -
107  fragment->hdrlen ) : 0 );
108  if ( offset != expected_offset ) {
109  DBGC ( fragment, "FRAG %p dropping out-of-sequence fragment "
110  "[%zd,%zd), expected [%zd,...)\n", fragment, offset,
111  ( offset + iob_len ( iobuf ) - *hdrlen ),
112  expected_offset );
113  goto drop;
114  }
115 
116  /* Create or extend fragment reassembly buffer as applicable */
117  if ( ! fragment ) {
118 
119  /* Create new fragment reassembly buffer */
120  fragment = zalloc ( sizeof ( *fragment ) );
121  if ( ! fragment )
122  goto drop;
123  list_add ( &fragment->list, &fragments->list );
124  fragment->iobuf = iobuf;
125  fragment->hdrlen = *hdrlen;
126  timer_init ( &fragment->timer, fragment_expired, NULL );
127  fragment->fragments = fragments;
128  DBGC ( fragment, "FRAG %p [0,%zd)\n", fragment,
129  ( iob_len ( iobuf ) - *hdrlen ) );
130 
131  } else {
132 
133  /* Check if this is the final fragment */
134  more_frags = fragments->more_fragments ( iobuf, *hdrlen );
135  DBGC ( fragment, "FRAG %p [%zd,%zd)%s\n", fragment,
136  offset, ( offset + iob_len ( iobuf ) - *hdrlen ),
137  ( more_frags ? "" : " complete" ) );
138 
139  /* Extend fragment reassembly buffer. Preserve I/O
140  * buffer headroom to allow for code which modifies
141  * and resends the buffer (e.g. ICMP echo responses).
142  */
143  iob_pull ( iobuf, *hdrlen );
144  new_len = ( iob_headroom ( fragment->iobuf ) +
145  iob_len ( fragment->iobuf ) + iob_len ( iobuf ) );
146  new_iobuf = alloc_iob ( new_len );
147  if ( ! new_iobuf ) {
148  DBGC ( fragment, "FRAG %p could not extend reassembly "
149  "buffer to %zd bytes\n", fragment, new_len );
150  goto drop;
151  }
152  iob_reserve ( new_iobuf, iob_headroom ( fragment->iobuf ) );
153  memcpy ( iob_put ( new_iobuf, iob_len ( fragment->iobuf ) ),
155  memcpy ( iob_put ( new_iobuf, iob_len ( iobuf ) ),
156  iobuf->data, iob_len ( iobuf ) );
157  free_iob ( fragment->iobuf );
158  fragment->iobuf = new_iobuf;
159  free_iob ( iobuf );
160 
161  /* Stop fragment reassembly timer */
162  stop_timer ( &fragment->timer );
163 
164  /* If this is the final fragment, return it */
165  if ( ! more_frags ) {
166  iobuf = fragment->iobuf;
167  *hdrlen = fragment->hdrlen;
168  list_del ( &fragment->list );
169  free ( fragment );
170  fragments->stats->reasm_oks++;
171  return iobuf;
172  }
173  }
174 
175  /* (Re)start fragment reassembly timer */
177 
178  return NULL;
179 
180  drop:
181  fragments->stats->reasm_fails++;
182  free_iob ( iobuf );
183  return NULL;
184 }
struct list_head list
Definition: fragment.h:23
unsigned long reasm_reqds
ipSystemStatsReasmReqds
Definition: ipstat.h:95
#define iob_pull(iobuf, len)
Definition: iobuf.h:102
unsigned long reasm_oks
ipSystemStatsReasmOks
Definition: ipstat.h:100
#define iob_put(iobuf, len)
Definition: iobuf.h:120
struct ip_statistics * stats
Associated IP statistics.
Definition: fragment.h:65
static struct fragment * fragment_find(struct fragment_reassembler *fragments, struct io_buffer *iobuf, size_t hdrlen)
Find fragment reassembly buffer.
Definition: fragment.c:65
#define list_add(new, head)
Add a new entry to the head of a list.
Definition: list.h:69
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
A fragment reassembly buffer.
Definition: fragment.h:21
void free_iob(struct io_buffer *iobuf)
Free I/O buffer.
Definition: iobuf.c:146
Retry timers.
#define DBGC(...)
Definition: compiler.h:505
A retry timer.
Definition: retry.h:21
iPXE timers
A fragment reassembler.
Definition: fragment.h:35
struct io_buffer * alloc_iob(size_t len)
Allocate I/O buffer.
Definition: iobuf.c:129
A timer.
Definition: timer.h:28
#define list_del(list)
Delete an entry from a list.
Definition: list.h:119
void * memcpy(void *dest, const void *src, size_t len) __nonnull
IP statistics.
struct io_buffer * fragment_reassemble(struct fragment_reassembler *fragments, struct io_buffer *iobuf, size_t *hdrlen)
Reassemble packet.
Definition: fragment.c:88
#define container_of(ptr, type, field)
Get containing structure.
Definition: stddef.h:35
unsigned long reasm_fails
ipSystemStatsReasmFails
Definition: ipstat.h:110
#define list_for_each_entry(pos, head, member)
Iterate over entries in a list.
Definition: list.h:431
static userptr_t size_t offset
Offset of the first segment within the content.
Definition: deflate.h:259
struct io_buffer * iobuf
Reassembled packet.
Definition: fragment.h:25
size_t(* fragment_offset)(struct io_buffer *iobuf, size_t hdrlen)
Get fragment offset.
Definition: fragment.h:55
static void(* free)(struct refcnt *refcnt))
Definition: refcnt.h:54
void * zalloc(size_t size)
Allocate cleared memory.
Definition: malloc.c:624
static void fragment_expired(struct retry_timer *timer, int fail __unused)
Expire fragment reassembly buffer.
Definition: fragment.c:46
static size_t iob_len(struct io_buffer *iobuf)
Calculate length of data in an I/O buffer.
Definition: iobuf.h:155
struct fragment_reassembler * fragments
Fragment reassembler.
Definition: fragment.h:31
void start_timer_fixed(struct retry_timer *timer, unsigned long timeout)
Start timer with a specified timeout.
Definition: retry.c:64
int(* is_fragment)(struct fragment *fragment, struct io_buffer *iobuf, size_t hdrlen)
Check if fragment matches fragment reassembly buffer.
Definition: fragment.h:46
#define __unused
Declare a variable or data structure as unused.
Definition: compiler.h:573
#define iob_reserve(iobuf, len)
Definition: iobuf.h:67
void stop_timer(struct retry_timer *timer)
Stop timer.
Definition: retry.c:117
static size_t iob_headroom(struct io_buffer *iobuf)
Calculate available space at start of an I/O buffer.
Definition: iobuf.h:165
int(* more_fragments)(struct io_buffer *iobuf, size_t hdrlen)
Check if more fragments exist.
Definition: fragment.h:63
void * data
Start of data.
Definition: iobuf.h:48
struct retry_timer timer
Reassembly timer.
Definition: fragment.h:29
#define FRAGMENT_TIMEOUT
Fragment reassembly timeout.
Definition: fragment.h:18
Fragment reassembly.
#define NULL
NULL pointer (VOID *)
Definition: Base.h:321
String functions.
struct list_head list
List of fragment reassembly buffers.
Definition: fragment.h:37
A persistent I/O buffer.
Definition: iobuf.h:33
size_t hdrlen
Length of non-fragmentable portion of reassembled packet.
Definition: fragment.h:27