DHCPv4Client.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  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. #include "DHCPv4Client.h"
  27. #include <AK/ByteBuffer.h>
  28. #include <AK/Debug.h>
  29. #include <AK/Endian.h>
  30. #include <AK/Function.h>
  31. #include <AK/Random.h>
  32. #include <LibCore/SocketAddress.h>
  33. #include <LibCore/Timer.h>
  34. #include <stdio.h>
  35. static void send(const InterfaceDescriptor& iface, const DHCPv4Packet& packet, Core::Object*)
  36. {
  37. int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  38. if (fd < 0) {
  39. dbgln("ERROR: socket :: {}", strerror(errno));
  40. return;
  41. }
  42. if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, iface.m_ifname.characters(), IFNAMSIZ) < 0) {
  43. dbgln("ERROR: setsockopt(SO_BINDTODEVICE) :: {}", strerror(errno));
  44. return;
  45. }
  46. sockaddr_in dst;
  47. memset(&dst, 0, sizeof(dst));
  48. dst.sin_family = AF_INET;
  49. dst.sin_port = htons(67);
  50. dst.sin_addr.s_addr = IPv4Address { 255, 255, 255, 255 }.to_u32();
  51. memset(&dst.sin_zero, 0, sizeof(dst.sin_zero));
  52. dbgln_if(DHCPV4CLIENT_DEBUG, "sendto({} bound to {}, ..., {} at {}) = ...?", fd, iface.m_ifname, dst.sin_addr.s_addr, dst.sin_port);
  53. auto rc = sendto(fd, &packet, sizeof(packet), 0, (sockaddr*)&dst, sizeof(dst));
  54. dbgln_if(DHCPV4CLIENT_DEBUG, "sendto({}) = {}", fd, rc);
  55. if (rc < 0) {
  56. dbgln("sendto failed with {}", strerror(errno));
  57. // FIXME: what do we do here?
  58. }
  59. }
  60. static void set_params(const InterfaceDescriptor& iface, const IPv4Address& ipv4_addr, const IPv4Address& netmask, const IPv4Address& gateway)
  61. {
  62. int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
  63. if (fd < 0) {
  64. dbgln("ERROR: socket :: {}", strerror(errno));
  65. return;
  66. }
  67. struct ifreq ifr;
  68. memset(&ifr, 0, sizeof(ifr));
  69. bool fits = iface.m_ifname.copy_characters_to_buffer(ifr.ifr_name, IFNAMSIZ);
  70. if (!fits) {
  71. dbgln("Interface name doesn't fit into IFNAMSIZ!");
  72. return;
  73. }
  74. // set the IP address
  75. ifr.ifr_addr.sa_family = AF_INET;
  76. ((sockaddr_in&)ifr.ifr_addr).sin_addr.s_addr = ipv4_addr.to_in_addr_t();
  77. if (ioctl(fd, SIOCSIFADDR, &ifr) < 0) {
  78. dbgln("ERROR: ioctl(SIOCSIFADDR) :: {}", strerror(errno));
  79. }
  80. // set the network mask
  81. ((sockaddr_in&)ifr.ifr_netmask).sin_addr.s_addr = netmask.to_in_addr_t();
  82. if (ioctl(fd, SIOCSIFNETMASK, &ifr) < 0) {
  83. dbgln("ERROR: ioctl(SIOCSIFNETMASK) :: {}", strerror(errno));
  84. }
  85. // set the default gateway
  86. struct rtentry rt;
  87. memset(&rt, 0, sizeof(rt));
  88. rt.rt_dev = const_cast<char*>(iface.m_ifname.characters());
  89. rt.rt_gateway.sa_family = AF_INET;
  90. ((sockaddr_in&)rt.rt_gateway).sin_addr.s_addr = gateway.to_in_addr_t();
  91. rt.rt_flags = RTF_UP | RTF_GATEWAY;
  92. if (ioctl(fd, SIOCADDRT, &rt) < 0) {
  93. dbgln("Error: ioctl(SIOCADDRT) :: {}", strerror(errno));
  94. }
  95. }
  96. DHCPv4Client::DHCPv4Client(Vector<InterfaceDescriptor> ifnames)
  97. : m_ifnames(ifnames)
  98. {
  99. m_server = Core::UDPServer::construct(this);
  100. m_server->on_ready_to_receive = [this] {
  101. auto buffer = m_server->receive(sizeof(DHCPv4Packet));
  102. dbgln_if(DHCPV4CLIENT_DEBUG, "Received {} bytes", buffer.size());
  103. if (buffer.size() < sizeof(DHCPv4Packet) - DHCPV4_OPTION_FIELD_MAX_LENGTH + 1 || buffer.size() > sizeof(DHCPv4Packet)) {
  104. dbgln("we expected {}-{} bytes, this is a bad packet", sizeof(DHCPv4Packet) - DHCPV4_OPTION_FIELD_MAX_LENGTH + 1, sizeof(DHCPv4Packet));
  105. return;
  106. }
  107. auto& packet = *(DHCPv4Packet*)buffer.data();
  108. process_incoming(packet);
  109. };
  110. if (!m_server->bind({}, 68)) {
  111. dbgln("The server we just created somehow came already bound, refusing to continue");
  112. ASSERT_NOT_REACHED();
  113. }
  114. for (auto& iface : m_ifnames)
  115. dhcp_discover(iface);
  116. }
  117. DHCPv4Client::~DHCPv4Client()
  118. {
  119. }
  120. void DHCPv4Client::handle_offer(const DHCPv4Packet& packet, const ParsedDHCPv4Options& options)
  121. {
  122. dbgln("We were offered {} for {}", packet.yiaddr().to_string(), options.get<u32>(DHCPOption::IPAddressLeaseTime).value_or(0));
  123. auto* transaction = const_cast<DHCPv4Transaction*>(m_ongoing_transactions.get(packet.xid()).value_or(nullptr));
  124. if (!transaction) {
  125. dbgln("we're not looking for {}", packet.xid());
  126. return;
  127. }
  128. if (transaction->has_ip)
  129. return;
  130. if (transaction->accepted_offer) {
  131. // we've accepted someone's offer, but they haven't given us an ack
  132. // TODO: maybe record this offer?
  133. return;
  134. }
  135. // TAKE IT...
  136. transaction->offered_lease_time = options.get<u32>(DHCPOption::IPAddressLeaseTime).value();
  137. dhcp_request(*transaction, packet);
  138. }
  139. void DHCPv4Client::handle_ack(const DHCPv4Packet& packet, const ParsedDHCPv4Options& options)
  140. {
  141. if constexpr (DHCPV4CLIENT_DEBUG) {
  142. dbgln("The DHCP server handed us {}", packet.yiaddr().to_string());
  143. dbgln("Here are the options: {}", options.to_string());
  144. }
  145. auto* transaction = const_cast<DHCPv4Transaction*>(m_ongoing_transactions.get(packet.xid()).value_or(nullptr));
  146. if (!transaction) {
  147. dbgln("we're not looking for {}", packet.xid());
  148. return;
  149. }
  150. transaction->has_ip = true;
  151. auto& interface = transaction->interface;
  152. auto new_ip = packet.yiaddr();
  153. auto lease_time = AK::convert_between_host_and_network_endian(options.get<u32>(DHCPOption::IPAddressLeaseTime).value_or(transaction->offered_lease_time));
  154. // set a timer for the duration of the lease, we shall renew if needed
  155. Core::Timer::create_single_shot(
  156. lease_time * 1000,
  157. [this, transaction, interface = InterfaceDescriptor { interface }, new_ip] {
  158. transaction->accepted_offer = false;
  159. transaction->has_ip = false;
  160. dhcp_discover(interface, new_ip);
  161. },
  162. this);
  163. set_params(transaction->interface, new_ip, options.get<IPv4Address>(DHCPOption::SubnetMask).value(), options.get_many<IPv4Address>(DHCPOption::Router, 1).first());
  164. }
  165. void DHCPv4Client::handle_nak(const DHCPv4Packet& packet, const ParsedDHCPv4Options& options)
  166. {
  167. dbgln("The DHCP server told us to go chase our own tail about {}", packet.yiaddr().to_string());
  168. dbgln("Here are the options: {}", options.to_string());
  169. // make another request a bit later :shrug:
  170. auto* transaction = const_cast<DHCPv4Transaction*>(m_ongoing_transactions.get(packet.xid()).value_or(nullptr));
  171. if (!transaction) {
  172. dbgln("we're not looking for {}", packet.xid());
  173. return;
  174. }
  175. transaction->accepted_offer = false;
  176. transaction->has_ip = false;
  177. auto& iface = transaction->interface;
  178. Core::Timer::create_single_shot(
  179. 10000,
  180. [this, iface = InterfaceDescriptor { iface }] {
  181. dhcp_discover(iface);
  182. },
  183. this);
  184. }
  185. void DHCPv4Client::process_incoming(const DHCPv4Packet& packet)
  186. {
  187. auto options = packet.parse_options();
  188. dbgln_if(DHCPV4CLIENT_DEBUG, "Here are the options: {}", options.to_string());
  189. auto value = options.get<DHCPMessageType>(DHCPOption::DHCPMessageType).value();
  190. switch (value) {
  191. case DHCPMessageType::DHCPOffer:
  192. handle_offer(packet, options);
  193. break;
  194. case DHCPMessageType::DHCPAck:
  195. handle_ack(packet, options);
  196. break;
  197. case DHCPMessageType::DHCPNak:
  198. handle_nak(packet, options);
  199. break;
  200. case DHCPMessageType::DHCPDiscover:
  201. case DHCPMessageType::DHCPRequest:
  202. case DHCPMessageType::DHCPRelease:
  203. // These are not for us
  204. // we're just getting them because there are other people on our subnet
  205. // broadcasting stuff
  206. break;
  207. case DHCPMessageType::DHCPDecline:
  208. default:
  209. dbgln("I dunno what to do with this {}", (u8)value);
  210. ASSERT_NOT_REACHED();
  211. break;
  212. }
  213. }
  214. void DHCPv4Client::dhcp_discover(const InterfaceDescriptor& iface, IPv4Address previous)
  215. {
  216. auto transaction_id = get_random<u32>();
  217. if constexpr (DHCPV4CLIENT_DEBUG) {
  218. dbgln("Trying to lease an IP for {} with ID {}", iface.m_ifname, transaction_id);
  219. if (!previous.is_zero())
  220. dbgln("going to request the server to hand us {}", previous.to_string());
  221. }
  222. DHCPv4PacketBuilder builder;
  223. DHCPv4Packet& packet = builder.peek();
  224. packet.set_op(DHCPv4Op::BootRequest);
  225. packet.set_htype(1); // 10mb ethernet
  226. packet.set_hlen(sizeof(MACAddress));
  227. packet.set_xid(transaction_id);
  228. packet.set_flags(DHCPv4Flags::Broadcast);
  229. packet.ciaddr() = previous;
  230. packet.set_chaddr(iface.m_mac_address);
  231. packet.set_secs(65535); // we lie
  232. // set packet options
  233. builder.set_message_type(DHCPMessageType::DHCPDiscover);
  234. auto& dhcp_packet = builder.build();
  235. // broadcast the discover request
  236. send(iface, dhcp_packet, this);
  237. m_ongoing_transactions.set(transaction_id, make<DHCPv4Transaction>(iface));
  238. }
  239. void DHCPv4Client::dhcp_request(DHCPv4Transaction& transaction, const DHCPv4Packet& offer)
  240. {
  241. auto& iface = transaction.interface;
  242. dbgln("Leasing the IP {} for adapter {}", offer.yiaddr().to_string(), iface.m_ifname);
  243. DHCPv4PacketBuilder builder;
  244. DHCPv4Packet& packet = builder.peek();
  245. packet.set_op(DHCPv4Op::BootRequest);
  246. packet.ciaddr() = offer.yiaddr();
  247. packet.set_htype(1); // 10mb ethernet
  248. packet.set_hlen(sizeof(MACAddress));
  249. packet.set_xid(offer.xid());
  250. packet.set_flags(DHCPv4Flags::Broadcast);
  251. packet.set_chaddr(iface.m_mac_address);
  252. packet.set_secs(65535); // we lie
  253. // set packet options
  254. builder.set_message_type(DHCPMessageType::DHCPRequest);
  255. builder.add_option(DHCPOption::ServerIdentifier, sizeof(IPv4Address), &offer.siaddr());
  256. builder.add_option(DHCPOption::RequestedIPAddress, sizeof(IPv4Address), &offer.yiaddr());
  257. auto& dhcp_packet = builder.build();
  258. // broadcast the "request" request
  259. send(iface, dhcp_packet, this);
  260. transaction.accepted_offer = true;
  261. }