DhcpClient.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. /*
  2. * DhcpClient.cpp
  3. *
  4. * Created on: Mar 25, 2023
  5. * Author: ZKSWE Develop Team
  6. */
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <unistd.h>
  10. #include <errno.h>
  11. #include <string.h>
  12. #include <time.h>
  13. #include <poll.h>
  14. #include <sys/time.h>
  15. #include <sys/socket.h>
  16. #include <sys/types.h>
  17. #include <sys/ioctl.h>
  18. #include <netinet/in.h>
  19. #include <netinet/ip.h>
  20. #include <arpa/inet.h>
  21. #include <linux/udp.h>
  22. #include <linux/if_packet.h>
  23. #include <linux/if_ether.h>
  24. #include "DhcpClient.h"
  25. #include "utils/Log.h"
  26. namespace net {
  27. #define PORT_BOOTP_SERVER 67
  28. #define PORT_BOOTP_CLIENT 68
  29. #define OP_BOOTREQUEST 1
  30. #define OP_BOOTREPLY 2
  31. #define FLAGS_BROADCAST 0x8000
  32. #define HTYPE_ETHER 1
  33. #define DHCP_MSG_FIXED_SIZE 236
  34. /* first four bytes of options are a cookie to indicate that
  35. ** the payload are DHCP options as opposed to some other BOOTP
  36. ** extension.
  37. */
  38. #define OPT_COOKIE1 0x63
  39. #define OPT_COOKIE2 0x82
  40. #define OPT_COOKIE3 0x53
  41. #define OPT_COOKIE4 0x63
  42. /* BOOTP/DHCP options - see RFC 2132 */
  43. #define OPT_PAD 0
  44. #define OPT_SUBNET_MASK 1 /* 4 <ipaddr> */
  45. #define OPT_TIME_OFFSET 2 /* 4 <seconds> */
  46. #define OPT_GATEWAY 3 /* 4*n <ipaddr> * n */
  47. #define OPT_DNS 6 /* 4*n <ipaddr> * n */
  48. #define OPT_HOST_NAME 12
  49. #define OPT_DOMAIN_NAME 15 /* n <domainnamestring> */
  50. #define OPT_BROADCAST_ADDR 28 /* 4 <ipaddr> */
  51. #define OPT_REQUESTED_IP 50 /* 4 <ipaddr> */
  52. #define OPT_LEASE_TIME 51 /* 4 <seconds> */
  53. #define OPT_MESSAGE_TYPE 53 /* 1 <msgtype> */
  54. #define OPT_SERVER_ID 54 /* 4 <ipaddr> */
  55. #define OPT_PARAMETER_LIST 55 /* n <optcode> * n */
  56. #define OPT_MESSAGE 56 /* n <errorstring> */
  57. #define OPT_CLASS_ID 60 /* n <opaque> */
  58. #define OPT_CLIENT_ID 61 /* n <opaque> */
  59. #define OPT_END 255
  60. /* DHCP message types */
  61. #define DHCPDISCOVER 1
  62. #define DHCPOFFER 2
  63. #define DHCPREQUEST 3
  64. #define DHCPDECLINE 4
  65. #define DHCPACK 5
  66. #define DHCPNAK 6
  67. #define DHCPRELEASE 7
  68. #define DHCPINFORM 8
  69. #ifndef TEMP_FAILURE_RETRY
  70. #define TEMP_FAILURE_RETRY(exp) ({ \
  71. long int _rc; \
  72. do { \
  73. _rc = (exp); \
  74. } while (_rc == -1 && errno == EINTR); \
  75. _rc; })
  76. #endif
  77. typedef struct {
  78. uint8_t op; /* BOOTREQUEST / BOOTREPLY */
  79. uint8_t htype; /* hw addr type */
  80. uint8_t hlen; /* hw addr len */
  81. uint8_t hops; /* client set to 0 */
  82. uint32_t xid; /* transaction id */
  83. uint16_t secs; /* seconds since start of acq */
  84. uint16_t flags;
  85. uint32_t ciaddr; /* client IP addr */
  86. uint32_t yiaddr; /* your (client) IP addr */
  87. uint32_t siaddr; /* ip addr of next server */
  88. /* (DHCPOFFER and DHCPACK) */
  89. uint32_t giaddr; /* relay agent IP addr */
  90. uint8_t chaddr[16]; /* client hw addr */
  91. char sname[64]; /* asciiz server hostname */
  92. char file[128]; /* asciiz boot file name */
  93. uint8_t options[312]; /* optional parameters */
  94. } dhcp_msg;
  95. typedef struct {
  96. uint32_t type;
  97. uint32_t ipaddr;
  98. uint32_t gateway;
  99. uint32_t prefixLength;
  100. uint32_t dns1;
  101. uint32_t dns2;
  102. uint32_t saddr;
  103. uint32_t lease;
  104. } dhcp_info;
  105. static bool _s_verbose = true;
  106. static uint32_t get_sec() {
  107. struct timespec ts;
  108. clock_gettime(CLOCK_MONOTONIC, &ts);
  109. return ts.tv_sec;
  110. }
  111. static int mask_to_prelen(in_addr_t mask) {
  112. int len = 0;
  113. uint32_t m = (uint32_t) ntohl(mask);
  114. while (m & 0x80000000) {
  115. len++;
  116. m = m << 1;
  117. }
  118. return len;
  119. }
  120. static void macaddr_to_hwaddr(const char *macaddr, uint8_t hwaddr[6]) {
  121. sscanf(macaddr, "%02x:%02x:%02x:%02x:%02x:%02x",
  122. hwaddr, hwaddr + 1, hwaddr + 2, hwaddr + 3, hwaddr + 4, hwaddr + 5);
  123. }
  124. static uint8_t *init_dhcp_msg(dhcp_msg *msg, int type, uint8_t *hwaddr, uint32_t xid) {
  125. uint8_t *x;
  126. memset(msg, 0, sizeof(dhcp_msg));
  127. msg->op = OP_BOOTREQUEST;
  128. msg->htype = HTYPE_ETHER;
  129. msg->hlen = 6;
  130. msg->hops = 0;
  131. msg->xid = xid;
  132. memcpy(msg->chaddr, hwaddr, 6);
  133. x = msg->options;
  134. *x++ = OPT_COOKIE1;
  135. *x++ = OPT_COOKIE2;
  136. *x++ = OPT_COOKIE3;
  137. *x++ = OPT_COOKIE4;
  138. *x++ = OPT_MESSAGE_TYPE;
  139. *x++ = 1;
  140. *x++ = type;
  141. return x;
  142. }
  143. static int init_dhcp_renew_msg(dhcp_msg *msg, uint8_t *hwaddr, uint32_t xid,
  144. uint32_t ipaddr, uint32_t saddr) {
  145. uint8_t *x;
  146. x = init_dhcp_msg(msg, DHCPREQUEST, hwaddr, xid);
  147. msg->ciaddr = ipaddr;
  148. *x++ = OPT_CLIENT_ID;
  149. *x++ = 7;
  150. *x++ = HTYPE_ETHER;
  151. memcpy(x, hwaddr, 6);
  152. x += 6;
  153. *x++ = OPT_PARAMETER_LIST;
  154. *x++ = 4;
  155. *x++ = OPT_SUBNET_MASK;
  156. *x++ = OPT_GATEWAY;
  157. *x++ = OPT_DNS;
  158. *x++ = OPT_BROADCAST_ADDR;
  159. *x++ = OPT_REQUESTED_IP;
  160. *x++ = 4;
  161. memcpy(x, &ipaddr, 4);
  162. x += 4;
  163. *x++ = OPT_SERVER_ID;
  164. *x++ = 4;
  165. memcpy(x, &saddr, 4);
  166. x += 4;
  167. *x++ = OPT_END;
  168. return DHCP_MSG_FIXED_SIZE + (x - msg->options);
  169. }
  170. static const char *dhcp_type_to_name(uint32_t type) {
  171. switch (type) {
  172. case DHCPDISCOVER: return "discover";
  173. case DHCPOFFER: return "offer";
  174. case DHCPREQUEST: return "request";
  175. case DHCPDECLINE: return "decline";
  176. case DHCPACK: return "ack";
  177. case DHCPNAK: return "nak";
  178. case DHCPRELEASE: return "release";
  179. case DHCPINFORM: return "inform";
  180. default: return "???";
  181. }
  182. }
  183. static void dump_dhcp_info(dhcp_info *info) {
  184. char addr[20], gway[20], dns[20];
  185. LOGD("[dhcp] %s (%d) ---\n", dhcp_type_to_name(info->type), info->type);
  186. inet_ntop(AF_INET, &info->ipaddr, addr, sizeof(addr));
  187. inet_ntop(AF_INET, &info->gateway, gway, sizeof(gway));
  188. LOGD("[dhcp] ip %s gw %s prefixLength %d\n", addr, gway, info->prefixLength);
  189. if (info->dns1) LOGD("[dhcp] dns1: %s\n", inet_ntop(AF_INET, &info->dns1, dns, sizeof(dns)));
  190. if (info->dns2) LOGD("[dhcp] dns2: %s\n", inet_ntop(AF_INET, &info->dns2, dns, sizeof(dns)));
  191. LOGD("[dhcp] server %s, lease %d seconds\n", inet_ntop(AF_INET, &info->saddr, addr, sizeof(addr)), info->lease);
  192. }
  193. static int decode_dhcp_msg(dhcp_msg *msg, int len, dhcp_info *info) {
  194. uint8_t *x;
  195. uint32_t opt;
  196. int optlen;
  197. memset(info, 0, sizeof(dhcp_info));
  198. if (len < (DHCP_MSG_FIXED_SIZE + 4)) return -1;
  199. len -= (DHCP_MSG_FIXED_SIZE + 4);
  200. if (msg->options[0] != OPT_COOKIE1) return -1;
  201. if (msg->options[1] != OPT_COOKIE2) return -1;
  202. if (msg->options[2] != OPT_COOKIE3) return -1;
  203. if (msg->options[3] != OPT_COOKIE4) return -1;
  204. x = msg->options + 4;
  205. while (len > 2) {
  206. opt = *x++;
  207. if (opt == OPT_PAD) {
  208. len--;
  209. continue;
  210. }
  211. if (opt == OPT_END) {
  212. break;
  213. }
  214. optlen = *x++;
  215. len -= 2;
  216. if (optlen > len) {
  217. break;
  218. }
  219. switch(opt) {
  220. case OPT_SUBNET_MASK:
  221. if (optlen >= 4) {
  222. in_addr_t mask;
  223. memcpy(&mask, x, 4);
  224. info->prefixLength = mask_to_prelen(mask);
  225. }
  226. break;
  227. case OPT_GATEWAY:
  228. if (optlen >= 4) memcpy(&info->gateway, x, 4);
  229. break;
  230. case OPT_DNS:
  231. if (optlen >= 4) memcpy(&info->dns1, x + 0, 4);
  232. if (optlen >= 8) memcpy(&info->dns2, x + 4, 4);
  233. break;
  234. case OPT_LEASE_TIME:
  235. if (optlen >= 4) {
  236. memcpy(&info->lease, x, 4);
  237. info->lease = ntohl(info->lease);
  238. }
  239. break;
  240. case OPT_SERVER_ID:
  241. if (optlen >= 4) memcpy(&info->saddr, x, 4);
  242. break;
  243. case OPT_MESSAGE_TYPE:
  244. info->type = *x;
  245. break;
  246. default:
  247. break;
  248. }
  249. x += optlen;
  250. len -= optlen;
  251. }
  252. info->ipaddr = msg->yiaddr;
  253. return 0;
  254. }
  255. static bool is_valid_reply(dhcp_msg *msg, dhcp_msg *reply, int sz) {
  256. if (sz < DHCP_MSG_FIXED_SIZE) {
  257. if (_s_verbose) {
  258. LOGD("netcfg: Wrong size %d != %d\n", sz, DHCP_MSG_FIXED_SIZE);
  259. }
  260. return false;
  261. }
  262. if (reply->op != OP_BOOTREPLY) {
  263. if (_s_verbose) {
  264. LOGD("netcfg: Wrong Op %d != %d\n", reply->op, OP_BOOTREPLY);
  265. }
  266. return false;
  267. }
  268. if (reply->xid != msg->xid) {
  269. if (_s_verbose) {
  270. LOGD("netcfg: Wrong Xid 0x%x != 0x%x\n", ntohl(reply->xid), ntohl(msg->xid));
  271. }
  272. return false;
  273. }
  274. if (reply->htype != msg->htype) {
  275. if (_s_verbose) {
  276. LOGD("netcfg: Wrong Htype %d != %d\n", reply->htype, msg->htype);
  277. }
  278. return false;
  279. }
  280. if (reply->hlen != msg->hlen) {
  281. if (_s_verbose) {
  282. LOGD("netcfg: Wrong Hlen %d != %d\n", reply->hlen, msg->hlen);
  283. }
  284. return false;
  285. }
  286. if (memcmp(msg->chaddr, reply->chaddr, msg->hlen)) {
  287. if (_s_verbose) {
  288. LOGD("netcfg: Wrong chaddr %x != %x\n", *(reply->chaddr),*(msg->chaddr));
  289. }
  290. return false;
  291. }
  292. return true;
  293. }
  294. DhcpClient::DhcpClient() : lease_cb_(NULL), record_time_(0), lease_(0), record_lease_(0) {
  295. wakeup_[0] = wakeup_[1] = -1;
  296. }
  297. DhcpClient::~DhcpClient() {
  298. stop();
  299. }
  300. void DhcpClient::set_lease_cb(dhcp_lease_cb cb) {
  301. lease_cb_ = cb;
  302. }
  303. bool DhcpClient::start(const char *ip, const char *macaddr, const char *gateway) {
  304. if (isRunning()) {
  305. return true;
  306. }
  307. if (pipe(wakeup_) < 0) {
  308. LOGE("[dhcp] create pipe fail\n");
  309. return false;
  310. }
  311. ip_ = ip;
  312. macaddr_ = macaddr;
  313. gateway_ = gateway;
  314. record_time_ = get_sec();
  315. // lease_ = 60 * 60; // 1 hour
  316. lease_ = 100; // 100分钟
  317. record_lease_ = 0;
  318. LOGD("开始请求dhcp,ip == %s", ip);
  319. return run("renew");
  320. }
  321. void DhcpClient::stop() {
  322. LOGD("[dhcp] stop +++\n");
  323. LOGD("正在断开dhcp,ip == %s", ip_.c_str());
  324. if (wakeup_[1] >= 0) {
  325. TEMP_FAILURE_RETRY(write(wakeup_[1], "W", 1));
  326. }
  327. requestExitAndWait();
  328. if (wakeup_[0] >= 0) {
  329. close(wakeup_[0]);
  330. wakeup_[0] = -1;
  331. }
  332. if (wakeup_[1] >= 0) {
  333. close(wakeup_[1]);
  334. wakeup_[1] = -1;
  335. }
  336. LOGD("[dhcp] stop ---\n");
  337. }
  338. bool DhcpClient::threadLoop() {
  339. uint32_t lease1_2 = lease_ / 2; // 1/2
  340. uint32_t lease7_8 = lease_ - lease_ / 8; // 7/8
  341. uint32_t past_time = get_sec() - record_time_;
  342. bool need_renew = false;
  343. // LOGD("lease_ %d\n", lease_);
  344. // LOGD("lease1_2 %d\n", lease1_2);
  345. // LOGD("lease7_8 %d\n", lease7_8);
  346. // LOGD("past_time %d\n", past_time);
  347. if (past_time > lease_) {
  348. LOGE("[dhcp] lease time out\n");
  349. if (lease_cb_) {
  350. lease_cb_(E_DHCP_LEASE_TYPE_TIMEOUT, this);
  351. }
  352. return false;
  353. } else if (past_time > lease7_8) {
  354. if (record_lease_ < lease7_8) {
  355. record_lease_ = lease7_8;
  356. need_renew = true;
  357. }
  358. } else if (past_time > lease1_2) {
  359. if (record_lease_ < lease1_2) {
  360. record_lease_ = lease1_2;
  361. need_renew = true;
  362. }
  363. }
  364. if (need_renew) {
  365. bool ret = false;
  366. for (int i = 0; i < 3; ++i) {
  367. if (renew()) {
  368. ret = true;
  369. break;
  370. }
  371. sleep(100);
  372. }
  373. if (ret) {
  374. record_lease_ = 0;
  375. record_time_ = get_sec();
  376. LOGD("[dhcp] lease ok\n");
  377. if (lease_cb_) {
  378. lease_cb_(E_DHCP_LEASE_TYPE_SUCCESS, this);
  379. }
  380. } else {
  381. LOGE("[dhcp] lease err\n");
  382. if (lease_cb_) {
  383. lease_cb_(E_DHCP_LEASE_TYPE_FAIL, this);
  384. }
  385. }
  386. }
  387. struct pollfd ufd = { wakeup_[0], POLLIN, 0 };
  388. poll(&ufd, 1, 3000);
  389. return true;
  390. }
  391. bool DhcpClient::renew() {
  392. int s = -1;
  393. sockaddr_in saddr;
  394. socklen_t saddr_len;
  395. sockaddr_in caddr;
  396. socklen_t caddr_len;
  397. struct timeval tv_out;
  398. uint8_t hwaddr[6];
  399. dhcp_msg renew_msg;
  400. dhcp_msg recv_msg;
  401. ssize_t renew_len;
  402. ssize_t recv_len;
  403. dhcp_info di;
  404. bool ret = false;
  405. s = socket(AF_INET, SOCK_DGRAM, 0);
  406. if (s < 0) {
  407. LOGE("[dhcp] create socket err\n");
  408. return false;
  409. }
  410. saddr_len = sizeof(struct sockaddr_in);
  411. memset(&saddr, 0, saddr_len);
  412. saddr.sin_family = AF_INET;
  413. saddr.sin_port = htons(PORT_BOOTP_SERVER);
  414. saddr.sin_addr.s_addr = inet_addr(gateway_.c_str());
  415. caddr_len = sizeof(struct sockaddr_in);
  416. memset(&caddr, 0, caddr_len);
  417. caddr.sin_family = AF_INET;
  418. caddr.sin_port = htons(PORT_BOOTP_CLIENT);
  419. caddr.sin_addr.s_addr = htonl(INADDR_ANY);
  420. if (bind(s, (sockaddr *) &caddr, caddr_len) < 0) {
  421. LOGE("[dhcp] bind err\n");
  422. goto END;
  423. }
  424. tv_out.tv_sec = 2; // 等待2秒
  425. tv_out.tv_usec = 0;
  426. if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv_out, sizeof(tv_out)) < 0) {
  427. LOGE("[dhcp] setsockopt err\n");
  428. goto END;
  429. }
  430. macaddr_to_hwaddr(macaddr_.c_str(), hwaddr);
  431. renew_len = init_dhcp_renew_msg(&renew_msg, hwaddr, get_sec(), inet_addr(ip_.c_str()), inet_addr(gateway_.c_str()));
  432. if (sendto(s, &renew_msg, renew_len, 0, (struct sockaddr *) &saddr, saddr_len) < 0) {
  433. LOGE("[dhcp] sendto err\n");
  434. goto END;
  435. }
  436. recv_len = recvfrom(s, &recv_msg, sizeof(recv_msg), 0, (struct sockaddr *) &saddr, &saddr_len);
  437. if (recv_len < 0) {
  438. LOGE("[dhcp] recvfrom err\n");
  439. goto END;
  440. }
  441. LOGD("[dhcp] recv len %d\n", recv_len);
  442. if (!is_valid_reply(&renew_msg, &recv_msg, recv_len)) {
  443. goto END;
  444. }
  445. if (decode_dhcp_msg(&recv_msg, recv_len, &di) == -1) {
  446. LOGE("[dhcp] decode msg err\n");
  447. goto END;
  448. }
  449. dump_dhcp_info(&di);
  450. if (di.type != DHCPACK) {
  451. goto END;
  452. }
  453. lease_ = di.lease;
  454. ret = true;
  455. END:
  456. close(s);
  457. return ret;
  458. }
  459. }