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 __u16 PAGE_SIZE_ALIGNMENT = 4096;
62
63 static constexpr __u16 DEFAULT_ALIGNMENT = sizeof(void *);
64
65 ceph_le32 length;
66 ceph_le16 alignment;
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 segment_t segments[MAX_NUM_SEGMENTS];
102 __u8 _reserved[2];
103
104 // CRC32 for this single preamble block.
105 ceph_le32 crc;
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;
131 ceph_le32 crc_values[MAX_NUM_SEGMENTS];
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 inline 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 std::move(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(const segment_t segments[])
181 {
182 for (__u8 num = SegmentsNumV; num > 0; num--) {
183 if (segments[num-1].length) {
184 return num;
185 }
186 }
187 // frame always has at least one segment.
188 return 1;
189 }
190
191 // craft the main preamble. It's always present regardless of the number
192 // of segments message is composed from.
193 void fill_preamble() {
194 ceph_assert(std::size(segments) <= MAX_NUM_SEGMENTS);
195
196 preamble_block_t main_preamble;
197 ::memset(&main_preamble, 0, sizeof(main_preamble));
198
199 main_preamble.tag = static_cast<__u8>(T::tag);
200 ceph_assert(main_preamble.tag != 0);
201
202 // implementation detail: the first bufferlist of Frame::segments carries
203 // space for preamble. This glueing isn't a part of the onwire format but
204 // just our private detail.
205 main_preamble.segments[0].length =
206 segments[0].length() - FRAME_PREAMBLE_SIZE;
207 main_preamble.segments[0].alignment = alignments[0];
208
209 // there is no business in issuing frame without at least one segment
210 // filled.
211 if constexpr(SegmentsNumV > 1) {
212 for (__u8 idx = 1; idx < SegmentsNumV; idx++) {
213 main_preamble.segments[idx].length = segments[idx].length();
214 main_preamble.segments[idx].alignment = alignments[idx];
215 }
216 }
217 // calculate the number of non-empty segments.
218 // TODO: reorder segments to get DATA first
219 main_preamble.num_segments = calc_num_segments(main_preamble.segments);
220
221 main_preamble.crc =
222 ceph_crc32c(0, reinterpret_cast<unsigned char *>(&main_preamble),
223 sizeof(main_preamble) - sizeof(main_preamble.crc));
224
225 preamble_filler.copy_in(sizeof(main_preamble),
226 reinterpret_cast<const char *>(&main_preamble));
227 }
228
229 template <size_t... Is>
230 void reset_tx_handler(
231 ceph::crypto::onwire::rxtx_t &session_stream_handlers,
232 std::index_sequence<Is...>)
233 {
234 session_stream_handlers.tx->reset_tx_handler({ segments[Is].length()... });
235 }
236
237 public:
238 ceph::bufferlist get_buffer(
239 ceph::crypto::onwire::rxtx_t &session_stream_handlers)
240 {
241 fill_preamble();
242 if (session_stream_handlers.tx) {
243 // we're padding segments to biggest cipher's block size. Although
244 // AES-GCM can live without that as it's a stream cipher, we don't
245 // to be fixed to stream ciphers only.
246 for (auto& segment : segments) {
247 segment = segment_onwire_bufferlist(std::move(segment));
248 }
249
250 // let's cipher allocate one huge buffer for entire ciphertext.
251 reset_tx_handler(
252 session_stream_handlers, std::make_index_sequence<SegmentsNumV>());
253
254 for (auto& segment : segments) {
255 if (segment.length()) {
256 session_stream_handlers.tx->authenticated_encrypt_update(
257 std::move(segment));
258 }
259 }
260
261 // in secure mode we craft only the late_flags. Signature (for AES-GCM
262 // called auth tag) will be added by the cipher.
263 {
264 epilogue_secure_block_t epilogue;
265 ::memset(&epilogue, 0, sizeof(epilogue));
266 ceph::bufferlist epilogue_bl;
267 epilogue_bl.append(reinterpret_cast<const char*>(&epilogue),
268 sizeof(epilogue));
269 session_stream_handlers.tx->authenticated_encrypt_update(epilogue_bl);
270 }
271 return session_stream_handlers.tx->authenticated_encrypt_final();
272 } else {
273 // plain mode
274 epilogue_plain_block_t epilogue;
275 ::memset(&epilogue, 0, sizeof(epilogue));
276
277 ceph::bufferlist::const_iterator hdriter(&segments.front(),
278 FRAME_PREAMBLE_SIZE);
279 epilogue.crc_values[SegmentIndex::Control::PAYLOAD] =
280 hdriter.crc32c(hdriter.get_remaining(), -1);
(1) Event stray_semicolon: |
An "if" statement with no "then" or "else" is suspicious. |
(2) Event remediation: |
Is the ";" after "if (false)" extraneous, or is the "if" itself unnecessary? |
281 if constexpr(SegmentsNumV > 1) {
282 for (__u8 idx = 1; idx < SegmentsNumV; idx++) {
283 epilogue.crc_values[idx] = segments[idx].crc32c(-1);
284 }
285 }
286
287 ceph::bufferlist ret;
288 for (auto& segment : segments) {
289 ret.claim_append(segment);
290 }
291 ret.append(reinterpret_cast<const char*>(&epilogue), sizeof(epilogue));
292 return ret;
293 }
294 }
295
296 Frame()
297 : preamble_filler(segments.front().append_hole(FRAME_PREAMBLE_SIZE)) {
298 }
299
300 public:
301 };
302
303
304 // ControlFrames are used to manage transceiver state (like connections) and
305 // orchestrate transfers of MessageFrames. They use only single segment with
306 // marshalling facilities -- derived classes specify frame structure through
307 // Args pack while ControlFrame provides common encode/decode machinery.
308 template <class C, typename... Args>
309 class ControlFrame : public Frame<C, segment_t::DEFAULT_ALIGNMENT /* single segment */> {
310 protected:
311 ceph::bufferlist &get_payload_segment() {
312 return this->segments[SegmentIndex::Control::PAYLOAD];
313 }
314
315 // this tuple is only used when decoding values from a payload segment
316 std::tuple<Args...> _values;
317
318 // FIXME: for now, we assume specific features for the purpoess of encoding
319 // the frames themselves (*not* messages in message frames!).
320 uint64_t features = msgr2_frame_assumed;
321
322 template <typename T>
323 inline void _encode_payload_each(T &t) {
324 if constexpr (std::is_same<T, std::vector<uint32_t> const>()) {
325 encode((uint32_t)t.size(), this->get_payload_segment(), features);
326 for (const auto &elem : t) {
327 encode(elem, this->get_payload_segment(), features);
328 }
329 } else {
330 encode(t, this->get_payload_segment(), features);
331 }
332 }
333
334 template <typename T>
335 inline void _decode_payload_each(T &t, bufferlist::const_iterator &ti) const {
336 if constexpr (std::is_same<T, std::vector<uint32_t>>()) {
337 uint32_t size;
338 decode(size, ti);
339 t.resize(size);
340 for (uint32_t i = 0; i < size; ++i) {
341 decode(t[i], ti);
342 }
343 } else {
344 decode(t, ti);
345 }
346 }
347
348 template <std::size_t... Is>
349 inline void _decode_payload(bufferlist::const_iterator &ti,
350 std::index_sequence<Is...>) const {
351 (_decode_payload_each((Args &)std::get<Is>(_values), ti), ...);
352 }
353
354 template <std::size_t N>
355 inline decltype(auto) get_val() {
356 return std::get<N>(_values);
357 }
358
359 ControlFrame()
360 : Frame<C, segment_t::DEFAULT_ALIGNMENT /* single segment */>() {
361 }
362
363 void _encode(const Args &... args) {
364 (_encode_payload_each(args), ...);
365 }
366
367 void _decode(const ceph::bufferlist &bl) {
368 auto ti = bl.cbegin();
369 _decode_payload(ti, std::index_sequence_for<Args...>());
370 }
371
372 public:
373 static C Encode(const Args &... args) {
374 C c;
375 c._encode(args...);
376 return c;
377 }
378
379 static C Decode(const ceph::bufferlist &payload) {
380 C c;
381 c._decode(payload);
382 return c;
383 }
384 };
385
386 struct HelloFrame : public ControlFrame<HelloFrame,
387 uint8_t, // entity type
388 entity_addr_t> { // peer address
389 static const Tag tag = Tag::HELLO;
390 using ControlFrame::Encode;
391 using ControlFrame::Decode;
392
393 inline uint8_t &entity_type() { return get_val<0>(); }
394 inline entity_addr_t &peer_addr() { return get_val<1>(); }
395
396 protected:
397 using ControlFrame::ControlFrame;
398 };
399
400 struct AuthRequestFrame : public ControlFrame<AuthRequestFrame,
401 uint32_t, // auth method
402 vector<uint32_t>, // preferred modes
403 bufferlist> { // auth payload
404 static const Tag tag = Tag::AUTH_REQUEST;
405 using ControlFrame::Encode;
406 using ControlFrame::Decode;
407
408 inline uint32_t &method() { return get_val<0>(); }
409 inline vector<uint32_t> &preferred_modes() { return get_val<1>(); }
410 inline bufferlist &auth_payload() { return get_val<2>(); }
411
412 protected:
413 using ControlFrame::ControlFrame;
414 };
415
416 struct AuthBadMethodFrame : public ControlFrame<AuthBadMethodFrame,
417 uint32_t, // method
418 int32_t, // result
419 std::vector<uint32_t>, // allowed methods
420 std::vector<uint32_t>> { // allowed modes
421 static const Tag tag = Tag::AUTH_BAD_METHOD;
422 using ControlFrame::Encode;
423 using ControlFrame::Decode;
424
425 inline uint32_t &method() { return get_val<0>(); }
426 inline int32_t &result() { return get_val<1>(); }
427 inline std::vector<uint32_t> &allowed_methods() { return get_val<2>(); }
428 inline std::vector<uint32_t> &allowed_modes() { return get_val<3>(); }
429
430 protected:
431 using ControlFrame::ControlFrame;
432 };
433
434 struct AuthReplyMoreFrame : public ControlFrame<AuthReplyMoreFrame,
435 bufferlist> { // auth payload
436 static const Tag tag = Tag::AUTH_REPLY_MORE;
437 using ControlFrame::Encode;
438 using ControlFrame::Decode;
439
440 inline bufferlist &auth_payload() { return get_val<0>(); }
441
442 protected:
443 using ControlFrame::ControlFrame;
444 };
445
446 struct AuthRequestMoreFrame : public ControlFrame<AuthRequestMoreFrame,
447 bufferlist> { // auth payload
448 static const Tag tag = Tag::AUTH_REQUEST_MORE;
449 using ControlFrame::Encode;
450 using ControlFrame::Decode;
451
452 inline bufferlist &auth_payload() { return get_val<0>(); }
453
454 protected:
455 using ControlFrame::ControlFrame;
456 };
457
458 struct AuthDoneFrame : public ControlFrame<AuthDoneFrame,
459 uint64_t, // global id
460 uint32_t, // connection mode
461 bufferlist> { // auth method payload
462 static const Tag tag = Tag::AUTH_DONE;
463 using ControlFrame::Encode;
464 using ControlFrame::Decode;
465
466 inline uint64_t &global_id() { return get_val<0>(); }
467 inline uint32_t &con_mode() { return get_val<1>(); }
468 inline bufferlist &auth_payload() { return get_val<2>(); }
469
470 protected:
471 using ControlFrame::ControlFrame;
472 };
473
474 struct AuthSignatureFrame
475 : public ControlFrame<AuthSignatureFrame,
476 sha256_digest_t> {
477 static const Tag tag = Tag::AUTH_SIGNATURE;
478 using ControlFrame::Encode;
479 using ControlFrame::Decode;
480
481 inline sha256_digest_t &signature() { return get_val<0>(); }
482
483 protected:
484 using ControlFrame::ControlFrame;
485 };
486
487 struct ClientIdentFrame
488 : public ControlFrame<ClientIdentFrame,
489 entity_addrvec_t, // my addresses
490 entity_addr_t, // target address
491 int64_t, // global_id
492 uint64_t, // global seq
493 uint64_t, // supported features
494 uint64_t, // required features
495 uint64_t, // flags
496 uint64_t> { // client cookie
497 static const Tag tag = Tag::CLIENT_IDENT;
498 using ControlFrame::Encode;
499 using ControlFrame::Decode;
500
501 inline entity_addrvec_t &addrs() { return get_val<0>(); }
502 inline entity_addr_t &target_addr() { return get_val<1>(); }
503 inline int64_t &gid() { return get_val<2>(); }
504 inline uint64_t &global_seq() { return get_val<3>(); }
505 inline uint64_t &supported_features() { return get_val<4>(); }
506 inline uint64_t &required_features() { return get_val<5>(); }
507 inline uint64_t &flags() { return get_val<6>(); }
508 inline uint64_t &cookie() { return get_val<7>(); }
509
510 protected:
511 using ControlFrame::ControlFrame;
512 };
513
514 struct ServerIdentFrame
515 : public ControlFrame<ServerIdentFrame,
516 entity_addrvec_t, // my addresses
517 int64_t, // global_id
518 uint64_t, // global seq
519 uint64_t, // supported features
520 uint64_t, // required features
521 uint64_t, // flags
522 uint64_t> { // server cookie
523 static const Tag tag = Tag::SERVER_IDENT;
524 using ControlFrame::Encode;
525 using ControlFrame::Decode;
526
527 inline entity_addrvec_t &addrs() { return get_val<0>(); }
528 inline int64_t &gid() { return get_val<1>(); }
529 inline uint64_t &global_seq() { return get_val<2>(); }
530 inline uint64_t &supported_features() { return get_val<3>(); }
531 inline uint64_t &required_features() { return get_val<4>(); }
532 inline uint64_t &flags() { return get_val<5>(); }
533 inline uint64_t &cookie() { return get_val<6>(); }
534
535 protected:
536 using ControlFrame::ControlFrame;
537 };
538
539 struct ReconnectFrame
540 : public ControlFrame<ReconnectFrame,
541 entity_addrvec_t, // my addresses
542 uint64_t, // client cookie
543 uint64_t, // server cookie
544 uint64_t, // global sequence
545 uint64_t, // connect sequence
546 uint64_t> { // message sequence
547 static const Tag tag = Tag::SESSION_RECONNECT;
548 using ControlFrame::Encode;
549 using ControlFrame::Decode;
550
551 inline entity_addrvec_t &addrs() { return get_val<0>(); }
552 inline uint64_t &client_cookie() { return get_val<1>(); }
553 inline uint64_t &server_cookie() { return get_val<2>(); }
554 inline uint64_t &global_seq() { return get_val<3>(); }
555 inline uint64_t &connect_seq() { return get_val<4>(); }
556 inline uint64_t &msg_seq() { return get_val<5>(); }
557
558 protected:
559 using ControlFrame::ControlFrame;
560 };
561
562 struct ResetFrame : public ControlFrame<ResetFrame,
563 bool> { // full reset
564 static const Tag tag = Tag::SESSION_RESET;
565 using ControlFrame::Encode;
566 using ControlFrame::Decode;
567
568 inline bool &full() { return get_val<0>(); }
569
570 protected:
571 using ControlFrame::ControlFrame;
572 };
573
574 struct RetryFrame : public ControlFrame<RetryFrame,
575 uint64_t> { // connection seq
576 static const Tag tag = Tag::SESSION_RETRY;
577 using ControlFrame::Encode;
578 using ControlFrame::Decode;
579
580 inline uint64_t &connect_seq() { return get_val<0>(); }
581
582 protected:
583 using ControlFrame::ControlFrame;
584 };
585
586 struct RetryGlobalFrame : public ControlFrame<RetryGlobalFrame,
587 uint64_t> { // global seq
588 static const Tag tag = Tag::SESSION_RETRY_GLOBAL;
589 using ControlFrame::Encode;
590 using ControlFrame::Decode;
591
592 inline uint64_t &global_seq() { return get_val<0>(); }
593
594 protected:
595 using ControlFrame::ControlFrame;
596 };
597
598 struct WaitFrame : public ControlFrame<WaitFrame> {
599 static const Tag tag = Tag::WAIT;
600 using ControlFrame::Encode;
601 using ControlFrame::Decode;
602
603 protected:
604 using ControlFrame::ControlFrame;
605 };
606
607 struct ReconnectOkFrame : public ControlFrame<ReconnectOkFrame,
608 uint64_t> { // message seq
609 static const Tag tag = Tag::SESSION_RECONNECT_OK;
610 using ControlFrame::Encode;
611 using ControlFrame::Decode;
612
613 inline uint64_t &msg_seq() { return get_val<0>(); }
614
615 protected:
616 using ControlFrame::ControlFrame;
617 };
618
619 struct IdentMissingFeaturesFrame
620 : public ControlFrame<IdentMissingFeaturesFrame,
621 uint64_t> { // missing features mask
622 static const Tag tag = Tag::IDENT_MISSING_FEATURES;
623 using ControlFrame::Encode;
624 using ControlFrame::Decode;
625
626 inline uint64_t &features() { return get_val<0>(); }
627
628 protected:
629 using ControlFrame::ControlFrame;
630 };
631
632 struct KeepAliveFrame : public ControlFrame<KeepAliveFrame,
633 utime_t> { // timestamp
634 static const Tag tag = Tag::KEEPALIVE2;
635 using ControlFrame::Encode;
636 using ControlFrame::Decode;
637
638 static KeepAliveFrame Encode() {
639 return KeepAliveFrame::Encode(ceph_clock_now());
640 }
641
642 inline utime_t ×tamp() { return get_val<0>(); }
643
644 protected:
645 using ControlFrame::ControlFrame;
646 };
647
648 struct KeepAliveFrameAck : public ControlFrame<KeepAliveFrameAck,
649 utime_t> { // ack timestamp
650 static const Tag tag = Tag::KEEPALIVE2_ACK;
651 using ControlFrame::Encode;
652 using ControlFrame::Decode;
653
654 inline utime_t ×tamp() { return get_val<0>(); }
655
656 protected:
657 using ControlFrame::ControlFrame;
658 };
659
660 struct AckFrame : public ControlFrame<AckFrame,
661 uint64_t> { // message sequence
662 static const Tag tag = Tag::ACK;
663 using ControlFrame::Encode;
664 using ControlFrame::Decode;
665
666 inline uint64_t &seq() { return get_val<0>(); }
667
668 protected:
669 using ControlFrame::ControlFrame;
670 };
671
672 // This class is used for encoding/decoding header of the message frame.
673 // Body is processed almost independently with the sole junction point
674 // being the `extra_payload_len` passed to get_buffer().
675 struct MessageFrame : public Frame<MessageFrame,
676 /* four segments */
677 segment_t::DEFAULT_ALIGNMENT,
678 segment_t::DEFAULT_ALIGNMENT,
679 segment_t::DEFAULT_ALIGNMENT,
680 segment_t::PAGE_SIZE_ALIGNMENT> {
681 struct {
682 uint32_t front;
683 uint32_t middle;
684 uint32_t data;
685 } len;
686
687 static const Tag tag = Tag::MESSAGE;
688
689 static MessageFrame Encode(const ceph_msg_header2 &msg_header,
690 const ceph::bufferlist &front,
691 const ceph::bufferlist &middle,
692 const ceph::bufferlist &data) {
693 MessageFrame f;
694 f.segments[SegmentIndex::Msg::HEADER].append(
695 reinterpret_cast<const char*>(&msg_header), sizeof(msg_header));
696
697 f.segments[SegmentIndex::Msg::FRONT] = front;
698 f.segments[SegmentIndex::Msg::MIDDLE] = middle;
699 f.segments[SegmentIndex::Msg::DATA] = data;
700
701 return f;
702 }
703
704 using rx_segments_t =
705 boost::container::static_vector<ceph::bufferlist,
706 ceph::msgr::v2::MAX_NUM_SEGMENTS>;
707 static MessageFrame Decode(rx_segments_t &&recv_segments) {
708 MessageFrame f;
709 // transfer segments' bufferlists. If a MessageFrame contains less
710 // SegmentsNumV segments, the missing ones will be seen as zeroed.
711 for (__u8 idx = 0; idx < std::size(recv_segments); idx++) {
712 f.segments[idx] = std::move(recv_segments[idx]);
713 }
714 return f;
715 }
716
717 inline const ceph_msg_header2 &header() {
718 auto& hdrbl = segments[SegmentIndex::Msg::HEADER];
719 return reinterpret_cast<const ceph_msg_header2&>(*hdrbl.c_str());
720 }
721
722 ceph::bufferlist &front() {
723 return segments[SegmentIndex::Msg::FRONT];
724 }
725
726 ceph::bufferlist &middle() {
727 return segments[SegmentIndex::Msg::MIDDLE];
728 }
729
730 ceph::bufferlist &data() {
731 return segments[SegmentIndex::Msg::DATA];
732 }
733
734 uint32_t front_len() const {
735 return segments[SegmentIndex::Msg::FRONT].length();
736 }
737
738 uint32_t middle_len() const {
739 return segments[SegmentIndex::Msg::MIDDLE].length();
740 }
741
742 uint32_t data_len() const {
743 return segments[SegmentIndex::Msg::DATA].length();
744 }
745
746 protected:
747 using Frame::Frame;
748 };
749
750 } // namespace ceph::msgr::v2
751
752 #endif // _MSG_ASYNC_FRAMES_V2_
753