]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | #ifndef _MSG_ASYNC_FRAMES_V2_ |
2 | #define _MSG_ASYNC_FRAMES_V2_ | |
3 | ||
4 | #include "include/types.h" | |
5 | #include "common/Clock.h" | |
6 | #include "crypto_onwire.h" | |
7 | #include <array> | |
8 | #include <utility> | |
9 | ||
10 | /** | |
11 | * Protocol V2 Frame Structures | |
12 | * | |
13 | * Documentation in: doc/dev/msgr2.rst | |
14 | **/ | |
15 | ||
16 | namespace ceph::msgr::v2 { | |
17 | ||
18 | // We require these features from any peer, period, in order to encode | |
19 | // a entity_addrvec_t. | |
20 | const uint64_t msgr2_required = CEPH_FEATUREMASK_MSG_ADDR2; | |
21 | ||
22 | // We additionally assume the peer has the below features *purely for | |
23 | // the purpose of encoding the frames themselves*. The only complex | |
24 | // types in the frames are entity_addr_t and entity_addrvec_t, and we | |
25 | // specifically want the peer to understand the (new in nautilus) | |
26 | // TYPE_ANY. We treat narrow this assumption to frames because we | |
27 | // expect there may be future clients (the kernel) that understand | |
28 | // msgr v2 and understand this encoding but don't necessarily have | |
29 | // everything else that SERVER_NAUTILUS implies. Yes, a fresh feature | |
30 | // bit would be a cleaner approach, but those are scarce these days. | |
31 | const uint64_t msgr2_frame_assumed = | |
32 | msgr2_required | | |
33 | CEPH_FEATUREMASK_SERVER_NAUTILUS; | |
34 | ||
35 | enum class Tag : __u8 { | |
36 | HELLO = 1, | |
37 | AUTH_REQUEST, | |
38 | AUTH_BAD_METHOD, | |
39 | AUTH_REPLY_MORE, | |
40 | AUTH_REQUEST_MORE, | |
41 | AUTH_DONE, | |
42 | AUTH_SIGNATURE, | |
43 | CLIENT_IDENT, | |
44 | SERVER_IDENT, | |
45 | IDENT_MISSING_FEATURES, | |
46 | SESSION_RECONNECT, | |
47 | SESSION_RESET, | |
48 | SESSION_RETRY, | |
49 | SESSION_RETRY_GLOBAL, | |
50 | SESSION_RECONNECT_OK, | |
51 | WAIT, | |
52 | MESSAGE, | |
53 | KEEPALIVE2, | |
54 | KEEPALIVE2_ACK, | |
55 | ACK | |
56 | }; | |
57 | ||
58 | struct segment_t { | |
59 | // TODO: this will be dropped with support for `allocation policies`. | |
60 | // We need them because of the rx_buffers zero-copy optimization. | |
61 | static constexpr __le16 PAGE_SIZE_ALIGNMENT{4096}; | |
62 | ||
63 | static constexpr __le16 DEFAULT_ALIGNMENT = sizeof(void *); | |
64 | ||
eafe8130 TL |
65 | ceph_le32 length; |
66 | ceph_le16 alignment; | |
11fdf7f2 TL |
67 | } __attribute__((packed)); |
68 | ||
69 | struct SegmentIndex { | |
70 | struct Msg { | |
71 | static constexpr std::size_t HEADER = 0; | |
72 | static constexpr std::size_t FRONT = 1; | |
73 | static constexpr std::size_t MIDDLE = 2; | |
74 | static constexpr std::size_t DATA = 3; | |
75 | }; | |
76 | ||
77 | struct Control { | |
78 | static constexpr std::size_t PAYLOAD = 0; | |
79 | }; | |
80 | }; | |
81 | ||
82 | static constexpr uint8_t CRYPTO_BLOCK_SIZE { 16 }; | |
83 | ||
84 | static constexpr std::size_t MAX_NUM_SEGMENTS = 4; | |
85 | ||
86 | // V2 preamble consists of one or more preamble blocks depending on | |
87 | // the number of segments a particular frame needs. Each block holds | |
88 | // up to MAX_NUM_SEGMENTS segments and has its own CRC. | |
89 | // | |
90 | // XXX: currently the multi-segment facility is NOT implemented. | |
91 | struct preamble_block_t { | |
92 | // Tag. For multi-segmented frames the value is the same | |
93 | // between subsequent preamble blocks. | |
94 | __u8 tag; | |
95 | ||
96 | // Number of segments to go in entire frame. First preable block has | |
97 | // set this to just #segments, second #segments - MAX_NUM_SEGMENTS, | |
98 | // third to #segments - MAX_NUM_SEGMENTS and so on. | |
99 | __u8 num_segments; | |
100 | ||
101 | std::array<segment_t, MAX_NUM_SEGMENTS> segments; | |
102 | __u8 _reserved[2]; | |
103 | ||
104 | // CRC32 for this single preamble block. | |
eafe8130 | 105 | ceph_le32 crc; |
11fdf7f2 TL |
106 | } __attribute__((packed)); |
107 | static_assert(sizeof(preamble_block_t) % CRYPTO_BLOCK_SIZE == 0); | |
108 | static_assert(std::is_standard_layout<preamble_block_t>::value); | |
109 | ||
110 | // Each Frame has an epilogue for integrity or authenticity validation. | |
111 | // For plain mode it's quite straightforward - the structure stores up | |
112 | // to MAX_NUM_SEGMENTS crc32 checksums, one per each segment. | |
113 | // For secure mode things become very different. The fundamental thing | |
114 | // is that epilogue format is **an implementation detail of particular | |
115 | // cipher**. ProtocolV2 only knows: | |
116 | // * where the data is placed (always at the end of ciphertext), | |
117 | // * how long it is. RxHandler provides get_extra_size_at_final() but | |
118 | // ProtocolV2 has NO WAY to alter this. | |
119 | // | |
120 | // The intention behind the contract is to provide flexibility of cipher | |
121 | // selection. Currently AES in GCM mode is used and epilogue conveys its | |
122 | // *auth tag* (following OpenSSL's terminology). However, it would be OK | |
123 | // to switch to e.g. AES128-CBC + HMAC-SHA512 without affecting protocol | |
124 | // (expect the cipher negotiation, of course). | |
125 | // | |
126 | // In addition to integrity/authenticity data each variant of epilogue | |
127 | // conveys late_flags. The initial user of this field will be the late | |
128 | // frame abortion facility. | |
129 | struct epilogue_plain_block_t { | |
130 | __u8 late_flags; | |
eafe8130 | 131 | std::array<ceph_le32, MAX_NUM_SEGMENTS> crc_values; |
11fdf7f2 TL |
132 | } __attribute__((packed)); |
133 | static_assert(std::is_standard_layout<epilogue_plain_block_t>::value); | |
134 | ||
135 | struct epilogue_secure_block_t { | |
136 | __u8 late_flags; | |
137 | __u8 padding[CRYPTO_BLOCK_SIZE - sizeof(late_flags)]; | |
138 | ||
139 | __u8 ciphers_private_data[]; | |
140 | } __attribute__((packed)); | |
141 | static_assert(sizeof(epilogue_secure_block_t) % CRYPTO_BLOCK_SIZE == 0); | |
142 | static_assert(std::is_standard_layout<epilogue_secure_block_t>::value); | |
143 | ||
144 | ||
145 | static constexpr uint32_t FRAME_PREAMBLE_SIZE = sizeof(preamble_block_t); | |
146 | static constexpr uint32_t FRAME_PLAIN_EPILOGUE_SIZE = | |
147 | sizeof(epilogue_plain_block_t); | |
148 | static constexpr uint32_t FRAME_SECURE_EPILOGUE_SIZE = | |
149 | sizeof(epilogue_secure_block_t); | |
150 | ||
151 | #define FRAME_FLAGS_LATEABRT (1<<0) /* frame was aborted after txing data */ | |
152 | ||
153 | static uint32_t segment_onwire_size(const uint32_t logical_size) | |
154 | { | |
155 | return p2roundup<uint32_t>(logical_size, CRYPTO_BLOCK_SIZE); | |
156 | } | |
157 | ||
158 | static ceph::bufferlist segment_onwire_bufferlist(ceph::bufferlist&& bl) | |
159 | { | |
160 | const auto padding_size = segment_onwire_size(bl.length()) - bl.length(); | |
161 | if (padding_size) { | |
162 | bl.append_zero(padding_size); | |
163 | } | |
164 | return bl; | |
165 | } | |
166 | ||
167 | template <class T, uint16_t... SegmentAlignmentVs> | |
168 | struct Frame { | |
169 | static constexpr size_t SegmentsNumV = sizeof...(SegmentAlignmentVs); | |
170 | static_assert(SegmentsNumV > 0 && SegmentsNumV <= MAX_NUM_SEGMENTS); | |
171 | protected: | |
172 | std::array<ceph::bufferlist, SegmentsNumV> segments; | |
173 | ||
174 | private: | |
175 | static constexpr std::array<uint16_t, SegmentsNumV> alignments { | |
176 | SegmentAlignmentVs... | |
177 | }; | |
178 | ceph::bufferlist::contiguous_filler preamble_filler; | |
179 | ||
180 | __u8 calc_num_segments( | |
181 | const std::array<segment_t, MAX_NUM_SEGMENTS>& segments) | |
182 | { | |
183 | for (__u8 num = SegmentsNumV; num > 0; num--) { | |
184 | if (segments[num-1].length) { | |
185 | return num; | |
186 | } | |
187 | } | |
188 | // frame always has at least one segment. | |
189 | return 1; | |
190 | } | |
191 | ||
192 | // craft the main preamble. It's always present regardless of the number | |
193 | // of segments message is composed from. | |
194 | void fill_preamble() { | |
195 | ceph_assert(std::size(segments) <= MAX_NUM_SEGMENTS); | |
196 | ||
197 | preamble_block_t main_preamble; | |
92f5a8d4 | 198 | // FIPS zeroization audit 20191115: this memset is not security related. |
11fdf7f2 TL |
199 | ::memset(&main_preamble, 0, sizeof(main_preamble)); |
200 | ||
201 | main_preamble.tag = static_cast<__u8>(T::tag); | |
202 | ceph_assert(main_preamble.tag != 0); | |
203 | ||
204 | // implementation detail: the first bufferlist of Frame::segments carries | |
205 | // space for preamble. This glueing isn't a part of the onwire format but | |
206 | // just our private detail. | |
207 | main_preamble.segments.front().length = | |
208 | segments.front().length() - FRAME_PREAMBLE_SIZE; | |
209 | main_preamble.segments.front().alignment = alignments.front(); | |
210 | ||
211 | // there is no business in issuing frame without at least one segment | |
212 | // filled. | |
213 | if constexpr(SegmentsNumV > 1) { | |
214 | for (__u8 idx = 1; idx < SegmentsNumV; idx++) { | |
215 | main_preamble.segments[idx].length = segments[idx].length(); | |
216 | main_preamble.segments[idx].alignment = alignments[idx]; | |
217 | } | |
218 | } | |
219 | // calculate the number of non-empty segments. | |
220 | // TODO: reorder segments to get DATA first | |
221 | main_preamble.num_segments = calc_num_segments(main_preamble.segments); | |
222 | ||
223 | main_preamble.crc = | |
224 | ceph_crc32c(0, reinterpret_cast<unsigned char *>(&main_preamble), | |
225 | sizeof(main_preamble) - sizeof(main_preamble.crc)); | |
226 | ||
227 | preamble_filler.copy_in(sizeof(main_preamble), | |
228 | reinterpret_cast<const char *>(&main_preamble)); | |
229 | } | |
230 | ||
231 | template <size_t... Is> | |
232 | void reset_tx_handler( | |
233 | ceph::crypto::onwire::rxtx_t &session_stream_handlers, | |
234 | std::index_sequence<Is...>) | |
235 | { | |
236 | session_stream_handlers.tx->reset_tx_handler({ segments[Is].length()... }); | |
237 | } | |
238 | ||
239 | public: | |
240 | ceph::bufferlist get_buffer( | |
241 | ceph::crypto::onwire::rxtx_t &session_stream_handlers) | |
242 | { | |
243 | fill_preamble(); | |
244 | if (session_stream_handlers.tx) { | |
245 | // we're padding segments to biggest cipher's block size. Although | |
246 | // AES-GCM can live without that as it's a stream cipher, we don't | |
247 | // to be fixed to stream ciphers only. | |
248 | for (auto& segment : segments) { | |
249 | segment = segment_onwire_bufferlist(std::move(segment)); | |
250 | } | |
251 | ||
252 | // let's cipher allocate one huge buffer for entire ciphertext. | |
253 | reset_tx_handler( | |
254 | session_stream_handlers, std::make_index_sequence<SegmentsNumV>()); | |
255 | ||
256 | for (auto& segment : segments) { | |
257 | if (segment.length()) { | |
258 | session_stream_handlers.tx->authenticated_encrypt_update( | |
259 | std::move(segment)); | |
260 | } | |
261 | } | |
262 | ||
263 | // in secure mode we craft only the late_flags. Signature (for AES-GCM | |
264 | // called auth tag) will be added by the cipher. | |
265 | { | |
266 | epilogue_secure_block_t epilogue; | |
92f5a8d4 TL |
267 | // FIPS zeroization audit 20191115: this memset is not security |
268 | // related. | |
11fdf7f2 TL |
269 | ::memset(&epilogue, 0, sizeof(epilogue)); |
270 | ceph::bufferlist epilogue_bl; | |
271 | epilogue_bl.append(reinterpret_cast<const char*>(&epilogue), | |
272 | sizeof(epilogue)); | |
273 | session_stream_handlers.tx->authenticated_encrypt_update(epilogue_bl); | |
274 | } | |
275 | return session_stream_handlers.tx->authenticated_encrypt_final(); | |
276 | } else { | |
277 | // plain mode | |
278 | epilogue_plain_block_t epilogue; | |
92f5a8d4 | 279 | // FIPS zeroization audit 20191115: this memset is not security related. |
11fdf7f2 TL |
280 | ::memset(&epilogue, 0, sizeof(epilogue)); |
281 | ||
282 | ceph::bufferlist::const_iterator hdriter(&segments.front(), | |
283 | FRAME_PREAMBLE_SIZE); | |
284 | epilogue.crc_values[SegmentIndex::Control::PAYLOAD] = | |
285 | hdriter.crc32c(hdriter.get_remaining(), -1); | |
286 | if constexpr(SegmentsNumV > 1) { | |
287 | for (__u8 idx = 1; idx < SegmentsNumV; idx++) { | |
288 | epilogue.crc_values[idx] = segments[idx].crc32c(-1); | |
289 | } | |
290 | } | |
291 | ||
292 | ceph::bufferlist ret; | |
293 | for (auto& segment : segments) { | |
294 | ret.claim_append(segment); | |
295 | } | |
296 | ret.append(reinterpret_cast<const char*>(&epilogue), sizeof(epilogue)); | |
297 | return ret; | |
298 | } | |
299 | } | |
300 | ||
301 | Frame() | |
302 | : preamble_filler(segments.front().append_hole(FRAME_PREAMBLE_SIZE)) { | |
303 | } | |
304 | ||
305 | public: | |
306 | }; | |
307 | ||
308 | ||
309 | // ControlFrames are used to manage transceiver state (like connections) and | |
310 | // orchestrate transfers of MessageFrames. They use only single segment with | |
311 | // marshalling facilities -- derived classes specify frame structure through | |
312 | // Args pack while ControlFrame provides common encode/decode machinery. | |
313 | template <class C, typename... Args> | |
314 | class ControlFrame : public Frame<C, segment_t::DEFAULT_ALIGNMENT /* single segment */> { | |
315 | protected: | |
316 | ceph::bufferlist &get_payload_segment() { | |
317 | return this->segments[SegmentIndex::Control::PAYLOAD]; | |
318 | } | |
319 | ||
320 | // this tuple is only used when decoding values from a payload segment | |
321 | std::tuple<Args...> _values; | |
322 | ||
323 | // FIXME: for now, we assume specific features for the purpoess of encoding | |
324 | // the frames themselves (*not* messages in message frames!). | |
325 | uint64_t features = msgr2_frame_assumed; | |
326 | ||
327 | template <typename T> | |
328 | inline void _encode_payload_each(T &t) { | |
329 | if constexpr (std::is_same<T, std::vector<uint32_t> const>()) { | |
330 | encode((uint32_t)t.size(), this->get_payload_segment(), features); | |
331 | for (const auto &elem : t) { | |
332 | encode(elem, this->get_payload_segment(), features); | |
333 | } | |
334 | } else { | |
335 | encode(t, this->get_payload_segment(), features); | |
336 | } | |
337 | } | |
338 | ||
339 | template <typename T> | |
340 | inline void _decode_payload_each(T &t, bufferlist::const_iterator &ti) const { | |
341 | if constexpr (std::is_same<T, std::vector<uint32_t>>()) { | |
342 | uint32_t size; | |
343 | decode(size, ti); | |
344 | t.resize(size); | |
345 | for (uint32_t i = 0; i < size; ++i) { | |
346 | decode(t[i], ti); | |
347 | } | |
348 | } else { | |
349 | decode(t, ti); | |
350 | } | |
351 | } | |
352 | ||
353 | template <std::size_t... Is> | |
354 | inline void _decode_payload(bufferlist::const_iterator &ti, | |
355 | std::index_sequence<Is...>) const { | |
356 | (_decode_payload_each((Args &)std::get<Is>(_values), ti), ...); | |
357 | } | |
358 | ||
359 | template <std::size_t N> | |
360 | inline decltype(auto) get_val() { | |
361 | return std::get<N>(_values); | |
362 | } | |
363 | ||
364 | ControlFrame() | |
365 | : Frame<C, segment_t::DEFAULT_ALIGNMENT /* single segment */>() { | |
366 | } | |
367 | ||
368 | void _encode(const Args &... args) { | |
369 | (_encode_payload_each(args), ...); | |
370 | } | |
371 | ||
372 | void _decode(const ceph::bufferlist &bl) { | |
373 | auto ti = bl.cbegin(); | |
374 | _decode_payload(ti, std::index_sequence_for<Args...>()); | |
375 | } | |
376 | ||
377 | public: | |
378 | static C Encode(const Args &... args) { | |
379 | C c; | |
380 | c._encode(args...); | |
381 | return c; | |
382 | } | |
383 | ||
384 | static C Decode(const ceph::bufferlist &payload) { | |
385 | C c; | |
386 | c._decode(payload); | |
387 | return c; | |
388 | } | |
389 | }; | |
390 | ||
391 | struct HelloFrame : public ControlFrame<HelloFrame, | |
392 | uint8_t, // entity type | |
393 | entity_addr_t> { // peer address | |
394 | static const Tag tag = Tag::HELLO; | |
395 | using ControlFrame::Encode; | |
396 | using ControlFrame::Decode; | |
397 | ||
398 | inline uint8_t &entity_type() { return get_val<0>(); } | |
399 | inline entity_addr_t &peer_addr() { return get_val<1>(); } | |
400 | ||
401 | protected: | |
402 | using ControlFrame::ControlFrame; | |
403 | }; | |
404 | ||
405 | struct AuthRequestFrame : public ControlFrame<AuthRequestFrame, | |
406 | uint32_t, // auth method | |
407 | vector<uint32_t>, // preferred modes | |
408 | bufferlist> { // auth payload | |
409 | static const Tag tag = Tag::AUTH_REQUEST; | |
410 | using ControlFrame::Encode; | |
411 | using ControlFrame::Decode; | |
412 | ||
413 | inline uint32_t &method() { return get_val<0>(); } | |
414 | inline vector<uint32_t> &preferred_modes() { return get_val<1>(); } | |
415 | inline bufferlist &auth_payload() { return get_val<2>(); } | |
416 | ||
417 | protected: | |
418 | using ControlFrame::ControlFrame; | |
419 | }; | |
420 | ||
421 | struct AuthBadMethodFrame : public ControlFrame<AuthBadMethodFrame, | |
422 | uint32_t, // method | |
423 | int32_t, // result | |
424 | std::vector<uint32_t>, // allowed methods | |
425 | std::vector<uint32_t>> { // allowed modes | |
426 | static const Tag tag = Tag::AUTH_BAD_METHOD; | |
427 | using ControlFrame::Encode; | |
428 | using ControlFrame::Decode; | |
429 | ||
430 | inline uint32_t &method() { return get_val<0>(); } | |
431 | inline int32_t &result() { return get_val<1>(); } | |
432 | inline std::vector<uint32_t> &allowed_methods() { return get_val<2>(); } | |
433 | inline std::vector<uint32_t> &allowed_modes() { return get_val<3>(); } | |
434 | ||
435 | protected: | |
436 | using ControlFrame::ControlFrame; | |
437 | }; | |
438 | ||
439 | struct AuthReplyMoreFrame : public ControlFrame<AuthReplyMoreFrame, | |
440 | bufferlist> { // auth payload | |
441 | static const Tag tag = Tag::AUTH_REPLY_MORE; | |
442 | using ControlFrame::Encode; | |
443 | using ControlFrame::Decode; | |
444 | ||
445 | inline bufferlist &auth_payload() { return get_val<0>(); } | |
446 | ||
447 | protected: | |
448 | using ControlFrame::ControlFrame; | |
449 | }; | |
450 | ||
451 | struct AuthRequestMoreFrame : public ControlFrame<AuthRequestMoreFrame, | |
452 | bufferlist> { // auth payload | |
453 | static const Tag tag = Tag::AUTH_REQUEST_MORE; | |
454 | using ControlFrame::Encode; | |
455 | using ControlFrame::Decode; | |
456 | ||
457 | inline bufferlist &auth_payload() { return get_val<0>(); } | |
458 | ||
459 | protected: | |
460 | using ControlFrame::ControlFrame; | |
461 | }; | |
462 | ||
463 | struct AuthDoneFrame : public ControlFrame<AuthDoneFrame, | |
464 | uint64_t, // global id | |
465 | uint32_t, // connection mode | |
466 | bufferlist> { // auth method payload | |
467 | static const Tag tag = Tag::AUTH_DONE; | |
468 | using ControlFrame::Encode; | |
469 | using ControlFrame::Decode; | |
470 | ||
471 | inline uint64_t &global_id() { return get_val<0>(); } | |
472 | inline uint32_t &con_mode() { return get_val<1>(); } | |
473 | inline bufferlist &auth_payload() { return get_val<2>(); } | |
474 | ||
475 | protected: | |
476 | using ControlFrame::ControlFrame; | |
477 | }; | |
478 | ||
479 | struct AuthSignatureFrame | |
480 | : public ControlFrame<AuthSignatureFrame, | |
481 | sha256_digest_t> { | |
482 | static const Tag tag = Tag::AUTH_SIGNATURE; | |
483 | using ControlFrame::Encode; | |
484 | using ControlFrame::Decode; | |
485 | ||
486 | inline sha256_digest_t &signature() { return get_val<0>(); } | |
487 | ||
488 | protected: | |
489 | using ControlFrame::ControlFrame; | |
490 | }; | |
491 | ||
492 | struct ClientIdentFrame | |
493 | : public ControlFrame<ClientIdentFrame, | |
494 | entity_addrvec_t, // my addresses | |
495 | entity_addr_t, // target address | |
496 | int64_t, // global_id | |
497 | uint64_t, // global seq | |
498 | uint64_t, // supported features | |
499 | uint64_t, // required features | |
500 | uint64_t, // flags | |
501 | uint64_t> { // client cookie | |
502 | static const Tag tag = Tag::CLIENT_IDENT; | |
503 | using ControlFrame::Encode; | |
504 | using ControlFrame::Decode; | |
505 | ||
506 | inline entity_addrvec_t &addrs() { return get_val<0>(); } | |
507 | inline entity_addr_t &target_addr() { return get_val<1>(); } | |
508 | inline int64_t &gid() { return get_val<2>(); } | |
509 | inline uint64_t &global_seq() { return get_val<3>(); } | |
510 | inline uint64_t &supported_features() { return get_val<4>(); } | |
511 | inline uint64_t &required_features() { return get_val<5>(); } | |
512 | inline uint64_t &flags() { return get_val<6>(); } | |
513 | inline uint64_t &cookie() { return get_val<7>(); } | |
514 | ||
515 | protected: | |
516 | using ControlFrame::ControlFrame; | |
517 | }; | |
518 | ||
519 | struct ServerIdentFrame | |
520 | : public ControlFrame<ServerIdentFrame, | |
521 | entity_addrvec_t, // my addresses | |
522 | int64_t, // global_id | |
523 | uint64_t, // global seq | |
524 | uint64_t, // supported features | |
525 | uint64_t, // required features | |
526 | uint64_t, // flags | |
527 | uint64_t> { // server cookie | |
528 | static const Tag tag = Tag::SERVER_IDENT; | |
529 | using ControlFrame::Encode; | |
530 | using ControlFrame::Decode; | |
531 | ||
532 | inline entity_addrvec_t &addrs() { return get_val<0>(); } | |
533 | inline int64_t &gid() { return get_val<1>(); } | |
534 | inline uint64_t &global_seq() { return get_val<2>(); } | |
535 | inline uint64_t &supported_features() { return get_val<3>(); } | |
536 | inline uint64_t &required_features() { return get_val<4>(); } | |
537 | inline uint64_t &flags() { return get_val<5>(); } | |
538 | inline uint64_t &cookie() { return get_val<6>(); } | |
539 | ||
540 | protected: | |
541 | using ControlFrame::ControlFrame; | |
542 | }; | |
543 | ||
544 | struct ReconnectFrame | |
545 | : public ControlFrame<ReconnectFrame, | |
546 | entity_addrvec_t, // my addresses | |
547 | uint64_t, // client cookie | |
548 | uint64_t, // server cookie | |
549 | uint64_t, // global sequence | |
550 | uint64_t, // connect sequence | |
551 | uint64_t> { // message sequence | |
552 | static const Tag tag = Tag::SESSION_RECONNECT; | |
553 | using ControlFrame::Encode; | |
554 | using ControlFrame::Decode; | |
555 | ||
556 | inline entity_addrvec_t &addrs() { return get_val<0>(); } | |
557 | inline uint64_t &client_cookie() { return get_val<1>(); } | |
558 | inline uint64_t &server_cookie() { return get_val<2>(); } | |
559 | inline uint64_t &global_seq() { return get_val<3>(); } | |
560 | inline uint64_t &connect_seq() { return get_val<4>(); } | |
561 | inline uint64_t &msg_seq() { return get_val<5>(); } | |
562 | ||
563 | protected: | |
564 | using ControlFrame::ControlFrame; | |
565 | }; | |
566 | ||
567 | struct ResetFrame : public ControlFrame<ResetFrame, | |
568 | bool> { // full reset | |
569 | static const Tag tag = Tag::SESSION_RESET; | |
570 | using ControlFrame::Encode; | |
571 | using ControlFrame::Decode; | |
572 | ||
573 | inline bool &full() { return get_val<0>(); } | |
574 | ||
575 | protected: | |
576 | using ControlFrame::ControlFrame; | |
577 | }; | |
578 | ||
579 | struct RetryFrame : public ControlFrame<RetryFrame, | |
580 | uint64_t> { // connection seq | |
581 | static const Tag tag = Tag::SESSION_RETRY; | |
582 | using ControlFrame::Encode; | |
583 | using ControlFrame::Decode; | |
584 | ||
585 | inline uint64_t &connect_seq() { return get_val<0>(); } | |
586 | ||
587 | protected: | |
588 | using ControlFrame::ControlFrame; | |
589 | }; | |
590 | ||
591 | struct RetryGlobalFrame : public ControlFrame<RetryGlobalFrame, | |
592 | uint64_t> { // global seq | |
593 | static const Tag tag = Tag::SESSION_RETRY_GLOBAL; | |
594 | using ControlFrame::Encode; | |
595 | using ControlFrame::Decode; | |
596 | ||
597 | inline uint64_t &global_seq() { return get_val<0>(); } | |
598 | ||
599 | protected: | |
600 | using ControlFrame::ControlFrame; | |
601 | }; | |
602 | ||
603 | struct WaitFrame : public ControlFrame<WaitFrame> { | |
604 | static const Tag tag = Tag::WAIT; | |
605 | using ControlFrame::Encode; | |
606 | using ControlFrame::Decode; | |
607 | ||
608 | protected: | |
609 | using ControlFrame::ControlFrame; | |
610 | }; | |
611 | ||
612 | struct ReconnectOkFrame : public ControlFrame<ReconnectOkFrame, | |
613 | uint64_t> { // message seq | |
614 | static const Tag tag = Tag::SESSION_RECONNECT_OK; | |
615 | using ControlFrame::Encode; | |
616 | using ControlFrame::Decode; | |
617 | ||
618 | inline uint64_t &msg_seq() { return get_val<0>(); } | |
619 | ||
620 | protected: | |
621 | using ControlFrame::ControlFrame; | |
622 | }; | |
623 | ||
624 | struct IdentMissingFeaturesFrame | |
625 | : public ControlFrame<IdentMissingFeaturesFrame, | |
626 | uint64_t> { // missing features mask | |
627 | static const Tag tag = Tag::IDENT_MISSING_FEATURES; | |
628 | using ControlFrame::Encode; | |
629 | using ControlFrame::Decode; | |
630 | ||
631 | inline uint64_t &features() { return get_val<0>(); } | |
632 | ||
633 | protected: | |
634 | using ControlFrame::ControlFrame; | |
635 | }; | |
636 | ||
637 | struct KeepAliveFrame : public ControlFrame<KeepAliveFrame, | |
638 | utime_t> { // timestamp | |
639 | static const Tag tag = Tag::KEEPALIVE2; | |
640 | using ControlFrame::Encode; | |
641 | using ControlFrame::Decode; | |
642 | ||
643 | static KeepAliveFrame Encode() { | |
644 | return KeepAliveFrame::Encode(ceph_clock_now()); | |
645 | } | |
646 | ||
647 | inline utime_t ×tamp() { return get_val<0>(); } | |
648 | ||
649 | protected: | |
650 | using ControlFrame::ControlFrame; | |
651 | }; | |
652 | ||
653 | struct KeepAliveFrameAck : public ControlFrame<KeepAliveFrameAck, | |
654 | utime_t> { // ack timestamp | |
655 | static const Tag tag = Tag::KEEPALIVE2_ACK; | |
656 | using ControlFrame::Encode; | |
657 | using ControlFrame::Decode; | |
658 | ||
659 | inline utime_t ×tamp() { return get_val<0>(); } | |
660 | ||
661 | protected: | |
662 | using ControlFrame::ControlFrame; | |
663 | }; | |
664 | ||
665 | struct AckFrame : public ControlFrame<AckFrame, | |
666 | uint64_t> { // message sequence | |
667 | static const Tag tag = Tag::ACK; | |
668 | using ControlFrame::Encode; | |
669 | using ControlFrame::Decode; | |
670 | ||
671 | inline uint64_t &seq() { return get_val<0>(); } | |
672 | ||
673 | protected: | |
674 | using ControlFrame::ControlFrame; | |
675 | }; | |
676 | ||
677 | // This class is used for encoding/decoding header of the message frame. | |
678 | // Body is processed almost independently with the sole junction point | |
679 | // being the `extra_payload_len` passed to get_buffer(). | |
680 | struct MessageFrame : public Frame<MessageFrame, | |
681 | /* four segments */ | |
682 | segment_t::DEFAULT_ALIGNMENT, | |
683 | segment_t::DEFAULT_ALIGNMENT, | |
684 | segment_t::DEFAULT_ALIGNMENT, | |
685 | segment_t::PAGE_SIZE_ALIGNMENT> { | |
686 | struct { | |
687 | uint32_t front; | |
688 | uint32_t middle; | |
689 | uint32_t data; | |
690 | } len; | |
691 | ||
692 | static const Tag tag = Tag::MESSAGE; | |
693 | ||
694 | static MessageFrame Encode(const ceph_msg_header2 &msg_header, | |
695 | const ceph::bufferlist &front, | |
696 | const ceph::bufferlist &middle, | |
697 | const ceph::bufferlist &data) { | |
698 | MessageFrame f; | |
699 | f.segments[SegmentIndex::Msg::HEADER].append( | |
700 | reinterpret_cast<const char*>(&msg_header), sizeof(msg_header)); | |
701 | ||
702 | f.segments[SegmentIndex::Msg::FRONT] = front; | |
703 | f.segments[SegmentIndex::Msg::MIDDLE] = middle; | |
704 | f.segments[SegmentIndex::Msg::DATA] = data; | |
705 | ||
706 | return f; | |
707 | } | |
708 | ||
709 | using rx_segments_t = | |
710 | boost::container::static_vector<ceph::bufferlist, | |
711 | ceph::msgr::v2::MAX_NUM_SEGMENTS>; | |
712 | static MessageFrame Decode(rx_segments_t &&recv_segments) { | |
713 | MessageFrame f; | |
714 | // transfer segments' bufferlists. If a MessageFrame contains less | |
715 | // SegmentsNumV segments, the missing ones will be seen as zeroed. | |
716 | for (__u8 idx = 0; idx < std::size(recv_segments); idx++) { | |
717 | f.segments[idx] = std::move(recv_segments[idx]); | |
718 | } | |
719 | return f; | |
720 | } | |
721 | ||
722 | inline const ceph_msg_header2 &header() { | |
723 | auto& hdrbl = segments[SegmentIndex::Msg::HEADER]; | |
724 | return reinterpret_cast<const ceph_msg_header2&>(*hdrbl.c_str()); | |
725 | } | |
726 | ||
727 | ceph::bufferlist &front() { | |
728 | return segments[SegmentIndex::Msg::FRONT]; | |
729 | } | |
730 | ||
731 | ceph::bufferlist &middle() { | |
732 | return segments[SegmentIndex::Msg::MIDDLE]; | |
733 | } | |
734 | ||
735 | ceph::bufferlist &data() { | |
736 | return segments[SegmentIndex::Msg::DATA]; | |
737 | } | |
738 | ||
739 | uint32_t front_len() const { | |
740 | return segments[SegmentIndex::Msg::FRONT].length(); | |
741 | } | |
742 | ||
743 | uint32_t middle_len() const { | |
744 | return segments[SegmentIndex::Msg::MIDDLE].length(); | |
745 | } | |
746 | ||
747 | uint32_t data_len() const { | |
748 | return segments[SegmentIndex::Msg::DATA].length(); | |
749 | } | |
750 | ||
751 | protected: | |
752 | using Frame::Frame; | |
753 | }; | |
754 | ||
755 | } // namespace ceph::msgr::v2 | |
756 | ||
757 | #endif // _MSG_ASYNC_FRAMES_V2_ |