1212#include < timeout.hh>
1313#include < vector>
1414
15+ namespace
16+ {
17+ // TODO These should probably be in their own library.
18+ uint16_t constexpr ntohs (uint16_t value)
19+ {
20+ return
21+ #ifdef __LITTLE_ENDIAN__
22+ __builtin_bswap16 (value)
23+ #else
24+ value
25+ #endif
26+ ;
27+ }
28+ uint16_t constexpr htons (uint16_t value)
29+ {
30+ return
31+ #ifdef __LITTLE_ENDIAN__
32+ __builtin_bswap16 (value)
33+ #else
34+ value
35+ #endif
36+ ;
37+ }
38+ } // namespace
39+
1540namespace
1641{
1742 using Debug = ConditionalDebug<false , " Firewall" >;
@@ -85,10 +110,13 @@ namespace
85110 UDP = 17 ,
86111 };
87112
113+ static constexpr const uint16_t DhcpServerPort = 67 ;
114+ static constexpr const uint16_t DhcpClientPort = 68 ;
115+
88116 struct IPv4Header
89117 {
90118 /* *
91- * Verson is in the low 4 bits, header length is in the high 4 bits.
119+ * Version is in the low 4 bits, header length is in the high 4 bits.
92120 */
93121 uint8_t versionAndHeaderLength;
94122 /* *
@@ -286,6 +314,23 @@ namespace
286314 }
287315 };
288316
317+ bool is_dhcp_reply (enum IPProtocolNumber protocol,
318+ bool isIngress,
319+ uint16_t remotePort,
320+ uint16_t localPort)
321+ {
322+ // A DHCP reply is an ingress UDP packet whose remote port
323+ // matches the DHCP server port, and whose local port matches
324+ // the DHCP client port.
325+ if (isIngress && (protocol == IPProtocolNumber::UDP) &&
326+ (remotePort == htons (DhcpServerPort)) &&
327+ (localPort == htons (DhcpClientPort)))
328+ {
329+ return true ;
330+ }
331+ return false ;
332+ }
333+
289334 uint32_t dnsServerAddress;
290335 _Atomic (uint32_t ) dnsIsPermitted;
291336
@@ -304,19 +349,13 @@ namespace
304349 auto *ipv4Header = reinterpret_cast <const IPv4Header *>(data);
305350 switch (ipv4Header->protocol )
306351 {
307- // Drop all packets with unknown protocol types.
352+ // Drop all packets with unknown IP protocol types.
308353 default :
309354 Debug::log (" Dropping IPv4 packet with unknown protocol {}" ,
310355 ipv4Header->protocol );
311356 return false ;
312357 case IPProtocolNumber::UDP:
313- // If we haven't finished doing DHCP, permit all UDP packets
314- // (we don't know the address of the DHCP server yet, so
315- // IP-based filtering is not going to be reliable).
316- if (__predict_false (dnsServerAddress == 0 ))
317- {
318- return true ;
319- }
358+
320359 // Permit DNS requests during a DNS query.
321360 if (dnsIsPermitted > 0 )
322361 {
@@ -356,6 +395,7 @@ namespace
356395 uint32_t endpoint = ipv4Header->*remoteAddress;
357396 uint16_t localPortNumber = tcpudpHeader->*localPort;
358397 uint16_t remotePortNumber = tcpudpHeader->*remotePort;
398+ bool isIngress = (remoteAddress == &IPv4Header::sourceAddress);
359399 if (EndpointsTable<uint32_t >::instance ().is_endpoint_permitted (
360400 ipv4Header->protocol ,
361401 endpoint,
@@ -364,15 +404,21 @@ namespace
364404 {
365405 Debug::log (" Permitting {} {} {}.{}.{}.{}" ,
366406 ipv4Header->protocol ,
367- remoteAddress == &IPv4Header::destinationAddress
368- ? " to"
369- : " from" ,
407+ isIngress ? " from" : " to" ,
370408 static_cast <int >(endpoint) & 0xff ,
371409 static_cast <int >(endpoint >> 8 ) & 0xff ,
372410 static_cast <int >(endpoint >> 16 ) & 0xff ,
373411 static_cast <int >(endpoint >> 24 ) & 0xff );
374412 return true ;
375413 }
414+ // Permit DHCP replies
415+ if (is_dhcp_reply (ipv4Header->protocol ,
416+ isIngress,
417+ remotePortNumber,
418+ localPortNumber))
419+ {
420+ return true ;
421+ }
376422 return false ;
377423 }
378424 break ;
0 commit comments