aboutsummaryrefslogtreecommitdiffstats
path: root/metis/ccnx/forwarder/metis/io/metis_HopByHopFragmenter.h
blob: 6fd83f1958e4594a1d675ce33b69742deb7f1b69 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
/*
 * Copyright (c) 2017 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @file metis_HopByHopFragmenterer.h
 * @brief Implements a fragmenter for a hop-by-hop protocol
 *
 * The hop-by-hop fragmenter can be though of as a bi-directional queue.
 *
 * The receive process takes fragmented packets from the wire and reassembles them.  Once a packet is
 * reassembed, it is put on end of the receive queue.  The user can then pop a message of the top of
 * the receive queue and process it normally.
 *
 * The send process takes a potentially large input packet and fragments it in to pieces that are places
 * in order on the send queue.  The caller can then pop messages of the head of the send queue and
 * put them on the wire.
 *
 * In the next update, we probalby want to make the send queue and receive queue external so I/O and
 * message processing threads can access them directly.
 *
 */

#ifndef __Metis__metis_HopByHopFragmenter__
#define __Metis__metis_HopByHopFragmenter__

#include <stdbool.h>
#include <ccnx/forwarder/metis/core/metis_Logger.h>
#include <ccnx/forwarder/metis/core/metis_Message.h>

struct metis_hopbyhop_fragment;
typedef struct metis_hopbyhop_fragment MetisHopByHopFragmenter;

/**
 * Creates a fragmentation class for a specific session
 *
 * The fragmenter is specific to a given flow (i.e. source-destination-ethertype tuple).
 * It is the responsibility of the caller to create the appropriate number of fragmenters
 * and classify packets in to the right fragmenter.
 *
 * @param [in] logger The logger to use for output
 * @param [in] mtu The MTU to use for send operations
 *
 * @return non-null An allocted MetisHopByHopFragmenter
 * @return null An error
 *
 * Example:
 * @code
 * {
 *    MetisHopByHopFragmenter *fragmenter = metisHopByHopFragmenter_Create(logger, mtu);
 *    metisHopByHopFragmenter_Release(&fragmenter);
 * }
 * @endcode
 */
MetisHopByHopFragmenter *metisHopByHopFragmenter_Create(MetisLogger *logger, unsigned mtu);

/**
 * Release a reference to the fragmenter
 *
 * Will destroy any packets in the receive and send queues.
 *
 * @param [in,out] fragmenterPtr A pointer to an allocated MetisHopByHopFragmenter
 *
 * Example:
 * @code
 * {
 *    MetisHopByHopFragmenter *fragmenter = metisHopByHopFragmenter_Create(logger, mtu);
 *    metisHopByHopFragmenter_Release(&fragmenter);
 * }
 * @endcode
 */
void metisHopByHopFragmenter_Release(MetisHopByHopFragmenter **fragmenterPtr);

/**
 * Receives a message as part of the fragmentation session
 *
 * Receives a fragment.  If this causes a reassembly to complete, the completed packet
 * will be placed in the receive queue and may be accessed by metisHopByHopFragmenter_PopReceiveQueue().
 * The caller is reponsible for releasing message.
 *
 * If a non-fragment packet is received, it is placed directly on the receive queue.
 *
 * The caller is responsible for releasing the message.
 *
 * @param [in] fragmenter An allocated MetisHopByHopFragmenter
 * @param [in] message An allocated MetisMessage
 *
 * @return true The receive buffer has an assembled packet ready for read
 * @return false the receive buffer does not have a complete packet ready.
 *
 * Example:
 * @code
 * {
 *   void
 *   acceptFragment(MetisHopByHopFragmenter *fragmenter, MetisMessage *message)
 *   {
 *      bool receiveQueueNotEmpty = metisHopByHopFragmenter_Receive(fragmenter, message);
 *      if (receiveQueueNotEmpty) {
 *         MetisMessage *assembled = NULL;
 *         while ((assembled = metisHopByHopFragmenter_PopReceiveQueue(fragmenter)) != NULL) {
 *            etherListener->stats.framesReassembled++;
 *            metisForwarder_Receive(etherListener->metis, assembled);
 *         }
 *      }
 *   }
 * }
 * @endcode
 */
