1use core::fmt::Debug;
9use core::num::NonZeroU16;
10
11use assert_matches::assert_matches;
12use log::{debug, error, warn};
13use net_types::ip::Ip;
14use net_types::{MulticastAddress as _, SpecifiedAddr, Witness as _};
15use netstack3_base::socket::{
16 AddrIsMappedError, AddrVec, AddrVecIter, ConnAddr, ConnIpAddr, InsertError, IpAddrVec,
17 ListenerAddr, ListenerIpAddr, SocketIpAddr, SocketIpAddrExt as _,
18};
19use netstack3_base::{
20 BidirectionalConverter as _, Control, CounterContext, CtxPair, EitherDeviceId, GsoInfo,
21 IpDeviceAddr, Marks, Mss, NotFoundError, PacketType, Payload, Segment, SegmentHeader, SeqNum,
22 StrongDeviceIdentifier, VerifiedTcpSegment, WeakDeviceIdentifier,
23};
24use netstack3_filter::{
25 FilterIpExt, SocketIngressFilterResult, SocketOpsFilter, TransportPacketSerializer,
26};
27use netstack3_hashmap::hash_map;
28use netstack3_ip::socket::{IpSockCreationError, IpSocketArgs, MmsError};
29use netstack3_ip::{
30 IpHeaderInfo, IpTransportContext, LocalDeliveryPacketInfo, MaybeSegmentableTransportSerializer,
31 ReceiveIpPacketMeta, TransportIpContext,
32};
33use netstack3_trace::trace_duration;
34use packet::{
35 BufferMut, BufferView as _, EmptyBuf, FragmentedByteSlice, InnerPacketBuilder,
36 NestablePacketBuilder as _, ParseBuffer,
37};
38use packet_formats::error::ParseError;
39use packet_formats::ip::IpProto;
40use packet_formats::tcp::{
41 TcpFlowAndSeqNum, TcpOptionsTooLongError, TcpParseArgs, TcpSegment, TcpSegmentBuilder,
42 TcpSegmentBuilderWithOptions, TcpSegmentRaw,
43};
44
45use crate::internal::base::{BufferSizes, ConnectionError, SocketOptions, TcpIpSockOptions};
46use crate::internal::counters::{
47 self, TcpCounterContext, TcpCountersRefs, TcpCountersWithoutSocket,
48};
49use crate::internal::socket::generators::{IsnGenerator, TimestampOffsetGenerator};
50use crate::internal::socket::{
51 self, AsThisStack as _, Connection, CoreTxMetadataContext, DemuxState, DeviceIpSocketHandler,
52 DoSendLimit, DualStackBaseIpExt, DualStackDemuxIdConverter as _, DualStackIpExt, EitherStack,
53 HandshakeStatus, Listener, ListenerAddrState, MaybeDualStack, PrimaryRc, TcpApi,
54 TcpBindingsContext, TcpBindingsTypes, TcpContext, TcpDemuxContext, TcpDualStackContext,
55 TcpIpTransportContext, TcpPortSpec, TcpSocketId, TcpSocketSetEntry, TcpSocketState,
56 TcpSocketStateInner, TcpSocketTxMetadata,
57};
58use crate::internal::state::{
59 BufferProvider, Closed, DataAcked, Initial, NewlyClosed, State, TimeWait,
60};
61
62impl<BT: TcpBindingsTypes> BufferProvider<BT::ReceiveBuffer, BT::SendBuffer> for BT {
63 type ActiveOpen = BT::ListenerNotifierOrProvidedBuffers;
64
65 type PassiveOpen = BT::ReturnedBuffers;
66
67 fn new_passive_open_buffers(
68 buffer_sizes: BufferSizes,
69 ) -> (BT::ReceiveBuffer, BT::SendBuffer, Self::PassiveOpen) {
70 BT::new_passive_open_buffers(buffer_sizes)
71 }
72}
73
74pub type DualStackTcpSocketId<I, D, BT> = <I as DualStackBaseIpExt>::DemuxSocketId<D, BT>;
76
77impl<I, BC, CC> IpTransportContext<I, BC, CC> for TcpIpTransportContext
78where
79 I: DualStackIpExt,
80 BC: TcpBindingsContext<CC::DeviceId>
81 + BufferProvider<
82 BC::ReceiveBuffer,
83 BC::SendBuffer,
84 ActiveOpen = <BC as TcpBindingsTypes>::ListenerNotifierOrProvidedBuffers,
85 PassiveOpen = <BC as TcpBindingsTypes>::ReturnedBuffers,
86 >,
87 CC: TcpContext<I, BC> + TcpContext<I::OtherVersion, BC>,
88{
89 type EarlyDemuxSocket = DualStackTcpSocketId<I, CC::WeakDeviceId, BC>;
90
91 fn early_demux<B: ParseBuffer>(
92 core_ctx: &mut CC,
93 device: &CC::DeviceId,
94 src_ip: I::Addr,
95 dst_ip: I::Addr,
96 buffer: B,
97 ) -> Option<Self::EarlyDemuxSocket> {
98 early_demux_ip_packet::<I, _, _, _>(core_ctx, device, src_ip, dst_ip, buffer)
99 }
100
101 fn receive_icmp_error(
102 core_ctx: &mut CC,
103 bindings_ctx: &mut BC,
104 _device: &CC::DeviceId,
105 original_src_ip: Option<SpecifiedAddr<I::Addr>>,
106 original_dst_ip: SpecifiedAddr<I::Addr>,
107 mut original_body: &[u8],
108 err: I::ErrorCode,
109 ) {
110 let mut buffer = &mut original_body;
111 let Some(flow_and_seqnum) = buffer.take_obj_front::<TcpFlowAndSeqNum>() else {
112 error!("received an ICMP error but its body is less than 8 bytes");
113 return;
114 };
115
116 let Some(original_src_ip) = original_src_ip else { return };
117 let Some(original_src_port) = NonZeroU16::new(flow_and_seqnum.src_port()) else { return };
118 let Some(original_dst_port) = NonZeroU16::new(flow_and_seqnum.dst_port()) else { return };
119 let original_seqnum = SeqNum::new(flow_and_seqnum.sequence_num());
120
121 TcpApi::<I, _>::new(CtxPair { core_ctx, bindings_ctx }).on_icmp_error(
122 original_src_ip,
123 original_dst_ip,
124 original_src_port,
125 original_dst_port,
126 original_seqnum,
127 err.into(),
128 );
129 }
130
131 fn receive_ip_packet<B: BufferMut, H: IpHeaderInfo<I>>(
132 core_ctx: &mut CC,
133 bindings_ctx: &mut BC,
134 device: &CC::DeviceId,
135 remote_ip: I::RecvSrcAddr,
136 local_ip: SpecifiedAddr<I::Addr>,
137 mut buffer: B,
138 info: &mut LocalDeliveryPacketInfo<I, H>,
139 early_demux_socket: Option<Self::EarlyDemuxSocket>,
140 ) -> Result<(), (B, I::IcmpError)> {
141 let LocalDeliveryPacketInfo { meta, header_info, marks, packet_type, gso_info } = info;
142 let ReceiveIpPacketMeta { broadcast, transparent_override, parsing_context } = meta;
143 if let Some(delivery) = transparent_override {
144 warn!(
145 "TODO(https://fxbug.dev/337009139): transparent proxy not supported for TCP \
146 sockets; will not override dispatch to perform local delivery to {delivery:?}"
147 );
148 }
149
150 if broadcast.is_some() {
160 CounterContext::<TcpCountersWithoutSocket<I>>::counters(core_ctx)
161 .invalid_ip_addrs_received
162 .increment();
163 debug!("tcp: dropping broadcast TCP packet");
164 return Ok(());
165 }
166 if local_ip.is_multicast() {
167 CounterContext::<TcpCountersWithoutSocket<I>>::counters(core_ctx)
168 .invalid_ip_addrs_received
169 .increment();
170 debug!("tcp: dropping multicast TCP packet");
171 return Ok(());
172 }
173
174 let remote_ip = match SpecifiedAddr::new(remote_ip.into_addr()) {
175 None => {
176 CounterContext::<TcpCountersWithoutSocket<I>>::counters(core_ctx)
177 .invalid_ip_addrs_received
178 .increment();
179 debug!("tcp: source address unspecified, dropping the packet");
180 return Ok(());
181 }
182 Some(src_ip) => src_ip,
183 };
184 let remote_ip: SocketIpAddr<_> = match remote_ip.try_into() {
185 Ok(remote_ip) => remote_ip,
186 Err(AddrIsMappedError {}) => {
187 CounterContext::<TcpCountersWithoutSocket<I>>::counters(core_ctx)
188 .invalid_ip_addrs_received
189 .increment();
190 debug!("tcp: source address is mapped (ipv4-mapped-ipv6), dropping the packet");
191 return Ok(());
192 }
193 };
194 let local_ip: SocketIpAddr<_> = match local_ip.try_into() {
195 Ok(local_ip) => local_ip,
196 Err(AddrIsMappedError {}) => {
197 CounterContext::<TcpCountersWithoutSocket<I>>::counters(core_ctx)
198 .invalid_ip_addrs_received
199 .increment();
200 debug!("tcp: local address is mapped (ipv4-mapped-ipv6), dropping the packet");
201 return Ok(());
202 }
203 };
204 let packet = match buffer.parse_with::<_, TcpSegment<_>>(TcpParseArgs::with_context(
205 remote_ip.addr(),
206 local_ip.addr(),
207 parsing_context,
208 )) {
209 Ok(packet) => packet,
210 Err(err) => {
211 CounterContext::<TcpCountersWithoutSocket<I>>::counters(core_ctx)
212 .invalid_segments_received
213 .increment();
214 debug!("tcp: failed parsing incoming packet {:?}", err);
215 match err {
216 ParseError::Checksum => {
217 CounterContext::<TcpCountersWithoutSocket<I>>::counters(core_ctx)
218 .checksum_errors
219 .increment();
220 }
221 ParseError::NotSupported | ParseError::NotExpected | ParseError::Format => {}
222 }
223 return Ok(());
224 }
225 };
226 let local_port = packet.dst_port();
227 let remote_port = packet.src_port();
228 let incoming = match VerifiedTcpSegment::try_from(packet) {
229 Ok(segment) => segment,
230 Err(err) => {
231 CounterContext::<TcpCountersWithoutSocket<I>>::counters(core_ctx)
232 .invalid_segments_received
233 .increment();
234 debug!("tcp: malformed segment {:?}", err);
235 return Ok(());
236 }
237 };
238 let conn_addr =
239 ConnIpAddr { local: (local_ip, local_port), remote: (remote_ip, remote_port) };
240
241 CounterContext::<TcpCountersWithoutSocket<I>>::counters(core_ctx)
242 .valid_segments_received
243 .increment();
244 handle_incoming_packet::<I, _, _, _>(
245 core_ctx,
246 bindings_ctx,
247 conn_addr,
248 device,
249 header_info,
250 &incoming,
251 marks,
252 *packet_type,
253 *gso_info,
254 early_demux_socket,
255 );
256 Ok(())
257 }
258}
259
260fn early_demux_ip_packet<I, BC, CC, B>(
261 core_ctx: &mut CC,
262 device: &CC::DeviceId,
263 src_ip: I::Addr,
264 dst_ip: I::Addr,
265 mut buffer: B,
266) -> Option<DualStackTcpSocketId<I, CC::WeakDeviceId, BC>>
267where
268 I: DualStackIpExt,
269 BC: TcpBindingsContext<CC::DeviceId>,
270 CC: TcpContext<I, BC> + TcpContext<I::OtherVersion, BC>,
271 B: ParseBuffer,
272{
273 let Ok(packet) = buffer.parse_with::<_, TcpSegmentRaw<_>>(()) else {
274 return None;
277 };
278
279 let src_ip = SocketIpAddr::new(src_ip)?;
280 let dst_ip = SocketIpAddr::new(dst_ip)?;
281 let (src_port, dst_port) = packet.flow_header().src_dst();
282 let src_port = NonZeroU16::new(src_port)?;
283 let dst_port = NonZeroU16::new(dst_port)?;
284 let device = device.downgrade();
285
286 core_ctx.with_demux(|demux: &DemuxState<I, _, _>| {
287 demux
288 .socketmap
289 .lookup_connected((src_ip, src_port), (dst_ip, dst_port), device)
290 .map(|entry| entry.id())
291 })
292}
293
294fn handle_incoming_packet<WireI, BC, CC, H>(
295 core_ctx: &mut CC,
296 bindings_ctx: &mut BC,
297 conn_addr: ConnIpAddr<WireI::Addr, NonZeroU16, NonZeroU16>,
298 incoming_device: &CC::DeviceId,
299 header_info: &H,
300 incoming: &VerifiedTcpSegment<'_>,
301 marks: &Marks,
302 packet_type: PacketType,
303 gso_info: Option<GsoInfo>,
304 mut early_demux_socket: Option<DualStackTcpSocketId<WireI, CC::WeakDeviceId, BC>>,
305) where
306 WireI: DualStackIpExt,
307 BC: TcpBindingsContext<CC::DeviceId>
308 + BufferProvider<
309 BC::ReceiveBuffer,
310 BC::SendBuffer,
311 ActiveOpen = <BC as TcpBindingsTypes>::ListenerNotifierOrProvidedBuffers,
312 PassiveOpen = <BC as TcpBindingsTypes>::ReturnedBuffers,
313 >,
314 CC: TcpContext<WireI, BC> + TcpContext<WireI::OtherVersion, BC>,
315 H: IpHeaderInfo<WireI>,
316{
317 trace_duration!("tcp::handle_incoming_packet");
318 let mut tw_reuse = None;
319
320 let addr: IpAddrVec<WireI, _> = if early_demux_socket.is_some() {
324 let (ip, port) = conn_addr.local;
325 IpAddrVec::new_listener(ip, port)
326 } else {
327 conn_addr.into()
328 };
329 let mut addrs_to_search = AddrVecIter::<WireI, CC::WeakDeviceId, TcpPortSpec>::with_device(
330 addr,
331 incoming_device.downgrade(),
332 );
333
334 enum FoundSocket<S> {
335 Yes(Option<S>),
338 No,
339 }
340 let found_socket = loop {
341 let sock = if let Some(early_demux_socket) = early_demux_socket.take() {
342 let device = match WireI::as_dual_stack_ip_socket(&early_demux_socket) {
343 EitherStack::ThisStack(conn_id) => conn_id.get_bound_device(core_ctx),
344 EitherStack::OtherStack(conn_id) => conn_id.get_bound_device(core_ctx),
345 };
346 let conn_addr = ConnAddr { ip: conn_addr, device };
347 Some(SocketLookupResult::Connection(early_demux_socket, conn_addr))
348 } else {
349 core_ctx.with_demux(|demux| lookup_socket::<WireI, CC, BC>(demux, &mut addrs_to_search))
350 };
351 match sock {
352 None => break FoundSocket::No,
353 Some(SocketLookupResult::Connection(demux_conn_id, conn_addr)) => {
354 assert_eq!(tw_reuse, None);
357 let disposition = match WireI::as_dual_stack_ip_socket(&demux_conn_id) {
358 EitherStack::ThisStack(conn_id) => {
359 try_handle_incoming_for_connection_dual_stack(
360 core_ctx,
361 bindings_ctx,
362 conn_id,
363 incoming_device,
364 header_info,
365 &incoming,
366 marks,
367 packet_type,
368 gso_info,
369 )
370 }
371 EitherStack::OtherStack(conn_id) => {
372 try_handle_incoming_for_connection_dual_stack(
373 core_ctx,
374 bindings_ctx,
375 conn_id,
376 incoming_device,
377 header_info,
378 &incoming,
379 marks,
380 packet_type,
381 gso_info,
382 )
383 }
384 };
385 match disposition {
386 ConnectionIncomingSegmentDisposition::Destroy => {
387 WireI::destroy_socket_with_demux_id(core_ctx, bindings_ctx, demux_conn_id);
388 break FoundSocket::Yes(None);
389 }
390 ConnectionIncomingSegmentDisposition::FoundSocket
391 | ConnectionIncomingSegmentDisposition::Filtered => {
392 break FoundSocket::Yes(Some(demux_conn_id));
393 }
394 ConnectionIncomingSegmentDisposition::ReuseCandidateForListener => {
395 tw_reuse = Some((demux_conn_id, conn_addr));
396 }
397 }
398 }
399 Some(SocketLookupResult::Listener((demux_listener_id, _listener_addr))) => {
400 match WireI::as_dual_stack_ip_socket(&demux_listener_id) {
401 EitherStack::ThisStack(listener_id) => {
402 let disposition = core_ctx.with_socket_mut_generators_transport_demux(
403 &listener_id,
404 |core_ctx, socket_state, isn, timestamp_offset| match core_ctx {
405 MaybeDualStack::NotDualStack((core_ctx, converter)) => {
406 try_handle_incoming_for_listener::<WireI, WireI, CC, BC, _, _>(
407 core_ctx,
408 bindings_ctx,
409 &listener_id,
410 isn,
411 timestamp_offset,
412 socket_state,
413 header_info,
414 incoming,
415 conn_addr,
416 incoming_device,
417 &mut tw_reuse,
418 move |conn, addr| converter.convert_back((conn, addr)),
419 WireI::into_demux_socket_id,
420 marks,
421 packet_type,
422 gso_info,
423 )
424 }
425 MaybeDualStack::DualStack((core_ctx, converter)) => {
426 try_handle_incoming_for_listener::<_, _, CC, BC, _, _>(
427 core_ctx,
428 bindings_ctx,
429 &listener_id,
430 isn,
431 timestamp_offset,
432 socket_state,
433 header_info,
434 incoming,
435 conn_addr,
436 incoming_device,
437 &mut tw_reuse,
438 move |conn, addr| {
439 converter
440 .convert_back(EitherStack::ThisStack((conn, addr)))
441 },
442 WireI::into_demux_socket_id,
443 marks,
444 packet_type,
445 gso_info,
446 )
447 }
448 },
449 );
450 if try_handle_listener_incoming_disposition(
451 core_ctx,
452 bindings_ctx,
453 disposition,
454 &demux_listener_id,
455 &mut tw_reuse,
456 &mut addrs_to_search,
457 conn_addr,
458 incoming_device,
459 ) {
460 break FoundSocket::Yes(Some(demux_listener_id));
461 }
462 }
463 EitherStack::OtherStack(listener_id) => {
464 let disposition = core_ctx.with_socket_mut_generators_transport_demux(
465 &listener_id,
466 |core_ctx, socket_state, isn, timestamp_offset| {
467 match core_ctx {
468 MaybeDualStack::NotDualStack((_core_ctx, _converter)) => {
469 unreachable!("OtherStack socket ID with non dual stack");
472 }
473 MaybeDualStack::DualStack((core_ctx, converter)) => {
474 let other_demux_id_converter =
475 core_ctx.other_demux_id_converter();
476 try_handle_incoming_for_listener::<_, _, CC, BC, _, _>(
477 core_ctx,
478 bindings_ctx,
479 &listener_id,
480 isn,
481 timestamp_offset,
482 socket_state,
483 header_info,
484 incoming,
485 conn_addr,
486 incoming_device,
487 &mut tw_reuse,
488 move |conn, addr| {
489 converter.convert_back(EitherStack::OtherStack((
490 conn, addr,
491 )))
492 },
493 move |id| other_demux_id_converter.convert(id),
494 marks,
495 packet_type,
496 gso_info,
497 )
498 }
499 }
500 },
501 );
502 if try_handle_listener_incoming_disposition::<_, _, CC, BC, _>(
503 core_ctx,
504 bindings_ctx,
505 disposition,
506 &demux_listener_id,
507 &mut tw_reuse,
508 &mut addrs_to_search,
509 conn_addr,
510 incoming_device,
511 ) {
512 break FoundSocket::Yes(Some(demux_listener_id));
513 }
514 }
515 };
516 }
517 }
518 };
519
520 let demux_id = match found_socket {
521 FoundSocket::No => {
522 CounterContext::<TcpCountersWithoutSocket<WireI>>::counters(core_ctx)
523 .received_segments_no_dispatch
524 .increment();
525
526 if let Some(seg) =
532 (Closed { reason: None::<Option<ConnectionError>> }.on_segment(&incoming.into()))
533 {
534 socket::send_tcp_segment::<WireI, WireI, _, _, _>(
535 core_ctx,
536 bindings_ctx,
537 None,
538 None,
539 conn_addr,
540 seg.into_empty(),
541 &TcpIpSockOptions { marks: *marks },
542 );
543 }
544 None
545 }
546 FoundSocket::Yes(demux_id) => {
547 counters::increment_counter_with_optional_demux_id::<WireI, _, _, _, _>(
548 core_ctx,
549 demux_id.as_ref(),
550 |c| &c.received_segments_dispatched,
551 );
552 demux_id
553 }
554 };
555
556 if let Some(control) = incoming.control() {
557 counters::increment_counter_with_optional_demux_id::<WireI, _, _, _, _>(
558 core_ctx,
559 demux_id.as_ref(),
560 |c| match control {
561 Control::RST => &c.resets_received,
562 Control::SYN => &c.syns_received,
563 Control::FIN => &c.fins_received,
564 },
565 )
566 }
567}
568
569enum SocketLookupResult<I: DualStackIpExt, D: WeakDeviceIdentifier, BT: TcpBindingsTypes> {
570 Connection(I::DemuxSocketId<D, BT>, ConnAddr<ConnIpAddr<I::Addr, NonZeroU16, NonZeroU16>, D>),
571 Listener((I::DemuxSocketId<D, BT>, ListenerAddr<ListenerIpAddr<I::Addr, NonZeroU16>, D>)),
572}
573
574fn lookup_socket<I, CC, BC>(
575 DemuxState { socketmap, .. }: &DemuxState<I, CC::WeakDeviceId, BC>,
576 addrs_to_search: &mut AddrVecIter<I, CC::WeakDeviceId, TcpPortSpec>,
577) -> Option<SocketLookupResult<I, CC::WeakDeviceId, BC>>
578where
579 I: DualStackIpExt,
580 BC: TcpBindingsContext<CC::DeviceId>,
581 CC: TcpContext<I, BC>,
582{
583 addrs_to_search.find_map(|addr| {
584 match addr {
585 AddrVec::Conn(conn_addr) => {
588 socketmap.conns().get_by_addr(&conn_addr).map(|conn_addr_state| {
589 SocketLookupResult::Connection(conn_addr_state.id(), conn_addr)
590 })
591 }
592 AddrVec::Listen(listener_addr) => {
593 socketmap
598 .listeners()
599 .get_by_addr(&listener_addr)
600 .and_then(|addr_state| match addr_state {
601 ListenerAddrState::ExclusiveListener(id) => Some(id.clone()),
602 ListenerAddrState::Shared { listener: Some(id), bound: _ } => {
603 Some(id.clone())
604 }
605 ListenerAddrState::ExclusiveBound(_)
606 | ListenerAddrState::Shared { listener: None, bound: _ } => None,
607 })
608 .map(|id| SocketLookupResult::Listener((id, listener_addr)))
609 }
610 }
611 })
612}
613
614#[derive(PartialEq, Eq)]
615enum ConnectionIncomingSegmentDisposition {
616 FoundSocket,
617 Filtered,
618 ReuseCandidateForListener,
619 Destroy,
620}
621
622enum ListenerIncomingSegmentDisposition<S> {
623 FoundSocket,
624 Filtered,
625 ConflictingConnection,
626 NoMatchingSocket,
627 NewConnection(S),
628}
629
630fn try_handle_incoming_for_connection_dual_stack<SockI, WireI, CC, BC, H>(
631 core_ctx: &mut CC,
632 bindings_ctx: &mut BC,
633 conn_id: &TcpSocketId<SockI, CC::WeakDeviceId, BC>,
634 incoming_device: &CC::DeviceId,
635 header_info: &H,
636 incoming: &VerifiedTcpSegment<'_>,
637 packet_marks: &Marks,
638 packet_type: PacketType,
639 gso_info: Option<GsoInfo>,
640) -> ConnectionIncomingSegmentDisposition
641where
642 SockI: DualStackIpExt,
643 WireI: Ip,
644 BC: TcpBindingsContext<CC::DeviceId>
645 + BufferProvider<
646 BC::ReceiveBuffer,
647 BC::SendBuffer,
648 ActiveOpen = <BC as TcpBindingsTypes>::ListenerNotifierOrProvidedBuffers,
649 PassiveOpen = <BC as TcpBindingsTypes>::ReturnedBuffers,
650 >,
651 CC: TcpContext<SockI, BC>,
652 H: IpHeaderInfo<WireI>,
653{
654 core_ctx.with_socket_mut_transport_demux(conn_id, |core_ctx, socket_state| {
655 let TcpSocketState { socket_state, sharing: _, ip_options: _, socket_options } =
656 socket_state;
657
658 match run_socket_ingress_filter(
659 bindings_ctx,
660 incoming_device,
661 conn_id.socket_info(),
662 socket_options,
663 packet_marks,
664 header_info,
665 incoming.tcp_segment(),
666 packet_type,
667 gso_info,
668 ) {
669 SocketIngressFilterResult::Accept => (),
670 SocketIngressFilterResult::Drop => {
671 return ConnectionIncomingSegmentDisposition::Filtered;
672 }
673 }
674
675 let (conn_and_addr, timer) = assert_matches!(
676 socket_state,
677 TcpSocketStateInner::Connected { conn, timer } => (conn, timer),
678 "invalid socket ID"
679 );
680 let this_or_other_stack = match core_ctx {
681 MaybeDualStack::DualStack((core_ctx, converter)) => {
682 match converter.convert(conn_and_addr) {
683 EitherStack::ThisStack((conn, conn_addr)) => {
684 EitherStack::ThisStack((
689 core_ctx.as_this_stack(),
690 conn,
691 conn_addr,
692 SockI::into_demux_socket_id(conn_id.clone()),
693 ))
694 }
695 EitherStack::OtherStack((conn, conn_addr)) => {
696 let demux_sock_id = core_ctx.into_other_demux_socket_id(conn_id.clone());
699 EitherStack::OtherStack((core_ctx, conn, conn_addr, demux_sock_id))
700 }
701 }
702 }
703 MaybeDualStack::NotDualStack((core_ctx, converter)) => {
704 let (conn, conn_addr) = converter.convert(conn_and_addr);
705 EitherStack::ThisStack((
708 core_ctx.as_this_stack(),
709 conn,
710 conn_addr,
711 SockI::into_demux_socket_id(conn_id.clone()),
712 ))
713 }
714 };
715
716 match this_or_other_stack {
717 EitherStack::ThisStack((core_ctx, conn, conn_addr, demux_conn_id)) => {
718 try_handle_incoming_for_connection::<_, _, CC, _, _>(
719 core_ctx,
720 bindings_ctx,
721 conn_addr.clone(),
722 conn_id,
723 demux_conn_id,
724 socket_options,
725 conn,
726 timer,
727 incoming.into(),
728 )
729 }
730 EitherStack::OtherStack((core_ctx, conn, conn_addr, demux_conn_id)) => {
731 try_handle_incoming_for_connection::<_, _, CC, _, _>(
732 core_ctx,
733 bindings_ctx,
734 conn_addr.clone(),
735 conn_id,
736 demux_conn_id,
737 socket_options,
738 conn,
739 timer,
740 incoming.into(),
741 )
742 }
743 }
744 })
745}
746
747fn try_handle_incoming_for_connection<SockI, WireI, CC, BC, DC>(
754 core_ctx: &mut DC,
755 bindings_ctx: &mut BC,
756 conn_addr: ConnAddr<ConnIpAddr<WireI::Addr, NonZeroU16, NonZeroU16>, CC::WeakDeviceId>,
757 conn_id: &TcpSocketId<SockI, CC::WeakDeviceId, BC>,
758 demux_id: WireI::DemuxSocketId<CC::WeakDeviceId, BC>,
759 socket_options: &SocketOptions,
760 conn: &mut Connection<SockI, WireI, CC::WeakDeviceId, BC>,
761 timer: &mut BC::Timer,
762 incoming: Segment<&[u8]>,
763) -> ConnectionIncomingSegmentDisposition
764where
765 SockI: DualStackIpExt,
766 WireI: DualStackIpExt,
767 BC: TcpBindingsContext<CC::DeviceId>
768 + BufferProvider<
769 BC::ReceiveBuffer,
770 BC::SendBuffer,
771 ActiveOpen = <BC as TcpBindingsTypes>::ListenerNotifierOrProvidedBuffers,
772 PassiveOpen = <BC as TcpBindingsTypes>::ReturnedBuffers,
773 >,
774 CC: TcpContext<SockI, BC>,
775 DC: TransportIpContext<WireI, BC, DeviceId = CC::DeviceId, WeakDeviceId = CC::WeakDeviceId>
776 + DeviceIpSocketHandler<SockI, BC>
777 + TcpDemuxContext<WireI, CC::WeakDeviceId, BC>
778 + TcpCounterContext<SockI, CC::WeakDeviceId, BC>
779 + CoreTxMetadataContext<TcpSocketTxMetadata<SockI, CC::WeakDeviceId, BC>, BC>,
780{
781 let Connection { accept_queue, state, ip_sock, defunct, soft_error: _, handshake_status } =
782 conn;
783
784 if *defunct
796 && incoming.header().control == Some(Control::SYN)
797 && incoming.header().ack.is_none()
798 {
799 if let State::TimeWait(TimeWait { last_seq: _, closed_rcv, expiry: _, snd_info: _ }) = state
800 {
801 if !incoming.header().seq.before(closed_rcv.ack) {
802 return ConnectionIncomingSegmentDisposition::ReuseCandidateForListener;
803 }
804 }
805 }
806 let (reply, passive_open, data_acked, newly_closed) = state.on_segment::<_, BC>(
807 &conn_id.either(),
808 &TcpCountersRefs::from_ctx(core_ctx, conn_id),
809 incoming,
810 bindings_ctx.now(),
811 socket_options,
812 *defunct,
813 );
814
815 match data_acked {
816 DataAcked::Yes => {
817 core_ctx.confirm_reachable(bindings_ctx, ip_sock, &socket_options.ip_options)
818 }
819 DataAcked::No => {}
820 }
821
822 match state {
823 State::Listen(_) => {
824 unreachable!("has an invalid status: {:?}", conn.state)
825 }
826 State::SynSent(_) | State::SynRcvd(_) => {
827 assert_eq!(*handshake_status, HandshakeStatus::Pending)
828 }
829 State::Established(_)
830 | State::FinWait1(_)
831 | State::FinWait2(_)
832 | State::Closing(_)
833 | State::CloseWait(_)
834 | State::LastAck(_)
835 | State::TimeWait(_) => {
836 if handshake_status
837 .update_if_pending(HandshakeStatus::Completed { reported: accept_queue.is_some() })
838 {
839 core_ctx.confirm_reachable(bindings_ctx, ip_sock, &socket_options.ip_options);
840 }
841 }
842 State::Closed(Closed { reason }) => {
843 socket::handle_newly_closed(
851 core_ctx,
852 bindings_ctx,
853 newly_closed,
854 &demux_id,
855 &conn_addr,
856 timer,
857 );
858 if let Some(accept_queue) = accept_queue {
859 accept_queue.remove(&conn_id);
860 *defunct = true;
861 }
862 if *defunct {
863 return ConnectionIncomingSegmentDisposition::Destroy;
866 }
867 let _: bool = handshake_status.update_if_pending(match reason {
868 None => HandshakeStatus::Completed { reported: accept_queue.is_some() },
869 Some(_err) => HandshakeStatus::Aborted,
870 });
871 }
872 }
873
874 if let Some(seg) = reply {
875 socket::send_tcp_segment(
876 core_ctx,
877 bindings_ctx,
878 Some(conn_id),
879 Some(&ip_sock),
880 conn_addr.ip,
881 seg.into_empty(),
882 &socket_options.ip_options,
883 );
884 }
885
886 socket::do_send_inner_and_then_handle_newly_closed(
888 conn_id,
889 &demux_id,
890 socket_options,
891 conn,
892 DoSendLimit::MultipleSegments,
893 &conn_addr,
894 timer,
895 core_ctx,
896 bindings_ctx,
897 );
898
899 if let Some(passive_open) = passive_open {
902 let accept_queue = conn.accept_queue.as_ref().expect("no accept queue but passive open");
903 accept_queue.notify_ready(conn_id, passive_open);
904 }
905
906 ConnectionIncomingSegmentDisposition::FoundSocket
908}
909
910fn try_handle_listener_incoming_disposition<SockI, WireI, CC, BC, Addr>(
915 core_ctx: &mut CC,
916 bindings_ctx: &mut BC,
917 disposition: ListenerIncomingSegmentDisposition<PrimaryRc<SockI, CC::WeakDeviceId, BC>>,
918 demux_listener_id: &WireI::DemuxSocketId<CC::WeakDeviceId, BC>,
919 tw_reuse: &mut Option<(WireI::DemuxSocketId<CC::WeakDeviceId, BC>, Addr)>,
920 addrs_to_search: &mut AddrVecIter<WireI, CC::WeakDeviceId, TcpPortSpec>,
921 conn_addr: ConnIpAddr<WireI::Addr, NonZeroU16, NonZeroU16>,
922 incoming_device: &CC::DeviceId,
923) -> bool
924where
925 SockI: DualStackIpExt,
926 WireI: DualStackIpExt,
927 CC: TcpContext<SockI, BC> + TcpContext<WireI, BC> + TcpContext<WireI::OtherVersion, BC>,
928 BC: TcpBindingsContext<CC::DeviceId>,
929{
930 match disposition {
931 ListenerIncomingSegmentDisposition::FoundSocket => true,
932 ListenerIncomingSegmentDisposition::Filtered => true,
933 ListenerIncomingSegmentDisposition::ConflictingConnection => {
934 if let Some((tw_reuse, _)) = tw_reuse.take() {
939 WireI::destroy_socket_with_demux_id(core_ctx, bindings_ctx, tw_reuse);
940 }
941
942 *addrs_to_search = AddrVecIter::<WireI, CC::WeakDeviceId, TcpPortSpec>::with_device(
945 conn_addr.into(),
946 incoming_device.downgrade(),
947 );
948 false
949 }
950 ListenerIncomingSegmentDisposition::NoMatchingSocket => false,
951 ListenerIncomingSegmentDisposition::NewConnection(primary) => {
952 if let Some((tw_reuse, _)) = tw_reuse.take() {
958 WireI::destroy_socket_with_demux_id(core_ctx, bindings_ctx, tw_reuse);
959 }
960
961 let id = TcpSocketId(PrimaryRc::clone_strong(&primary));
970 let to_destroy = core_ctx.with_all_sockets_mut(move |all_sockets| {
971 let insert_entry = TcpSocketSetEntry::Primary(primary);
972 match all_sockets.entry(id) {
973 hash_map::Entry::Vacant(v) => {
974 let _: &mut _ = v.insert(insert_entry);
975 None
976 }
977 hash_map::Entry::Occupied(mut o) => {
978 assert_matches!(
982 core::mem::replace(o.get_mut(), insert_entry),
983 TcpSocketSetEntry::DeadOnArrival
984 );
985 Some(o.key().clone())
986 }
987 }
988 });
989 if let Some(to_destroy) = to_destroy {
997 socket::destroy_socket(core_ctx, bindings_ctx, to_destroy);
998 }
999 counters::increment_counter_for_demux_id::<WireI, _, _, _, _>(
1000 core_ctx,
1001 demux_listener_id,
1002 |c| &c.passive_connection_openings,
1003 );
1004 true
1005 }
1006 }
1007}
1008
1009fn try_handle_incoming_for_listener<SockI, WireI, CC, BC, DC, H>(
1013 core_ctx: &mut DC,
1014 bindings_ctx: &mut BC,
1015 listener_id: &TcpSocketId<SockI, CC::WeakDeviceId, BC>,
1016 isn: &IsnGenerator<BC::Instant>,
1017 timestamp_offset: &TimestampOffsetGenerator<BC::Instant>,
1018 socket_state: &mut TcpSocketState<SockI, CC::WeakDeviceId, BC>,
1019 header_info: &H,
1020 incoming: &VerifiedTcpSegment<'_>,
1021 incoming_addrs: ConnIpAddr<WireI::Addr, NonZeroU16, NonZeroU16>,
1022 incoming_device: &CC::DeviceId,
1023 tw_reuse: &mut Option<(
1024 WireI::DemuxSocketId<CC::WeakDeviceId, BC>,
1025 ConnAddr<ConnIpAddr<WireI::Addr, NonZeroU16, NonZeroU16>, CC::WeakDeviceId>,
1026 )>,
1027 make_connection: impl FnOnce(
1028 Connection<SockI, WireI, CC::WeakDeviceId, BC>,
1029 ConnAddr<ConnIpAddr<WireI::Addr, NonZeroU16, NonZeroU16>, CC::WeakDeviceId>,
1030 ) -> SockI::ConnectionAndAddr<CC::WeakDeviceId, BC>,
1031 make_demux_id: impl Fn(
1032 TcpSocketId<SockI, CC::WeakDeviceId, BC>,
1033 ) -> WireI::DemuxSocketId<CC::WeakDeviceId, BC>,
1034 marks: &Marks,
1035 packet_type: PacketType,
1036 gso_info: Option<GsoInfo>,
1037) -> ListenerIncomingSegmentDisposition<PrimaryRc<SockI, CC::WeakDeviceId, BC>>
1038where
1039 SockI: DualStackIpExt,
1040 WireI: DualStackIpExt,
1041 BC: TcpBindingsContext<CC::DeviceId>
1042 + BufferProvider<
1043 BC::ReceiveBuffer,
1044 BC::SendBuffer,
1045 ActiveOpen = <BC as TcpBindingsTypes>::ListenerNotifierOrProvidedBuffers,
1046 PassiveOpen = <BC as TcpBindingsTypes>::ReturnedBuffers,
1047 >,
1048 CC: TcpContext<SockI, BC>,
1049 DC: TransportIpContext<WireI, BC, DeviceId = CC::DeviceId, WeakDeviceId = CC::WeakDeviceId>
1050 + DeviceIpSocketHandler<WireI, BC>
1051 + TcpDemuxContext<WireI, CC::WeakDeviceId, BC>
1052 + TcpCounterContext<SockI, CC::WeakDeviceId, BC>
1053 + CoreTxMetadataContext<TcpSocketTxMetadata<SockI, CC::WeakDeviceId, BC>, BC>,
1054 H: IpHeaderInfo<WireI>,
1055{
1056 let Listener { addr: listener_addr, accept_queue, backlog, buffer_sizes } =
1057 match &socket_state.socket_state {
1058 TcpSocketStateInner::Bound(_) => {
1059 return ListenerIncomingSegmentDisposition::NoMatchingSocket;
1061 }
1062 TcpSocketStateInner::Listener(listener) => listener,
1063 _ => panic!("unexpected socket state: {:?}", socket_state.socket_state),
1064 };
1065
1066 let ConnIpAddr { local: (local_ip, local_port), remote: (remote_ip, remote_port) } =
1067 incoming_addrs;
1068
1069 let marks = BC::update_ingress_marks(*marks, &socket_state.socket_options.ip_options.marks);
1070
1071 match run_socket_ingress_filter(
1072 bindings_ctx,
1073 incoming_device,
1074 listener_id.socket_info(),
1075 &socket_state.socket_options,
1076 &marks,
1077 header_info,
1078 incoming.tcp_segment(),
1079 packet_type,
1080 gso_info,
1081 ) {
1082 SocketIngressFilterResult::Accept => (),
1083 SocketIngressFilterResult::Drop => {
1084 return ListenerIncomingSegmentDisposition::Filtered;
1085 }
1086 }
1087
1088 if accept_queue.len() == backlog.get() {
1092 core_ctx.increment_both(listener_id, |counters| &counters.listener_queue_overflow);
1093 core_ctx.increment_both(listener_id, |counters| &counters.failed_connection_attempts);
1094 debug!("incoming SYN dropped because of the full backlog of the listener");
1095 return ListenerIncomingSegmentDisposition::FoundSocket;
1096 }
1097
1098 let bound_device = listener_addr.as_ref().clone();
1101 let bound_device = if remote_ip.as_ref().must_have_zone() {
1102 Some(bound_device.map_or(EitherDeviceId::Strong(incoming_device), EitherDeviceId::Weak))
1103 } else {
1104 bound_device.map(EitherDeviceId::Weak)
1105 };
1106
1107 let ip_options = TcpIpSockOptions { marks, ..socket_state.socket_options.ip_options };
1108 let socket_options = SocketOptions { ip_options, ..socket_state.socket_options };
1109
1110 let bound_device = bound_device.as_ref().map(|d| d.as_ref());
1111 let ip_sock = match core_ctx.new_ip_socket(
1112 bindings_ctx,
1113 IpSocketArgs {
1114 device: bound_device,
1115 local_ip: IpDeviceAddr::new_from_socket_ip_addr(local_ip),
1116 remote_ip,
1117 proto: IpProto::Tcp.into(),
1118 options: &ip_options,
1119 },
1120 ) {
1121 Ok(ip_sock) => ip_sock,
1122 err @ Err(IpSockCreationError::Route(_)) => {
1123 core_ctx.increment_both(listener_id, |counters| &counters.passive_open_no_route_errors);
1124 core_ctx.increment_both(listener_id, |counters| &counters.failed_connection_attempts);
1125 debug!("cannot construct an ip socket to the SYN originator: {:?}, ignoring", err);
1126 return ListenerIncomingSegmentDisposition::NoMatchingSocket;
1127 }
1128 };
1129
1130 let isn = isn.generate(
1131 bindings_ctx.now(),
1132 (ip_sock.local_ip().clone().into(), local_port),
1133 (ip_sock.remote_ip().clone(), remote_port),
1134 );
1135 let timestamp_offset = timestamp_offset.generate::<SocketIpAddr<WireI::Addr>, NonZeroU16>(
1136 bindings_ctx.now(),
1137 (ip_sock.local_ip().clone().into(), local_port),
1138 (ip_sock.remote_ip().clone(), remote_port),
1139 );
1140 let device_mms = match core_ctx.get_mms(bindings_ctx, &ip_sock, &socket_options.ip_options) {
1141 Ok(mms) => mms,
1142 Err(err) => {
1143 error!("Cannot find a device with large enough MTU for the connection");
1147 core_ctx.increment_both(listener_id, |counters| &counters.failed_connection_attempts);
1148 match err {
1149 MmsError::NoDevice(_) | MmsError::MTUTooSmall(_) => {
1150 return ListenerIncomingSegmentDisposition::FoundSocket;
1151 }
1152 }
1153 }
1154 };
1155 let Some(device_mss) = Mss::from_mms(device_mms) else {
1156 return ListenerIncomingSegmentDisposition::FoundSocket;
1157 };
1158
1159 let mut state = State::Listen(Closed::<Initial>::listen(
1160 isn,
1161 timestamp_offset,
1162 buffer_sizes.clone(),
1163 device_mss,
1164 Mss::default::<WireI>(),
1165 socket_options.user_timeout,
1166 ));
1167
1168 let result = state.on_segment::<_, BC>(
1173 &listener_id.either(),
1177 &TcpCountersRefs::from_ctx(core_ctx, listener_id),
1178 incoming.into(),
1179 bindings_ctx.now(),
1180 &SocketOptions::default(),
1181 false, );
1183 let reply = assert_matches!(
1184 result,
1185 (reply, None, _, NewlyClosed::No ) => reply
1186 );
1187
1188 let result = if matches!(state, State::SynRcvd(_)) {
1189 let poll_send_at = state.poll_send_at().expect("no retrans timer");
1190 let bound_device = ip_sock.device().cloned();
1191
1192 let addr = ConnAddr {
1193 ip: ConnIpAddr { local: (local_ip, local_port), remote: (remote_ip, remote_port) },
1194 device: bound_device,
1195 };
1196
1197 let new_socket = core_ctx.with_demux_mut(|DemuxState { socketmap, .. }| {
1198 if let Some((tw_reuse, conn_addr)) = tw_reuse {
1208 match socketmap.conns_mut().remove(tw_reuse, &conn_addr) {
1209 Ok(()) => {
1210 }
1216 Err(NotFoundError) => {
1217 }
1221 }
1222 }
1223
1224 let accept_queue_clone = accept_queue.clone();
1226 let ip_sock = ip_sock.clone();
1227 let bindings_ctx_moved = &mut *bindings_ctx;
1228 let sharing = socket_state.sharing;
1229 match socketmap.conns_mut().try_insert_with(addr, sharing, move |addr, sharing| {
1230 let conn = make_connection(
1231 Connection {
1232 accept_queue: Some(accept_queue_clone),
1233 state,
1234 ip_sock,
1235 defunct: false,
1236 soft_error: None,
1237 handshake_status: HandshakeStatus::Pending,
1238 },
1239 addr,
1240 );
1241
1242 let (id, primary) = TcpSocketId::new_cyclic(
1243 |weak| {
1244 let mut timer = CC::new_timer(bindings_ctx_moved, weak);
1245 assert_eq!(
1249 bindings_ctx_moved.schedule_timer_instant(poll_send_at, &mut timer),
1250 None
1251 );
1252 TcpSocketStateInner::Connected { conn, timer }
1253 },
1254 sharing,
1255 socket_options,
1256 );
1257 (make_demux_id(id.clone()), (primary, id))
1258 }) {
1259 Ok((_entry, (primary, id))) => {
1260 accept_queue.push_pending(id);
1263 Some(primary)
1264 }
1265 Err(e) => {
1266 assert_matches!(e, InsertError::Exists);
1271 None
1276 }
1277 }
1278 });
1279
1280 match new_socket {
1281 Some(new_socket) => ListenerIncomingSegmentDisposition::NewConnection(new_socket),
1282 None => {
1283 core_ctx
1286 .increment_both(listener_id, |counters| &counters.failed_connection_attempts);
1287 return ListenerIncomingSegmentDisposition::ConflictingConnection;
1288 }
1289 }
1290 } else {
1291 ListenerIncomingSegmentDisposition::FoundSocket
1294 };
1295
1296 if let Some(seg) = reply {
1298 socket::send_tcp_segment(
1299 core_ctx,
1300 bindings_ctx,
1301 Some(&listener_id),
1302 Some(&ip_sock),
1303 incoming_addrs,
1304 seg.into_empty(),
1305 &socket_options.ip_options,
1306 );
1307 }
1308
1309 result
1310}
1311
1312pub(super) fn tcp_serialize_segment<'a, I, P>(
1313 header: &'a SegmentHeader,
1314 data: P,
1315 conn_addr: ConnIpAddr<I::Addr, NonZeroU16, NonZeroU16>,
1316) -> impl TransportPacketSerializer<I, Buffer = EmptyBuf>
1317+ MaybeSegmentableTransportSerializer
1318+ Debug
1319+ 'a
1320where
1321 I: FilterIpExt,
1322 P: InnerPacketBuilder + Copy + Debug + Payload + 'a,
1323{
1324 let SegmentHeader { seq, ack, wnd, control, options, push } = header;
1325 let ConnIpAddr { local: (local_ip, local_port), remote: (remote_ip, remote_port) } = conn_addr;
1326 let mut builder = TcpSegmentBuilder::new(
1327 local_ip.addr(),
1328 remote_ip.addr(),
1329 local_port,
1330 remote_port,
1331 (*seq).into(),
1332 ack.map(Into::into),
1333 u16::from(*wnd),
1334 );
1335 builder.psh(*push);
1336 match control {
1337 None => {}
1338 Some(Control::SYN) => builder.syn(true),
1339 Some(Control::FIN) => builder.fin(true),
1340 Some(Control::RST) => builder.rst(true),
1341 }
1342 TcpSegmentBuilderWithOptions::new(builder, options.builder())
1343 .unwrap_or_else(|TcpOptionsTooLongError| {
1344 panic!("Too many TCP options");
1345 })
1346 .wrap_body(data.into_serializer())
1347}
1348
1349fn run_socket_ingress_filter<I, BC, D>(
1350 bindings_ctx: &BC,
1351 incoming_device: &D,
1352 socket_info: netstack3_base::socket::SocketInfo,
1353 socket_options: &SocketOptions,
1354 packet_marks: &Marks,
1355 header_info: &impl IpHeaderInfo<I>,
1356 tcp_segment: &TcpSegment<&'_ [u8]>,
1357 packet_type: PacketType,
1358 gso_info: Option<GsoInfo>,
1359) -> SocketIngressFilterResult
1360where
1361 I: Ip,
1362 BC: TcpBindingsContext<D>,
1363 D: StrongDeviceIdentifier,
1364{
1365 let [ip_prefix, ip_options] = header_info.as_bytes();
1366 let [tcp_prefix, tcp_options, _data] = tcp_segment.as_bytes();
1367 let mut slices = [ip_prefix, ip_options, tcp_prefix, tcp_options, _data];
1368 let packet = FragmentedByteSlice::new(&mut slices);
1369 let header_len = ip_prefix.len() + ip_options.len() + tcp_prefix.len() + tcp_options.len();
1370
1371 let marks = BC::update_ingress_marks(*packet_marks, &socket_options.ip_options.marks);
1372 bindings_ctx.socket_ops_filter().on_ingress(
1373 I::VERSION,
1374 packet,
1375 header_len,
1376 incoming_device,
1377 socket_info,
1378 &marks,
1379 packet_type,
1380 gso_info,
1381 )
1382}
1383
1384#[cfg(test)]
1385mod test {
1386 use ip_test_macro::ip_test;
1387 use netstack3_base::{
1388 HandshakeOptions, NetworkSerializationContext, Options, ResetOptions, SackBlocks,
1389 SegmentOptions, UnscaledWindowSize,
1390 };
1391 use packet::Serializer as _;
1392 use packet_formats::tcp::options::TcpOptions as _;
1393 use test_case::test_case;
1394
1395 use super::*;
1396
1397 trait TestIpExt: netstack3_base::testutil::TestIpExt + FilterIpExt {}
1398 impl<T> TestIpExt for T where T: netstack3_base::testutil::TestIpExt + FilterIpExt {}
1399
1400 const SEQ: SeqNum = SeqNum::new(12345);
1401 const ACK: SeqNum = SeqNum::new(67890);
1402 const FAKE_DATA: &'static [u8] = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
1403
1404 #[ip_test(I)]
1405 #[test_case(
1406 Segment::syn(SEQ, UnscaledWindowSize::from(u16::MAX),
1407 HandshakeOptions::default()), &[]
1408 ; "syn")]
1409 #[test_case(
1410 Segment::syn(SEQ, UnscaledWindowSize::from(u16::MAX),
1411 HandshakeOptions {
1412 mss: Some(Mss::new(1440).unwrap()),
1413 ..Default::default() }), &[]
1414 ; "syn with mss")]
1415 #[test_case(
1416 Segment::ack(SEQ, ACK, UnscaledWindowSize::from(u16::MAX), SegmentOptions::default()),
1417 &[]; "ack")]
1418 #[test_case(Segment::with_fake_data(SEQ, ACK, FAKE_DATA), FAKE_DATA; "data")]
1419 #[test_case(Segment::new_assert_no_discard(SegmentHeader {
1420 seq: SEQ,
1421 ack: Some(ACK),
1422 push: true,
1423 wnd: UnscaledWindowSize::from(u16::MAX),
1424 ..Default::default()
1425 },
1426 FAKE_DATA
1427 ), FAKE_DATA; "push")]
1428 fn tcp_serialize_segment<I: TestIpExt>(segment: Segment<&[u8]>, expected_body: &[u8]) {
1429 const SOURCE_PORT: NonZeroU16 = NonZeroU16::new(1111).unwrap();
1430 const DEST_PORT: NonZeroU16 = NonZeroU16::new(2222).unwrap();
1431
1432 let (header, data) = segment.into_parts();
1433 let serializer = super::tcp_serialize_segment::<I, _>(
1434 &header,
1435 data,
1436 ConnIpAddr {
1437 local: (SocketIpAddr::try_from(I::TEST_ADDRS.local_ip).unwrap(), SOURCE_PORT),
1438 remote: (SocketIpAddr::try_from(I::TEST_ADDRS.remote_ip).unwrap(), DEST_PORT),
1439 },
1440 );
1441
1442 let mut serialized = serializer
1443 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1444 .unwrap()
1445 .unwrap_b();
1446 let parsed_segment = serialized
1447 .parse_with::<_, TcpSegment<_>>(TcpParseArgs::new(
1448 *I::TEST_ADDRS.remote_ip,
1449 *I::TEST_ADDRS.local_ip,
1450 ))
1451 .expect("is valid segment");
1452
1453 assert_eq!(parsed_segment.src_port(), SOURCE_PORT);
1454 assert_eq!(parsed_segment.dst_port(), DEST_PORT);
1455 assert_eq!(parsed_segment.seq_num(), u32::from(SEQ));
1456 assert_eq!(parsed_segment.psh(), header.push);
1457 assert_eq!(
1458 UnscaledWindowSize::from(parsed_segment.window_size()),
1459 UnscaledWindowSize::from(u16::MAX)
1460 );
1461
1462 let (mss, window_scale, sack_permitted, sack_blocks, timestamp) = match header.options {
1463 Options::Handshake(HandshakeOptions {
1464 mss,
1465 window_scale,
1466 sack_permitted,
1467 timestamp,
1468 }) => (mss, window_scale, sack_permitted, SackBlocks::EMPTY, timestamp),
1469 Options::Segment(SegmentOptions { timestamp, sack_blocks }) => {
1470 (None, None, false, sack_blocks, timestamp)
1471 }
1472 Options::Reset(ResetOptions { timestamp }) => {
1473 (None, None, false, SackBlocks::EMPTY, timestamp)
1474 }
1475 };
1476 assert_eq!(mss.map(|mss| mss.get()), parsed_segment.options().mss());
1477 assert_eq!(window_scale.map(|ws| ws.get()), parsed_segment.options().window_scale());
1478 assert_eq!(sack_permitted, parsed_segment.options().sack_permitted());
1479 assert_eq!(sack_blocks.as_slice(), parsed_segment.options().sack_blocks());
1480 assert_eq!(
1481 timestamp.as_ref().map(Into::into).as_ref(),
1482 parsed_segment.options().timestamp()
1483 );
1484
1485 assert_eq!(parsed_segment.into_body(), expected_body);
1486 }
1487}