Serenity Operating System
at hosted 283 lines 10 kB view raw
1/* 2 * Copyright (c) 2020, The SerenityOS developers. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include "DHCPv4Client.h" 28#include <AK/ByteBuffer.h> 29#include <AK/Function.h> 30#include <LibCore/SocketAddress.h> 31#include <LibCore/Timer.h> 32#include <stdio.h> 33 34//#define DHCPV4CLIENT_DEBUG 35 36static void send(const InterfaceDescriptor& iface, const DHCPv4Packet& packet, Core::Object*) 37{ 38 int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 39 if (fd < 0) { 40 dbg() << "ERROR: socket :: " << strerror(errno); 41 return; 42 } 43 44 if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, iface.m_ifname.characters(), IFNAMSIZ) < 0) { 45 dbg() << "ERROR: setsockopt(SO_BINDTODEVICE) :: " << strerror(errno); 46 return; 47 } 48 49 sockaddr_in dst; 50 memset(&dst, 0, sizeof(dst)); 51 dst.sin_family = AF_INET; 52 dst.sin_port = htons(67); 53 dst.sin_addr.s_addr = IPv4Address { 255, 255, 255, 255 }.to_u32(); 54 memset(&dst.sin_zero, 0, sizeof(dst.sin_zero)); 55 56 auto rc = sendto(fd, &packet, sizeof(packet), 0, (sockaddr*)&dst, sizeof(dst)); 57 if (rc < 0) { 58 dbg() << "sendto failed with " << strerror(errno); 59 // FIXME: what do we do here? 60 } 61} 62 63static void set_params(const InterfaceDescriptor& iface, const IPv4Address& ipv4_addr, const IPv4Address& netmask, const IPv4Address& gateway) 64{ 65 int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); 66 if (fd < 0) { 67 dbg() << "ERROR: socket :: " << strerror(errno); 68 return; 69 } 70 71 struct ifreq ifr; 72 memset(&ifr, 0, sizeof(ifr)); 73 strncpy(ifr.ifr_name, iface.m_ifname.characters(), IFNAMSIZ); 74 75 // set the IP address 76 ifr.ifr_addr.sa_family = AF_INET; 77 ((sockaddr_in&)ifr.ifr_addr).sin_addr.s_addr = ipv4_addr.to_in_addr_t(); 78 79 if (ioctl(fd, SIOCSIFADDR, &ifr) < 0) { 80 dbg() << "ERROR: ioctl(SIOCSIFADDR) :: " << strerror(errno); 81 } 82 83 // set the network mask 84 ((sockaddr_in&)ifr.ifr_netmask).sin_addr.s_addr = netmask.to_in_addr_t(); 85 86 if (ioctl(fd, SIOCSIFNETMASK, &ifr) < 0) { 87 dbg() << "ERROR: ioctl(SIOCSIFNETMASK) :: " << strerror(errno); 88 } 89 90 // set the default gateway 91 struct rtentry rt; 92 memset(&rt, 0, sizeof(rt)); 93 94 rt.rt_dev = const_cast<char*>(iface.m_ifname.characters()); 95 rt.rt_gateway.sa_family = AF_INET; 96 ((sockaddr_in&)rt.rt_gateway).sin_addr.s_addr = gateway.to_in_addr_t(); 97 rt.rt_flags = RTF_UP | RTF_GATEWAY; 98 99 if (ioctl(fd, SIOCADDRT, &rt) < 0) { 100 dbg() << "Error: ioctl(SIOCADDRT) :: " << strerror(errno); 101 } 102} 103 104DHCPv4Client::DHCPv4Client(Vector<InterfaceDescriptor> ifnames) 105 : m_ifnames(ifnames) 106{ 107 m_server = Core::UDPServer::construct(this); 108 m_server->on_ready_to_receive = [this] { 109 auto buffer = m_server->receive(sizeof(DHCPv4Packet)); 110 dbg() << "Received " << buffer.size() << " bytes"; 111 if (buffer.size() != sizeof(DHCPv4Packet)) { 112 dbg() << "we expected " << sizeof(DHCPv4Packet) << " bytes, this is a bad packet"; 113 return; 114 } 115 auto& packet = *(DHCPv4Packet*)buffer.data(); 116 process_incoming(packet); 117 }; 118 119 if (!m_server->bind({}, 68)) { 120 dbg() << "The server we just created somehow came already bound, refusing to continue"; 121 ASSERT_NOT_REACHED(); 122 } 123 124 for (auto& iface : m_ifnames) 125 dhcp_discover(iface); 126} 127 128DHCPv4Client::~DHCPv4Client() 129{ 130} 131 132void DHCPv4Client::handle_offer(const DHCPv4Packet& packet, const ParsedDHCPv4Options& options) 133{ 134 dbg() << "We were offered " << packet.yiaddr().to_string() << " for " << options.get<u32>(DHCPOption::IPAddressLeaseTime).value_or(0); 135 auto* transaction = const_cast<DHCPv4Transaction*>(m_ongoing_transactions.get(packet.xid()).value_or(nullptr)); 136 if (!transaction) { 137 dbg() << "we're not looking for " << packet.xid(); 138 return; 139 } 140 if (transaction->has_ip) 141 return; 142 if (transaction->accepted_offer) { 143 // we've accepted someone's offer, but they haven't given us an ack 144 // TODO: maybe record this offer? 145 return; 146 } 147 // TAKE IT... 148 transaction->offered_lease_time = options.get<u32>(DHCPOption::IPAddressLeaseTime).value(); 149 dhcp_request(*transaction, packet); 150} 151 152void DHCPv4Client::handle_ack(const DHCPv4Packet& packet, const ParsedDHCPv4Options& options) 153{ 154#ifdef DHCPV4CLIENT_DEBUG 155 dbg() << "The DHCP server handed us " << packet.yiaddr().to_string(); 156 dbg() << "Here are the options: " << options.to_string(); 157#endif 158 auto* transaction = const_cast<DHCPv4Transaction*>(m_ongoing_transactions.get(packet.xid()).value_or(nullptr)); 159 if (!transaction) { 160 dbg() << "we're not looking for " << packet.xid(); 161 return; 162 } 163 transaction->has_ip = true; 164 auto& interface = transaction->interface; 165 auto new_ip = packet.yiaddr(); 166 auto lease_time = convert_between_host_and_network(options.get<u32>(DHCPOption::IPAddressLeaseTime).value_or(transaction->offered_lease_time)); 167 // set a timer for the duration of the lease, we shall renew if needed 168 Core::Timer::create_single_shot( 169 lease_time * 1000, 170 [this, transaction, interface = InterfaceDescriptor { interface }, new_ip] { 171 transaction->accepted_offer = false; 172 transaction->has_ip = false; 173 dhcp_discover(interface, new_ip); 174 }, 175 this); 176 set_params(transaction->interface, new_ip, options.get<IPv4Address>(DHCPOption::SubnetMask).value(), options.get_many<IPv4Address>(DHCPOption::Router, 1).first()); 177} 178 179void DHCPv4Client::handle_nak(const DHCPv4Packet& packet, const ParsedDHCPv4Options& options) 180{ 181 dbg() << "The DHCP server told us to go chase our own tail about " << packet.yiaddr().to_string(); 182 dbg() << "Here are the options: " << options.to_string(); 183 // make another request a bit later :shrug: 184 auto* transaction = const_cast<DHCPv4Transaction*>(m_ongoing_transactions.get(packet.xid()).value_or(nullptr)); 185 if (!transaction) { 186 dbg() << "we're not looking for " << packet.xid(); 187 return; 188 } 189 transaction->accepted_offer = false; 190 transaction->has_ip = false; 191 auto& iface = transaction->interface; 192 Core::Timer::create_single_shot( 193 10000, 194 [this, iface = InterfaceDescriptor { iface }] { 195 dhcp_discover(iface); 196 }, 197 this); 198} 199 200void DHCPv4Client::process_incoming(const DHCPv4Packet& packet) 201{ 202 auto options = packet.parse_options(); 203#ifdef DHCPV4CLIENT_DEBUG 204 dbg() << "Here are the options: " << options.to_string(); 205#endif 206 auto value = options.get<DHCPMessageType>(DHCPOption::DHCPMessageType).value(); 207 switch (value) { 208 case DHCPMessageType::DHCPOffer: 209 handle_offer(packet, options); 210 break; 211 case DHCPMessageType::DHCPAck: 212 handle_ack(packet, options); 213 break; 214 case DHCPMessageType::DHCPNak: 215 handle_nak(packet, options); 216 break; 217 case DHCPMessageType::DHCPDiscover: 218 case DHCPMessageType::DHCPRequest: 219 case DHCPMessageType::DHCPRelease: 220 // These are not for us 221 // we're just getting them because there are other people on our subnet 222 // broadcasting stuff 223 break; 224 case DHCPMessageType::DHCPDecline: 225 default: 226 dbg() << "I dunno what to do with this " << (u8)value; 227 ASSERT_NOT_REACHED(); 228 break; 229 } 230} 231 232void DHCPv4Client::dhcp_discover(const InterfaceDescriptor& iface, IPv4Address previous) 233{ 234 auto transaction_id = rand(); 235#ifdef DHCPV4CLIENT_DEBUG 236 dbg() << "Trying to lease an IP for " << iface.m_ifname << " with ID " << transaction_id; 237 if (!previous.is_zero()) 238 dbg() << "going to request the server to hand us " << previous.to_string(); 239#endif 240 DHCPv4PacketBuilder builder; 241 242 DHCPv4Packet& packet = builder.peek(); 243 packet.set_op(DHCPv4Op::BootRequest); 244 packet.set_htype(1); // 10mb ethernet 245 packet.set_hlen(sizeof(MACAddress)); 246 packet.set_xid(transaction_id); 247 packet.set_flags(DHCPv4Flags::Broadcast); 248 packet.ciaddr() = previous; 249 packet.set_chaddr(iface.m_mac_address); 250 packet.set_secs(65535); // we lie 251 252 // set packet options 253 builder.set_message_type(DHCPMessageType::DHCPDiscover); 254 auto& dhcp_packet = builder.build(); 255 256 // broadcast the discover request 257 send(iface, dhcp_packet, this); 258 m_ongoing_transactions.set(transaction_id, make<DHCPv4Transaction>(iface)); 259} 260 261void DHCPv4Client::dhcp_request(DHCPv4Transaction& transaction, const DHCPv4Packet& offer) 262{ 263 auto& iface = transaction.interface; 264 dbg() << "Leasing the IP " << offer.yiaddr().to_string() << " for adapter " << iface.m_ifname; 265 DHCPv4PacketBuilder builder; 266 267 DHCPv4Packet& packet = builder.peek(); 268 packet.set_op(DHCPv4Op::BootRequest); 269 packet.set_htype(1); // 10mb ethernet 270 packet.set_hlen(sizeof(MACAddress)); 271 packet.set_xid(offer.xid()); 272 packet.set_flags(DHCPv4Flags::Broadcast); 273 packet.set_chaddr(iface.m_mac_address); 274 packet.set_secs(65535); // we lie 275 276 // set packet options 277 builder.set_message_type(DHCPMessageType::DHCPRequest); 278 auto& dhcp_packet = builder.build(); 279 280 // broadcast the "request" request 281 send(iface, dhcp_packet, this); 282 transaction.accepted_offer = true; 283}