|
1 /* |
|
2 * Copyright 2009-2010 Darren Long, darren.long@mac.com |
|
3 * Copyright 2006 Intel Corporation |
|
4 * |
|
5 * Licensed under the Apache License, Version 2.0 (the "License"); |
|
6 * you may not use this file except in compliance with the License. |
|
7 * You may obtain a copy of the License at |
|
8 * |
|
9 * http://www.apache.org/licenses/LICENSE-2.0 |
|
10 * |
|
11 * Unless required by applicable law or agreed to in writing, software |
|
12 * distributed under the License is distributed on an "AS IS" BASIS, |
|
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
14 * See the License for the specific language governing permissions and |
|
15 * limitations under the License. |
|
16 */ |
|
17 |
|
18 #ifndef _SEQPACKET_CONVERGENCE_LAYER_H_ |
|
19 #define _SEQPACKET_CONVERGENCE_LAYER_H_ |
|
20 |
|
21 #include "ConnectionConvergenceLayer.h" |
|
22 #include "CLConnection.h" |
|
23 |
|
24 namespace dtn { |
|
25 |
|
26 /** |
|
27 * Another shared-implementation convergence layer class for use with |
|
28 * reliable, in-order SEQ_PACKET protocols, i.e. Connected Mode AX.25. |
|
29 * The goal is to share as much functionality as |
|
30 * possible between protocols that have in-order, reliable, delivery |
|
31 * semantics, but require fixed sized payloads rather than conforming to |
|
32 * stream semantics. |
|
33 * |
|
34 * For the protocol, bundles are broken up into configurable-sized |
|
35 * segments that are sent sequentially. Only a single bundle is |
|
36 * inflight on the wire at one time (i.e. we don't interleave segments |
|
37 * from different bundles). When segment acknowledgements are enabled |
|
38 * (the default behavior), the receiving node sends an acknowledgement |
|
39 * for each segment of the bundle that was received. |
|
40 * |
|
41 * Keepalive messages are sent back and forth to ensure that the |
|
42 * connection remains open. In the case of on demand links, a |
|
43 * configurable idle timer is maintained to close the link when no |
|
44 * bundle traffic has been sent or received. Links that are expected |
|
45 * to be open but have broken due to underlying network conditions |
|
46 * (i.e. always on and on demand links) are reopened by a timer that |
|
47 * is managed by the contact manager. |
|
48 * |
|
49 * Flow control is managed through the poll callbacks given by the |
|
50 * base class CLConnection. In send_pending_data, we check if there |
|
51 * are any acks that need to be sent, then check if there are bundle |
|
52 * segments to be sent (i.e. acks are given priority). The only |
|
53 * exception to this is that the connection might be write blocked in |
|
54 * the middle of sending a data segment. In that case, we must first |
|
55 * finish transmitting the current segment before sending any other |
|
56 * acks (or the shutdown message), otherwise those messages will be |
|
57 * consumed as part of the payload. |
|
58 * |
|
59 * To make sure that we don't deadlock with the other side, we always |
|
60 * drain any data that is ready on the channel. All incoming messages |
|
61 * mark state in the appropriate data structures (i.e. InFlightList |
|
62 * and IncomingList), then rely on send_pending_data to send the |
|
63 * appropriate responses. |
|
64 * |
|
65 * The InflightBundle is used to record state about bundle |
|
66 * transmissions. To record the segments that have been sent, we fill |
|
67 * in the sent_data_ sparse bitmap with the range of bytes as we send |
|
68 * segments out. As acks arrive, we extend the ack_data_ field to |
|
69 * match. Once the whole bundle is acked, the entry is removed from |
|
70 * the InFlightList. |
|
71 * |
|
72 * The IncomingBundle is used to record state about bundle reception. |
|
73 * The rcvd_data_ bitmap is extended contiguously with the amount of |
|
74 * data that has been received, including partially received segments. |
|
75 * |
|
76 * To track segments that we have received but haven't yet acked, we |
|
77 * set a single bit for the offset of the end of the segment in the |
|
78 * ack_data_ bitmap. We also separately record the total range of acks |
|
79 * that have been previously sent in acked_length_. As we send acks |
|
80 * out, we clear away the bits in ack_data_ |
|
81 */ |
|
82 class SeqpacketConvergenceLayer : public ConnectionConvergenceLayer { |
|
83 public: |
|
84 /** |
|
85 * Constructor |
|
86 */ |
|
87 SeqpacketConvergenceLayer(const char* logpath, const char* cl_name, |
|
88 u_int8_t cl_version); |
|
89 |
|
90 protected: |
|
91 /** |
|
92 * Values for ContactHeader flags. |
|
93 */ |
|
94 typedef enum { |
|
95 SEGMENT_ACK_ENABLED = 1 << 0, ///< segment acks requested |
|
96 REACTIVE_FRAG_ENABLED = 1 << 1, ///< reactive fragmentation enabled |
|
97 NEGATIVE_ACK_ENABLED = 1 << 2, ///< refuse bundle enabled |
|
98 } contact_header_flags_t; |
|
99 |
|
100 /** |
|
101 * Contact initiation header. Sent at the beginning of a contact. |
|
102 */ |
|
103 struct ContactHeader { |
|
104 u_int32_t magic; ///< magic word (MAGIC: "dtn!") |
|
105 u_int8_t version; ///< cl protocol version |
|
106 u_int8_t flags; ///< connection flags (see above) |
|
107 u_int16_t keepalive_interval; ///< seconds between keepalive packets |
|
108 // SDNV local_eid_length local eid length |
|
109 // byte[] local_eid local eid data |
|
110 } __attribute__((packed)); |
|
111 |
|
112 /** |
|
113 * Valid type codes for the protocol messages, shifted into the |
|
114 * high-order four bits of the byte. The lower four bits are used |
|
115 * for per-message flags, defined below. |
|
116 */ |
|
117 typedef enum { |
|
118 DATA_SEGMENT = 0x1 << 4, ///< a segment of bundle data |
|
119 ///< (followed by a SDNV segment length) |
|
120 ACK_SEGMENT = 0x2 << 4, ///< acknowledgement of a segment |
|
121 ///< (followed by a SDNV ack length) |
|
122 REFUSE_BUNDLE = 0x3 << 4, ///< reject reception of current bundle |
|
123 KEEPALIVE = 0x4 << 4, ///< keepalive packet |
|
124 SHUTDOWN = 0x5 << 4, ///< about to shutdown |
|
125 } msg_type_t; |
|
126 |
|
127 /** |
|
128 * Valid flags for the DATA_SEGMENT message. |
|
129 */ |
|
130 typedef enum { |
|
131 BUNDLE_START = 0x1 << 1, ///< First segment of a bundle |
|
132 BUNDLE_END = 0x1 << 0, ///< Last segment of a bundle |
|
133 } data_segment_flags_t; |
|
134 |
|
135 /** |
|
136 * Valid flags for the SHUTDOWN message. |
|
137 */ |
|
138 typedef enum { |
|
139 SHUTDOWN_HAS_REASON = 0x1 << 1, ///< Has reason code |
|
140 SHUTDOWN_HAS_DELAY = 0x1 << 0, ///< Has reconnect delay |
|
141 } shutdown_flags_t; |
|
142 |
|
143 /** |
|
144 * Values for the SHUTDOWN reason codes |
|
145 */ |
|
146 typedef enum { |
|
147 SHUTDOWN_NO_REASON = 0xff, ///< no reason code (never sent) |
|
148 SHUTDOWN_IDLE_TIMEOUT = 0x0, ///< idle connection |
|
149 SHUTDOWN_VERSION_MISMATCH = 0x1, ///< version mismatch |
|
150 SHUTDOWN_BUSY = 0x2, ///< node is busy |
|
151 } shutdown_reason_t; |
|
152 |
|
153 /** |
|
154 * Convert a reason code to a string. |
|
155 */ |
|
156 static const char* shutdown_reason_to_str(shutdown_reason_t reason) |
|
157 { |
|
158 switch (reason) { |
|
159 case SHUTDOWN_NO_REASON: return "no reason"; |
|
160 case SHUTDOWN_IDLE_TIMEOUT: return "idle connection"; |
|
161 case SHUTDOWN_VERSION_MISMATCH: return "version mismatch"; |
|
162 case SHUTDOWN_BUSY: return "node is busy"; |
|
163 } |
|
164 NOTREACHED; |
|
165 } |
|
166 |
|
167 /** |
|
168 * Link parameters shared among all Seqpacket based convergence layers. |
|
169 */ |
|
170 class SeqpacketLinkParams : public ConnectionConvergenceLayer::LinkParams { |
|
171 public: |
|
172 bool segment_ack_enabled_; ///< Use per-segment acks |
|
173 bool negative_ack_enabled_;///< Enable negative acks |
|
174 u_int keepalive_interval_; ///< Seconds between keepalive packets |
|
175 u_int segment_length_; ///< Maximum size of transmitted segments |
|
176 u_int ack_window_; ///< Number of segments before acking |
|
177 |
|
178 protected: |
|
179 // See comment in LinkParams for why this should be protected |
|
180 SeqpacketLinkParams(bool init_defaults); |
|
181 }; |
|
182 |
|
183 /** |
|
184 * Version of the actual CL protocol. |
|
185 */ |
|
186 u_int8_t cl_version_; |
|
187 |
|
188 /** |
|
189 * Seqpacket connection class. |
|
190 */ |
|
191 class Connection : public CLConnection { |
|
192 public: |
|
193 /** |
|
194 * Constructor. |
|
195 */ |
|
196 Connection(const char* classname, |
|
197 const char* logpath, |
|
198 SeqpacketConvergenceLayer* cl, |
|
199 SeqpacketLinkParams* params, |
|
200 bool active_connector); |
|
201 |
|
202 /// @{ virtual from CLConnection |
|
203 bool send_pending_data(); |
|
204 void handle_bundles_queued(); |
|
205 void handle_cancel_bundle(Bundle* bundle); |
|
206 void handle_poll_timeout(); |
|
207 void break_contact(ContactEvent::reason_t reason); |
|
208 /// @} |
|
209 |
|
210 protected: |
|
211 /** |
|
212 * Hook used to tell the derived CL class to drain data out of |
|
213 * the send buffer. |
|
214 */ |
|
215 virtual void send_data() = 0; |
|
216 |
|
217 /// @{ utility functions used by derived classes |
|
218 void initiate_contact(); |
|
219 void process_data(); |
|
220 void check_keepalive(); |
|
221 /// @} |
|
222 |
|
223 private: |
|
224 /// @{ utility functions used internally in this class |
|
225 void note_data_rcvd(); |
|
226 void note_data_sent(); |
|
227 bool send_pending_acks(); |
|
228 bool start_next_bundle(); |
|
229 bool send_next_segment(InFlightBundle* inflight); |
|
230 bool send_data_todo(InFlightBundle* inflight); |
|
231 bool finish_bundle(InFlightBundle* inflight); |
|
232 void check_completed(InFlightBundle* inflight); |
|
233 void send_keepalive(); |
|
234 |
|
235 void handle_contact_initiation(); |
|
236 bool handle_data_segment(u_int8_t flags); |
|
237 bool handle_data_todo(); |
|
238 bool handle_ack_segment(u_int8_t flags); |
|
239 bool handle_refuse_bundle(u_int8_t flags); |
|
240 bool handle_keepalive(u_int8_t flags); |
|
241 bool handle_shutdown(u_int8_t flags); |
|
242 void check_completed(IncomingBundle* incoming); |
|
243 /// @} |
|
244 |
|
245 /** |
|
246 * Utility function to downcast the params_ pointer that's |
|
247 * stored in the CLConnection parent class. |
|
248 */ |
|
249 SeqpacketLinkParams* seqpacket_lparams() |
|
250 { |
|
251 SeqpacketLinkParams* ret = dynamic_cast<SeqpacketLinkParams*>(params_); |
|
252 ASSERT(ret != NULL); |
|
253 return ret; |
|
254 } |
|
255 |
|
256 protected: |
|
257 InFlightBundle* current_inflight_; ///< Current bundle that's in flight |
|
258 size_t send_segment_todo_; ///< Bytes left to send of current segment |
|
259 std::queue<u_int> sendbuf_sequence_delimiters_; ///< sendbuf_ may hold many segments |
|
260 size_t recv_segment_todo_; ///< Bytes left to recv of current segment |
|
261 struct timeval data_rcvd_; ///< Timestamp for idle/keepalive timer |
|
262 struct timeval data_sent_; ///< Timestamp for idle timer |
|
263 struct timeval keepalive_sent_; ///< Timestamp for keepalive timer |
|
264 bool breaking_contact_; ///< Bit to catch multiple calls to |
|
265 ///< break_contact |
|
266 bool contact_initiated_; ///< bit to prevent certain actions before |
|
267 ///< contact is initiated |
|
268 u_int ack_window_todo_; ///< number of segments left in ack_window |
|
269 }; |
|
270 |
|
271 /// For some gcc variants, this typedef seems to be needed |
|
272 typedef ConnectionConvergenceLayer::LinkParams LinkParams; |
|
273 |
|
274 /// @{ Virtual from ConvergenceLayer |
|
275 void dump_link(const LinkRef& link, oasys::StringBuffer* buf); |
|
276 /// @} |
|
277 |
|
278 /// @{ Virtual from ConnectionConvergenceLayer |
|
279 bool parse_link_params(LinkParams* params, |
|
280 int argc, const char** argv, |
|
281 const char** invalidp); |
|
282 bool finish_init_link(const LinkRef& link, LinkParams* params); |
|
283 /// @} |
|
284 |
|
285 }; |
|
286 |
|
287 } // namespace dtn |
|
288 |
|
289 #endif /* _STREAM_CONVERGENCE_LAYER_H_ */ |