123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550 |
- /*
- * DhcpClient.cpp
- *
- * Created on: Mar 25, 2023
- * Author: ZKSWE Develop Team
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <errno.h>
- #include <string.h>
- #include <time.h>
- #include <poll.h>
- #include <sys/time.h>
- #include <sys/socket.h>
- #include <sys/types.h>
- #include <sys/ioctl.h>
- #include <netinet/in.h>
- #include <netinet/ip.h>
- #include <arpa/inet.h>
- #include <linux/udp.h>
- #include <linux/if_packet.h>
- #include <linux/if_ether.h>
- #include "DhcpClient.h"
- #include "utils/Log.h"
- namespace net {
- #define PORT_BOOTP_SERVER 67
- #define PORT_BOOTP_CLIENT 68
- #define OP_BOOTREQUEST 1
- #define OP_BOOTREPLY 2
- #define FLAGS_BROADCAST 0x8000
- #define HTYPE_ETHER 1
- #define DHCP_MSG_FIXED_SIZE 236
- /* first four bytes of options are a cookie to indicate that
- ** the payload are DHCP options as opposed to some other BOOTP
- ** extension.
- */
- #define OPT_COOKIE1 0x63
- #define OPT_COOKIE2 0x82
- #define OPT_COOKIE3 0x53
- #define OPT_COOKIE4 0x63
- /* BOOTP/DHCP options - see RFC 2132 */
- #define OPT_PAD 0
- #define OPT_SUBNET_MASK 1 /* 4 <ipaddr> */
- #define OPT_TIME_OFFSET 2 /* 4 <seconds> */
- #define OPT_GATEWAY 3 /* 4*n <ipaddr> * n */
- #define OPT_DNS 6 /* 4*n <ipaddr> * n */
- #define OPT_HOST_NAME 12
- #define OPT_DOMAIN_NAME 15 /* n <domainnamestring> */
- #define OPT_BROADCAST_ADDR 28 /* 4 <ipaddr> */
- #define OPT_REQUESTED_IP 50 /* 4 <ipaddr> */
- #define OPT_LEASE_TIME 51 /* 4 <seconds> */
- #define OPT_MESSAGE_TYPE 53 /* 1 <msgtype> */
- #define OPT_SERVER_ID 54 /* 4 <ipaddr> */
- #define OPT_PARAMETER_LIST 55 /* n <optcode> * n */
- #define OPT_MESSAGE 56 /* n <errorstring> */
- #define OPT_CLASS_ID 60 /* n <opaque> */
- #define OPT_CLIENT_ID 61 /* n <opaque> */
- #define OPT_END 255
- /* DHCP message types */
- #define DHCPDISCOVER 1
- #define DHCPOFFER 2
- #define DHCPREQUEST 3
- #define DHCPDECLINE 4
- #define DHCPACK 5
- #define DHCPNAK 6
- #define DHCPRELEASE 7
- #define DHCPINFORM 8
- #ifndef TEMP_FAILURE_RETRY
- #define TEMP_FAILURE_RETRY(exp) ({ \
- long int _rc; \
- do { \
- _rc = (exp); \
- } while (_rc == -1 && errno == EINTR); \
- _rc; })
- #endif
- typedef struct {
- uint8_t op; /* BOOTREQUEST / BOOTREPLY */
- uint8_t htype; /* hw addr type */
- uint8_t hlen; /* hw addr len */
- uint8_t hops; /* client set to 0 */
- uint32_t xid; /* transaction id */
- uint16_t secs; /* seconds since start of acq */
- uint16_t flags;
- uint32_t ciaddr; /* client IP addr */
- uint32_t yiaddr; /* your (client) IP addr */
- uint32_t siaddr; /* ip addr of next server */
- /* (DHCPOFFER and DHCPACK) */
- uint32_t giaddr; /* relay agent IP addr */
- uint8_t chaddr[16]; /* client hw addr */
- char sname[64]; /* asciiz server hostname */
- char file[128]; /* asciiz boot file name */
- uint8_t options[312]; /* optional parameters */
- } dhcp_msg;
- typedef struct {
- uint32_t type;
- uint32_t ipaddr;
- uint32_t gateway;
- uint32_t prefixLength;
- uint32_t dns1;
- uint32_t dns2;
- uint32_t saddr;
- uint32_t lease;
- } dhcp_info;
- static bool _s_verbose = true;
- static uint32_t get_sec() {
- struct timespec ts;
- clock_gettime(CLOCK_MONOTONIC, &ts);
- return ts.tv_sec;
- }
- static int mask_to_prelen(in_addr_t mask) {
- int len = 0;
- uint32_t m = (uint32_t) ntohl(mask);
- while (m & 0x80000000) {
- len++;
- m = m << 1;
- }
- return len;
- }
- static void macaddr_to_hwaddr(const char *macaddr, uint8_t hwaddr[6]) {
- sscanf(macaddr, "%02x:%02x:%02x:%02x:%02x:%02x",
- hwaddr, hwaddr + 1, hwaddr + 2, hwaddr + 3, hwaddr + 4, hwaddr + 5);
- }
- static uint8_t *init_dhcp_msg(dhcp_msg *msg, int type, uint8_t *hwaddr, uint32_t xid) {
- uint8_t *x;
- memset(msg, 0, sizeof(dhcp_msg));
- msg->op = OP_BOOTREQUEST;
- msg->htype = HTYPE_ETHER;
- msg->hlen = 6;
- msg->hops = 0;
- msg->xid = xid;
- memcpy(msg->chaddr, hwaddr, 6);
- x = msg->options;
- *x++ = OPT_COOKIE1;
- *x++ = OPT_COOKIE2;
- *x++ = OPT_COOKIE3;
- *x++ = OPT_COOKIE4;
- *x++ = OPT_MESSAGE_TYPE;
- *x++ = 1;
- *x++ = type;
- return x;
- }
- static int init_dhcp_renew_msg(dhcp_msg *msg, uint8_t *hwaddr, uint32_t xid,
- uint32_t ipaddr, uint32_t saddr) {
- uint8_t *x;
- x = init_dhcp_msg(msg, DHCPREQUEST, hwaddr, xid);
- msg->ciaddr = ipaddr;
- *x++ = OPT_CLIENT_ID;
- *x++ = 7;
- *x++ = HTYPE_ETHER;
- memcpy(x, hwaddr, 6);
- x += 6;
- *x++ = OPT_PARAMETER_LIST;
- *x++ = 4;
- *x++ = OPT_SUBNET_MASK;
- *x++ = OPT_GATEWAY;
- *x++ = OPT_DNS;
- *x++ = OPT_BROADCAST_ADDR;
- *x++ = OPT_REQUESTED_IP;
- *x++ = 4;
- memcpy(x, &ipaddr, 4);
- x += 4;
- *x++ = OPT_SERVER_ID;
- *x++ = 4;
- memcpy(x, &saddr, 4);
- x += 4;
- *x++ = OPT_END;
- return DHCP_MSG_FIXED_SIZE + (x - msg->options);
- }
- static const char *dhcp_type_to_name(uint32_t type) {
- switch (type) {
- case DHCPDISCOVER: return "discover";
- case DHCPOFFER: return "offer";
- case DHCPREQUEST: return "request";
- case DHCPDECLINE: return "decline";
- case DHCPACK: return "ack";
- case DHCPNAK: return "nak";
- case DHCPRELEASE: return "release";
- case DHCPINFORM: return "inform";
- default: return "???";
- }
- }
- static void dump_dhcp_info(dhcp_info *info) {
- char addr[20], gway[20], dns[20];
- LOGD("[dhcp] %s (%d) ---\n", dhcp_type_to_name(info->type), info->type);
- inet_ntop(AF_INET, &info->ipaddr, addr, sizeof(addr));
- inet_ntop(AF_INET, &info->gateway, gway, sizeof(gway));
- LOGD("[dhcp] ip %s gw %s prefixLength %d\n", addr, gway, info->prefixLength);
- if (info->dns1) LOGD("[dhcp] dns1: %s\n", inet_ntop(AF_INET, &info->dns1, dns, sizeof(dns)));
- if (info->dns2) LOGD("[dhcp] dns2: %s\n", inet_ntop(AF_INET, &info->dns2, dns, sizeof(dns)));
- LOGD("[dhcp] server %s, lease %d seconds\n", inet_ntop(AF_INET, &info->saddr, addr, sizeof(addr)), info->lease);
- }
- static int decode_dhcp_msg(dhcp_msg *msg, int len, dhcp_info *info) {
- uint8_t *x;
- uint32_t opt;
- int optlen;
- memset(info, 0, sizeof(dhcp_info));
- if (len < (DHCP_MSG_FIXED_SIZE + 4)) return -1;
- len -= (DHCP_MSG_FIXED_SIZE + 4);
- if (msg->options[0] != OPT_COOKIE1) return -1;
- if (msg->options[1] != OPT_COOKIE2) return -1;
- if (msg->options[2] != OPT_COOKIE3) return -1;
- if (msg->options[3] != OPT_COOKIE4) return -1;
- x = msg->options + 4;
- while (len > 2) {
- opt = *x++;
- if (opt == OPT_PAD) {
- len--;
- continue;
- }
- if (opt == OPT_END) {
- break;
- }
- optlen = *x++;
- len -= 2;
- if (optlen > len) {
- break;
- }
- switch(opt) {
- case OPT_SUBNET_MASK:
- if (optlen >= 4) {
- in_addr_t mask;
- memcpy(&mask, x, 4);
- info->prefixLength = mask_to_prelen(mask);
- }
- break;
- case OPT_GATEWAY:
- if (optlen >= 4) memcpy(&info->gateway, x, 4);
- break;
- case OPT_DNS:
- if (optlen >= 4) memcpy(&info->dns1, x + 0, 4);
- if (optlen >= 8) memcpy(&info->dns2, x + 4, 4);
- break;
- case OPT_LEASE_TIME:
- if (optlen >= 4) {
- memcpy(&info->lease, x, 4);
- info->lease = ntohl(info->lease);
- }
- break;
- case OPT_SERVER_ID:
- if (optlen >= 4) memcpy(&info->saddr, x, 4);
- break;
- case OPT_MESSAGE_TYPE:
- info->type = *x;
- break;
- default:
- break;
- }
- x += optlen;
- len -= optlen;
- }
- info->ipaddr = msg->yiaddr;
- return 0;
- }
- static bool is_valid_reply(dhcp_msg *msg, dhcp_msg *reply, int sz) {
- if (sz < DHCP_MSG_FIXED_SIZE) {
- if (_s_verbose) {
- LOGD("netcfg: Wrong size %d != %d\n", sz, DHCP_MSG_FIXED_SIZE);
- }
- return false;
- }
- if (reply->op != OP_BOOTREPLY) {
- if (_s_verbose) {
- LOGD("netcfg: Wrong Op %d != %d\n", reply->op, OP_BOOTREPLY);
- }
- return false;
- }
- if (reply->xid != msg->xid) {
- if (_s_verbose) {
- LOGD("netcfg: Wrong Xid 0x%x != 0x%x\n", ntohl(reply->xid), ntohl(msg->xid));
- }
- return false;
- }
- if (reply->htype != msg->htype) {
- if (_s_verbose) {
- LOGD("netcfg: Wrong Htype %d != %d\n", reply->htype, msg->htype);
- }
- return false;
- }
- if (reply->hlen != msg->hlen) {
- if (_s_verbose) {
- LOGD("netcfg: Wrong Hlen %d != %d\n", reply->hlen, msg->hlen);
- }
- return false;
- }
- if (memcmp(msg->chaddr, reply->chaddr, msg->hlen)) {
- if (_s_verbose) {
- LOGD("netcfg: Wrong chaddr %x != %x\n", *(reply->chaddr),*(msg->chaddr));
- }
- return false;
- }
- return true;
- }
- DhcpClient::DhcpClient() : lease_cb_(NULL), record_time_(0), lease_(0), record_lease_(0) {
- wakeup_[0] = wakeup_[1] = -1;
- }
- DhcpClient::~DhcpClient() {
- stop();
- }
- void DhcpClient::set_lease_cb(dhcp_lease_cb cb) {
- lease_cb_ = cb;
- }
- bool DhcpClient::start(const char *ip, const char *macaddr, const char *gateway) {
- if (isRunning()) {
- return true;
- }
- if (pipe(wakeup_) < 0) {
- LOGE("[dhcp] create pipe fail\n");
- return false;
- }
- ip_ = ip;
- macaddr_ = macaddr;
- gateway_ = gateway;
- record_time_ = get_sec();
- // lease_ = 60 * 60; // 1 hour
- lease_ = 100; // 100分钟
- record_lease_ = 0;
- LOGD("开始请求dhcp,ip == %s", ip);
- return run("renew");
- }
- void DhcpClient::stop() {
- LOGD("[dhcp] stop +++\n");
- LOGD("正在断开dhcp,ip == %s", ip_.c_str());
- if (wakeup_[1] >= 0) {
- TEMP_FAILURE_RETRY(write(wakeup_[1], "W", 1));
- }
- requestExitAndWait();
- if (wakeup_[0] >= 0) {
- close(wakeup_[0]);
- wakeup_[0] = -1;
- }
- if (wakeup_[1] >= 0) {
- close(wakeup_[1]);
- wakeup_[1] = -1;
- }
- LOGD("[dhcp] stop ---\n");
- }
- bool DhcpClient::threadLoop() {
- uint32_t lease1_2 = lease_ / 2; // 1/2
- uint32_t lease7_8 = lease_ - lease_ / 8; // 7/8
- uint32_t past_time = get_sec() - record_time_;
- bool need_renew = false;
- // LOGD("lease_ %d\n", lease_);
- // LOGD("lease1_2 %d\n", lease1_2);
- // LOGD("lease7_8 %d\n", lease7_8);
- // LOGD("past_time %d\n", past_time);
- if (past_time > lease_) {
- LOGE("[dhcp] lease time out\n");
- if (lease_cb_) {
- lease_cb_(E_DHCP_LEASE_TYPE_TIMEOUT, this);
- }
- return false;
- } else if (past_time > lease7_8) {
- if (record_lease_ < lease7_8) {
- record_lease_ = lease7_8;
- need_renew = true;
- }
- } else if (past_time > lease1_2) {
- if (record_lease_ < lease1_2) {
- record_lease_ = lease1_2;
- need_renew = true;
- }
- }
- if (need_renew) {
- bool ret = false;
- for (int i = 0; i < 3; ++i) {
- if (renew()) {
- ret = true;
- break;
- }
- sleep(100);
- }
- if (ret) {
- record_lease_ = 0;
- record_time_ = get_sec();
- LOGD("[dhcp] lease ok\n");
- if (lease_cb_) {
- lease_cb_(E_DHCP_LEASE_TYPE_SUCCESS, this);
- }
- } else {
- LOGE("[dhcp] lease err\n");
- if (lease_cb_) {
- lease_cb_(E_DHCP_LEASE_TYPE_FAIL, this);
- }
- }
- }
- struct pollfd ufd = { wakeup_[0], POLLIN, 0 };
- poll(&ufd, 1, 3000);
- return true;
- }
- bool DhcpClient::renew() {
- int s = -1;
- sockaddr_in saddr;
- socklen_t saddr_len;
- sockaddr_in caddr;
- socklen_t caddr_len;
- struct timeval tv_out;
- uint8_t hwaddr[6];
- dhcp_msg renew_msg;
- dhcp_msg recv_msg;
- ssize_t renew_len;
- ssize_t recv_len;
- dhcp_info di;
- bool ret = false;
- s = socket(AF_INET, SOCK_DGRAM, 0);
- if (s < 0) {
- LOGE("[dhcp] create socket err\n");
- return false;
- }
- saddr_len = sizeof(struct sockaddr_in);
- memset(&saddr, 0, saddr_len);
- saddr.sin_family = AF_INET;
- saddr.sin_port = htons(PORT_BOOTP_SERVER);
- saddr.sin_addr.s_addr = inet_addr(gateway_.c_str());
- caddr_len = sizeof(struct sockaddr_in);
- memset(&caddr, 0, caddr_len);
- caddr.sin_family = AF_INET;
- caddr.sin_port = htons(PORT_BOOTP_CLIENT);
- caddr.sin_addr.s_addr = htonl(INADDR_ANY);
- if (bind(s, (sockaddr *) &caddr, caddr_len) < 0) {
- LOGE("[dhcp] bind err\n");
- goto END;
- }
- tv_out.tv_sec = 2; // 等待2秒
- tv_out.tv_usec = 0;
- if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv_out, sizeof(tv_out)) < 0) {
- LOGE("[dhcp] setsockopt err\n");
- goto END;
- }
- macaddr_to_hwaddr(macaddr_.c_str(), hwaddr);
- renew_len = init_dhcp_renew_msg(&renew_msg, hwaddr, get_sec(), inet_addr(ip_.c_str()), inet_addr(gateway_.c_str()));
- if (sendto(s, &renew_msg, renew_len, 0, (struct sockaddr *) &saddr, saddr_len) < 0) {
- LOGE("[dhcp] sendto err\n");
- goto END;
- }
- recv_len = recvfrom(s, &recv_msg, sizeof(recv_msg), 0, (struct sockaddr *) &saddr, &saddr_len);
- if (recv_len < 0) {
- LOGE("[dhcp] recvfrom err\n");
- goto END;
- }
- LOGD("[dhcp] recv len %d\n", recv_len);
- if (!is_valid_reply(&renew_msg, &recv_msg, recv_len)) {
- goto END;
- }
- if (decode_dhcp_msg(&recv_msg, recv_len, &di) == -1) {
- LOGE("[dhcp] decode msg err\n");
- goto END;
- }
- dump_dhcp_info(&di);
- if (di.type != DHCPACK) {
- goto END;
- }
- lease_ = di.lease;
- ret = true;
- END:
- close(s);
- return ret;
- }
- }
|