mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 09:42:45 +00:00 
			
		
		
		
	Kernel: Properly support the SO_BROADCAST socket option
POSIX requires that broadcast sends will only be allowed if the SO_BROADCAST socket option was set on the socket. Also, broadcast sends to protocols that do not support broadcast (like TCP), should always fail.
This commit is contained in:
		
							parent
							
								
									8b2beb2ebe
								
							
						
					
					
						commit
						545f4b6cc1
					
				
					 7 changed files with 36 additions and 6 deletions
				
			
		|  | @ -211,9 +211,10 @@ ErrorOr<size_t> IPv4Socket::sendto(OpenFileDescription&, UserOrKernelBuffer cons | ||||||
|     if (!is_connected() && m_peer_address.is_zero()) |     if (!is_connected() && m_peer_address.is_zero()) | ||||||
|         return set_so_error(EPIPE); |         return set_so_error(EPIPE); | ||||||
| 
 | 
 | ||||||
|  |     auto allow_broadcast = m_broadcast_allowed ? AllowBroadcast::Yes : AllowBroadcast::No; | ||||||
|     auto allow_using_gateway = ((flags & MSG_DONTROUTE) || m_routing_disabled) ? AllowUsingGateway::No : AllowUsingGateway::Yes; |     auto allow_using_gateway = ((flags & MSG_DONTROUTE) || m_routing_disabled) ? AllowUsingGateway::No : AllowUsingGateway::Yes; | ||||||
|     auto adapter = bound_interface().with([](auto& bound_device) -> RefPtr<NetworkAdapter> { return bound_device; }); |     auto adapter = bound_interface().with([](auto& bound_device) -> RefPtr<NetworkAdapter> { return bound_device; }); | ||||||
|     auto routing_decision = route_to(m_peer_address, m_local_address, adapter, allow_using_gateway); |     auto routing_decision = route_to(m_peer_address, m_local_address, adapter, allow_broadcast, allow_using_gateway); | ||||||
|     if (routing_decision.is_zero()) |     if (routing_decision.is_zero()) | ||||||
|         return set_so_error(EHOSTUNREACH); |         return set_so_error(EHOSTUNREACH); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -178,7 +178,7 @@ static MACAddress multicast_ethernet_address(IPv4Address const& address) | ||||||
|     return MACAddress { 0x01, 0x00, 0x5e, (u8)(address[1] & 0x7f), address[2], address[3] }; |     return MACAddress { 0x01, 0x00, 0x5e, (u8)(address[1] & 0x7f), address[2], address[3] }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| RoutingDecision route_to(IPv4Address const& target, IPv4Address const& source, RefPtr<NetworkAdapter> const through, AllowUsingGateway allow_using_gateway) | RoutingDecision route_to(IPv4Address const& target, IPv4Address const& source, RefPtr<NetworkAdapter> const through, AllowBroadcast allow_broadcast, AllowUsingGateway allow_using_gateway) | ||||||
| { | { | ||||||
|     auto matches = [&](auto& adapter) { |     auto matches = [&](auto& adapter) { | ||||||
|         if (!through) |         if (!through) | ||||||
|  | @ -291,8 +291,11 @@ RoutingDecision route_to(IPv4Address const& target, IPv4Address const& source, R | ||||||
|     // If it's a broadcast, we already know everything we need to know.
 |     // If it's a broadcast, we already know everything we need to know.
 | ||||||
|     // FIXME: We should also deal with the case where `target_addr` is
 |     // FIXME: We should also deal with the case where `target_addr` is
 | ||||||
|     //        a broadcast to a subnet rather than a full broadcast.
 |     //        a broadcast to a subnet rather than a full broadcast.
 | ||||||
|     if (target_addr == 0xffffffff && matches(adapter)) |     if (target_addr == 0xffffffff && matches(adapter)) { | ||||||
|  |         if (allow_broadcast == AllowBroadcast::Yes) | ||||||
|             return { adapter, { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }; |             return { adapter, { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } }; | ||||||
|  |         return { nullptr, {} }; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     if (adapter == NetworkingManagement::the().loopback_adapter()) |     if (adapter == NetworkingManagement::the().loopback_adapter()) | ||||||
|         return { adapter, adapter->mac_address() }; |         return { adapter, adapter->mac_address() }; | ||||||
|  |  | ||||||
|  | @ -64,7 +64,12 @@ enum class AllowUsingGateway { | ||||||
|     No, |     No, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| RoutingDecision route_to(IPv4Address const& target, IPv4Address const& source, RefPtr<NetworkAdapter> const through = nullptr, AllowUsingGateway = AllowUsingGateway::Yes); | enum class AllowBroadcast { | ||||||
|  |     Yes, | ||||||
|  |     No, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | RoutingDecision route_to(IPv4Address const& target, IPv4Address const& source, RefPtr<NetworkAdapter> const through = nullptr, AllowBroadcast = AllowBroadcast::No, AllowUsingGateway = AllowUsingGateway::Yes); | ||||||
| 
 | 
 | ||||||
| SpinlockProtected<HashMap<IPv4Address, MACAddress>, LockRank::None>& arp_table(); | SpinlockProtected<HashMap<IPv4Address, MACAddress>, LockRank::None>& arp_table(); | ||||||
| SpinlockProtected<Route::RouteList, LockRank::None>& routing_table(); | SpinlockProtected<Route::RouteList, LockRank::None>& routing_table(); | ||||||
|  |  | ||||||
|  | @ -130,6 +130,12 @@ ErrorOr<void> Socket::setsockopt(int level, int option, Userspace<void const*> u | ||||||
|     case SO_REUSEADDR: |     case SO_REUSEADDR: | ||||||
|         dbgln("FIXME: SO_REUSEADDR requested, but not implemented."); |         dbgln("FIXME: SO_REUSEADDR requested, but not implemented."); | ||||||
|         return {}; |         return {}; | ||||||
|  |     case SO_BROADCAST: { | ||||||
|  |         if (user_value_size != sizeof(int)) | ||||||
|  |             return EINVAL; | ||||||
|  |         m_broadcast_allowed = TRY(copy_typed_from_user(static_ptr_cast<int const*>(user_value))) != 0; | ||||||
|  |         return {}; | ||||||
|  |     } | ||||||
|     default: |     default: | ||||||
|         dbgln("setsockopt({}) at SOL_SOCKET not implemented.", option); |         dbgln("setsockopt({}) at SOL_SOCKET not implemented.", option); | ||||||
|         return ENOPROTOOPT; |         return ENOPROTOOPT; | ||||||
|  | @ -235,6 +241,14 @@ ErrorOr<void> Socket::getsockopt(OpenFileDescription&, int level, int option, Us | ||||||
|         size = sizeof(routing_disabled); |         size = sizeof(routing_disabled); | ||||||
|         return copy_to_user(value_size, &size); |         return copy_to_user(value_size, &size); | ||||||
|     } |     } | ||||||
|  |     case SO_BROADCAST: { | ||||||
|  |         int broadcast_allowed = m_broadcast_allowed ? 1 : 0; | ||||||
|  |         if (size < sizeof(broadcast_allowed)) | ||||||
|  |             return EINVAL; | ||||||
|  |         TRY(copy_to_user(static_ptr_cast<int*>(value), &broadcast_allowed)); | ||||||
|  |         size = sizeof(broadcast_allowed); | ||||||
|  |         return copy_to_user(value_size, &size); | ||||||
|  |     } | ||||||
|     default: |     default: | ||||||
|         dbgln("getsockopt({}) at SOL_SOCKET not implemented.", option); |         dbgln("getsockopt({}) at SOL_SOCKET not implemented.", option); | ||||||
|         return ENOPROTOOPT; |         return ENOPROTOOPT; | ||||||
|  |  | ||||||
|  | @ -156,6 +156,7 @@ protected: | ||||||
|     ucred m_origin { 0, 0, 0 }; |     ucred m_origin { 0, 0, 0 }; | ||||||
|     ucred m_acceptor { 0, 0, 0 }; |     ucred m_acceptor { 0, 0, 0 }; | ||||||
|     bool m_routing_disabled { false }; |     bool m_routing_disabled { false }; | ||||||
|  |     bool m_broadcast_allowed { false }; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     virtual bool is_socket() const final { return true; } |     virtual bool is_socket() const final { return true; } | ||||||
|  |  | ||||||
|  | @ -85,7 +85,8 @@ ErrorOr<size_t> UDPSocket::protocol_receive(ReadonlyBytes raw_ipv4_packet, UserO | ||||||
| ErrorOr<size_t> UDPSocket::protocol_send(UserOrKernelBuffer const& data, size_t data_length) | ErrorOr<size_t> UDPSocket::protocol_send(UserOrKernelBuffer const& data, size_t data_length) | ||||||
| { | { | ||||||
|     auto adapter = bound_interface().with([](auto& bound_device) -> RefPtr<NetworkAdapter> { return bound_device; }); |     auto adapter = bound_interface().with([](auto& bound_device) -> RefPtr<NetworkAdapter> { return bound_device; }); | ||||||
|     auto routing_decision = route_to(peer_address(), local_address(), adapter); |     auto allow_broadcast = m_broadcast_allowed ? AllowBroadcast::Yes : AllowBroadcast::No; | ||||||
|  |     auto routing_decision = route_to(peer_address(), local_address(), adapter, allow_broadcast); | ||||||
|     if (routing_decision.is_zero()) |     if (routing_decision.is_zero()) | ||||||
|         return set_so_error(EHOSTUNREACH); |         return set_so_error(EHOSTUNREACH); | ||||||
|     auto ipv4_payload_offset = routing_decision.adapter->ipv4_payload_offset(); |     auto ipv4_payload_offset = routing_decision.adapter->ipv4_payload_offset(); | ||||||
|  |  | ||||||
|  | @ -49,6 +49,11 @@ static bool send(InterfaceDescriptor const& iface, DHCPv4Packet const& packet, C | ||||||
|         dbgln("ERROR: setsockopt(SO_BINDTODEVICE) :: {}", strerror(errno)); |         dbgln("ERROR: setsockopt(SO_BINDTODEVICE) :: {}", strerror(errno)); | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
|  |     int allow_broadcast = 1; | ||||||
|  |     if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &allow_broadcast, sizeof(int)) < 0) { | ||||||
|  |         dbgln("ERROR: setsockopt(SO_BROADCAST) :: {}", strerror(errno)); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     sockaddr_in dst; |     sockaddr_in dst; | ||||||
|     memset(&dst, 0, sizeof(dst)); |     memset(&dst, 0, sizeof(dst)); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Idan Horowitz
						Idan Horowitz