bool metisHopByHopFragmenter_Receive(MetisHopByHopFragmenter *fragmenter, const MetisMessage *message);

/**
 * Pops the top assembed message from the receive queue
 *
 * Reads the top reassembled packet from the receive queue.  The caller must
 * release the returned message.
 *
 * @param [in] fragmenter An allocated MetisHopByHopFragmenter
 *
 * @return NULL The receive queue is empty (i.e. the current reassembly is not complete)
 * @return non-null A re-assembed message
 *
 * Example:
 * @code
 * {
 *   void
 *   acceptFragment(MetisHopByHopFragmenter *fragmenter, MetisMessage *message)
 *   {
 *      bool receiveQueueNotEmpty = metisHopByHopFragmenter_Receive(fragmenter, message);
 *      if (receiveQueueNotEmpty) {
 *         MetisMessage *assembled = NULL;
 *         while ((assembled = metisHopByHopFragmenter_PopReceiveQueue(fragmenter)) != NULL) {
 *            etherListener->stats.framesReassembled++;
 *            metisForwarder_Receive(etherListener->metis, assembled);
 *         }
 *      }
 *   }
 * }
 * @endcode
 */
MetisMessage *metisHopByHopFragmenter_PopReceiveQueue(MetisHopByHopFragmenter *fragmenter);

/**
 * Adds a message to the send buffer
 *
 * This may make multiple references to the original message where each fragment is
 * pointing as an extent in to the original message.
 *
 * @param [in] fragmenter An allocated MetisHopByHopFragmenter
 * @param [in] message An allocated MetisMessage
 *
 * @return true The message was fragmented and put on the send queue
 * @return false An error
 *
 * Example:
 * @code
 * {
 *   void sendFragments(MetisHopByHopFragmenter *fragmenter, const MetisMessage *message) {
 *      bool success = metisHopByHopFragmenter_Send(fragmenter, message);
 *
 *      MetisMessage *fragment;
 *      while (success && (fragment = metisHopByHopFragmenter_PopSendQueue(fragmenter)) != NULL) {
 *         success = _sendFrame(fragment);
 *         metisMessage_Release(&fragment);
 *      }
 *
 *      // if we failed, drain the other fragments
 *      if (!success) {
 *         while ((fragment = metisHopByHopFragmenter_PopSendQueue(fragmenter)) != NULL) {
 *         metisMessage_Release(&fragment);
 *      }
 *   }
 *   // caller must release message
 * }
 * @endcode
 */
bool metisHopByHopFragmenter_Send(MetisHopByHopFragmenter *fragmenter, MetisMessage *message);

/**
 * Pops the next message to send to the wire from the send queue
 *
 * Returns the front of the Send FIFO queue of fragments that should be
 * sent on the wire.
 *
 * @param [in] fragmenter An allocated MetisHopByHopFragmenter
 *
 * @return null there is no message awaiting transmit
 * @return non-null A message to send
 *
 * Example:
 * @code
 * {
 *   void sendIdleFragment(MetisHopByHopFragmenter *fragmenter) {
 *      bool success = metisHopByHopFragmenter_SendIdle(fragmenter);
 *
 *      MetisMessage *fragment;
 *      while (success && (fragment = metisHopByHopFragmenter_PopSendQueue(fragmenter)) != NULL) {
 *         success = _sendFrame(fragment);
 *         metisMessage_Release(&fragment);
 *      }
 *   }
 * }
 * @endcode
 */
MetisMessage *metisHopByHopFragmenter_PopSendQueue(MetisHopByHopFragmenter *fragmenter);
#endif /* defined(__Metis__metis_HopByHopFragmenter__) */