2
2
// SPDX-License-Identifier: MIT
3
3
4
4
#include " firewall.hh"
5
+ #include " ../tcpip/checksum-internal.h"
5
6
#include < atomic>
6
7
#include < compartment-macros.h>
7
8
#include < debug.hh>
@@ -32,6 +33,26 @@ namespace
32
33
__builtin_bswap16 (value)
33
34
#else
34
35
value
36
+ #endif
37
+ ;
38
+ }
39
+ uint32_t constexpr ntohs (uint32_t value)
40
+ {
41
+ return
42
+ #ifdef __LITTLE_ENDIAN__
43
+ __builtin_bswap32 (value)
44
+ #else
45
+ value
46
+ #endif
47
+ ;
48
+ }
49
+ uint32_t constexpr htons (uint32_t value)
50
+ {
51
+ return
52
+ #ifdef __LITTLE_ENDIAN__
53
+ __builtin_bswap32 (value)
54
+ #else
55
+ value
35
56
#endif
36
57
;
37
58
}
@@ -172,13 +193,57 @@ namespace
172
193
}
173
194
} __packed;
174
195
196
+ static_assert (sizeof (IPv4Header) == 20 );
197
+
175
198
struct TCPUDPCommonPrefix
176
199
{
177
200
uint16_t sourcePort;
178
201
uint16_t destinationPort;
179
202
} __packed;
180
203
181
- static_assert (sizeof (IPv4Header) == 20 );
204
+ struct TCPHeader
205
+ {
206
+ /* *
207
+ * Source port.
208
+ */
209
+ uint16_t sourcePort;
210
+ /* *
211
+ * Destination port.
212
+ */
213
+ uint16_t destinationPort;
214
+ /* *
215
+ * Sequence number.
216
+ */
217
+ uint32_t sequenceNumber;
218
+ /* *
219
+ * Acknowledgement number.
220
+ */
221
+ uint32_t acknowledgementNumber;
222
+ /* *
223
+ * Reserved bits, data offset, and flags.
224
+ */
225
+ uint16_t reserved : 4 , dataOffset : 4 , fin : 1 , syn : 1 , rst : 1 ,
226
+ psh : 1 , ack : 1 , urg : 1 , ece : 1 , cwr : 1 ;
227
+ /* *
228
+ * Window size.
229
+ */
230
+ uint16_t windowSize;
231
+ /* *
232
+ * Checksum.
233
+ */
234
+ uint16_t checksum;
235
+ /* *
236
+ * Urgent pointer.
237
+ */
238
+ uint16_t urgentPointer;
239
+ } __packed;
240
+
241
+ struct FullPacket
242
+ {
243
+ EthernetHeader ethernet;
244
+ IPv4Header ipv4;
245
+ TCPHeader tcp;
246
+ } __packed;
182
247
183
248
/* *
184
249
* Simple firewall table for IPv4 endpoints.
@@ -320,6 +385,10 @@ namespace
320
385
});
321
386
if (!found)
322
387
{
388
+ // Note that a failure to remove the endpoint
389
+ // is not always a bug. This is meant to happen
390
+ // if the DNS resolution failed when binding a
391
+ // socket.
323
392
Debug::log (" Failed to remove endpoint (local: {})" , localPort);
324
393
}
325
394
}
@@ -361,6 +430,34 @@ namespace
361
430
uint32_t dnsServerAddress;
362
431
_Atomic (uint32_t ) dnsIsPermitted;
363
432
433
+ /* *
434
+ * This buffer will be pre-set into a TCP RST packet template during
435
+ * the initialization of the firewall. When the firewall needs to send
436
+ * a TCP RST, this template is updated with matching MACs, addresses,
437
+ * ports, sequence number, and checksums, and sent.
438
+ */
439
+ static struct FullPacket rstPacketTemplate = {0 };
440
+
441
+ /* *
442
+ * Pre-set the RST packet template.
443
+ */
444
+ void init_rst_template ()
445
+ {
446
+ rstPacketTemplate.ethernet .etherType = EtherType::IPv4;
447
+ // 5 x 32 bit = 20 bytes
448
+ rstPacketTemplate.ipv4 .versionAndHeaderLength = (4 << 4 ) | 5 ;
449
+ // The RST packet does not have a payload.
450
+ rstPacketTemplate.ipv4 .packetLength =
451
+ ntohs (static_cast <uint16_t >(sizeof (IPv4Header) + sizeof (TCPHeader)));
452
+ // Default TTL as recommended by RFC 1700.
453
+ rstPacketTemplate.ipv4 .timeToLive = 64 ;
454
+ rstPacketTemplate.ipv4 .protocol = IPProtocolNumber::TCP;
455
+ // 5 * 32 bit = 20 bytes (again)
456
+ rstPacketTemplate.tcp .dataOffset = 5 ;
457
+ // Enable TCP RST flag.
458
+ rstPacketTemplate.tcp .rst = 1 ;
459
+ }
460
+
364
461
bool packet_filter_ipv4 (const uint8_t *data,
365
462
size_t length,
366
463
uint32_t (IPv4Header::*remoteAddress),
@@ -502,16 +599,118 @@ namespace
502
599
return true ;
503
600
}
504
601
602
+ /* *
603
+ * If passed packet is an IPv4 TCP packet, reply with a RST to the
604
+ * sender. This takes the whole ethernet frame into `data` (and the
605
+ * size of the buffer in `length`).
606
+ */
607
+ void try_reset_ipv4_tcp (const uint8_t *data, size_t length)
608
+ {
609
+ if (__predict_false (length <
610
+ sizeof (EthernetHeader) + sizeof (IPv4Header)))
611
+ {
612
+ Debug::log (" Ignoring inbound packet with length {}" , length);
613
+ return ;
614
+ }
615
+
616
+ EthernetHeader *ethernetHeader =
617
+ reinterpret_cast <EthernetHeader *>(const_cast <uint8_t *>(data));
618
+ auto *ipv4Header =
619
+ reinterpret_cast <const IPv4Header *>(data + sizeof (EthernetHeader));
620
+ if (ipv4Header->protocol == IPProtocolNumber::TCP)
621
+ {
622
+ if (ipv4Header->body_offset () < sizeof (ipv4Header))
623
+ {
624
+ Debug::log (" Body offset is {} but IPv4 header is {} bytes" ,
625
+ ipv4Header->body_offset (),
626
+ sizeof (ipv4Header));
627
+ return ;
628
+ }
629
+ if (ipv4Header->body_offset () + sizeof (TCPHeader) > length)
630
+ {
631
+ Debug::log (" Ignoring inbound packet with length {}" , length);
632
+ return ;
633
+ }
634
+ const TCPHeader *tcpHeader = reinterpret_cast <const TCPHeader *>(
635
+ data + sizeof (EthernetHeader) + ipv4Header->body_offset ());
636
+
637
+ // Do not send a RST if the received TCP packet is
638
+ // itself a RST.
639
+ if (tcpHeader->rst == 1 )
640
+ {
641
+ Debug::log (" Ignoring inbound TCP RST packet." );
642
+ return ;
643
+ }
644
+
645
+ // Create a read-only capability to pass to the TCP/IP
646
+ // stack when we calculate checksums.
647
+ CHERI::Capability<uint8_t > rstPacketTemplateROCap{
648
+ reinterpret_cast <uint8_t *>(&rstPacketTemplate)};
649
+ // Remove all permissions except load. This also
650
+ // removes global, so that this cannot be captured.
651
+ rstPacketTemplateROCap.permissions () &=
652
+ CHERI::PermissionSet{CHERI::Permission::Load};
653
+
654
+ // / Build the RST packet.
655
+ // Source and destination MACs.
656
+ std::copy (std::begin (ethernetHeader->source ),
657
+ std::end (ethernetHeader->source ),
658
+ std::begin (rstPacketTemplate.ethernet .destination ));
659
+ std::copy (std::begin (ethernetHeader->destination ),
660
+ std::end (ethernetHeader->destination ),
661
+ std::begin (rstPacketTemplate.ethernet .source ));
662
+ // Source and destination IPs.
663
+ rstPacketTemplate.ipv4 .sourceAddress =
664
+ ipv4Header->destinationAddress ;
665
+ rstPacketTemplate.ipv4 .destinationAddress =
666
+ ipv4Header->sourceAddress ;
667
+ // IPv4 checksum. The value returned is in network byte
668
+ // order. Make sure to reset the checksum field's value
669
+ // before calculation.
670
+ rstPacketTemplate.ipv4 .headerChecksum = 0 ;
671
+ rstPacketTemplate.ipv4 .headerChecksum =
672
+ network_calculate_ipv4_checksum (rstPacketTemplateROCap +
673
+ sizeof (EthernetHeader),
674
+ sizeof (IPv4Header));
675
+ // Source and destination ports.
676
+ rstPacketTemplate.tcp .sourcePort = tcpHeader->destinationPort ;
677
+ rstPacketTemplate.tcp .destinationPort = tcpHeader->sourcePort ;
678
+ // Set the sequence number to the ack.
679
+ rstPacketTemplate.tcp .sequenceNumber =
680
+ tcpHeader->acknowledgementNumber ;
681
+ // TCP checksum. The value returned is in network byte
682
+ // order. No need to reset the field here as it isn't
683
+ // included in the calculation.
684
+ rstPacketTemplate.tcp .checksum = network_calculate_tcp_checksum (
685
+ rstPacketTemplateROCap,
686
+ sizeof (FullPacket),
687
+ sizeof (EthernetHeader) + sizeof (IPv4Header) + 16 );
688
+
689
+ // / Send the RST packet.
690
+ Debug::log (" Sending a RST packet." );
691
+ LockGuard g{sendLock};
692
+ auto ðernet = lazy_network_interface ();
693
+ // Do not go through the firewall: the packet would be
694
+ // rejected since the destination is not present in the
695
+ // table.
696
+ ethernet.send_frame (
697
+ rstPacketTemplateROCap,
698
+ sizeof (FullPacket),
699
+ [](const uint8_t *data, size_t length) { return true ; });
700
+ }
701
+ }
702
+
505
703
bool packet_filter_ingress (const uint8_t *data, size_t length)
506
704
{
507
- uint32_t stateSnapshot = tcpipRestartState->load ();
705
+ uint32_t stateSnapshot = tcpipRestartState->load ();
706
+ bool isOngoingReset = false ;
508
707
if (stateSnapshot != 0 &&
509
708
((stateSnapshot & RestartStateDriverKickedBit) == 0 ))
510
709
{
511
710
// We are in a reset and the driver has not yet been
512
711
// restarted.
513
712
Debug::log (" Dropping packet due to network stack restart." );
514
- return false ;
713
+ isOngoingReset = true ;
515
714
}
516
715
517
716
// Not a valid Ethernet frame (64 bytes including four-byte FCS, which
@@ -523,26 +722,42 @@ namespace
523
722
}
524
723
EthernetHeader *ethernetHeader =
525
724
reinterpret_cast <EthernetHeader *>(const_cast <uint8_t *>(data));
725
+ bool accept = false ;
526
726
switch (ethernetHeader->etherType )
527
727
{
528
728
// For now, testing with v6 disabled.
529
729
case EtherType::IPv6:
530
- return true ;
730
+ accept = true ;
731
+ break ;
531
732
case EtherType::ARP:
532
733
Debug::log (" Saw ARP frame" );
533
- return true ;
734
+ accept = true ;
735
+ break ;
534
736
case EtherType::IPv4:
535
- return packet_filter_ipv4 (data + sizeof (EthernetHeader),
536
- length - sizeof (EthernetHeader),
537
- &IPv4Header::sourceAddress,
538
- &TCPUDPCommonPrefix::destinationPort,
539
- &TCPUDPCommonPrefix::sourcePort,
540
- false );
541
- default :
542
- return false ;
737
+ if (!isOngoingReset)
738
+ {
739
+ accept =
740
+ packet_filter_ipv4 (data + sizeof (EthernetHeader),
741
+ length - sizeof (EthernetHeader),
742
+ &IPv4Header::sourceAddress,
743
+ &TCPUDPCommonPrefix::destinationPort,
744
+ &TCPUDPCommonPrefix::sourcePort,
745
+ false );
746
+ }
747
+ if (!accept)
748
+ {
749
+ // If this is a TCP packet, send a RST
750
+ // to the source.
751
+ //
752
+ // Only reset for IPv4 for now. Pass
753
+ // the whole Ethernet packet (unlike
754
+ // `packet_filter_ipv4`).
755
+ try_reset_ipv4_tcp (data, length);
756
+ }
757
+ break ;
543
758
}
544
759
545
- return false ;
760
+ return accept && !isOngoingReset ;
546
761
}
547
762
548
763
std::atomic<uint32_t > receivedCounter;
@@ -787,6 +1002,7 @@ bool ethernet_driver_start(std::atomic<uint8_t> *state)
787
1002
Debug::log (" Initialising network interface" );
788
1003
auto ðernet = lazy_network_interface ();
789
1004
ethernet.mac_address_set ();
1005
+ init_rst_template ();
790
1006
// Poke the barrier and make the driver thread start.
791
1007
barrier = 2 ;
792
1008
barrier.notify_one ();
0 commit comments