/* * Copyright (C) 2021-2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "dhcp_s_server.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "address_utils.h" #include "common_util.h" #include "dhcp_address_pool.h" #include "dhcp_binding.h" #include "dhcp_config.h" #include "dhcp_server_ipv4.h" #include "dhcp_logger.h" #include "dhcp_option.h" #include "dhcp_common_utils.h" DEFINE_DHCPLOG_DHCP_LABEL("DhcpServer"); #ifndef DHCP_SEL_WAIT_TIMEOUTS #define DHCP_SEL_WAIT_TIMEOUTS 1000 #endif #define OPT_MESSAGE_TYPE_LEGTH 1 #define OPT_HEADER_LENGTH 2 #define OPT_TIME_LENGTH 4 #define OPT_TYPE_FIELD_LENGTH 1 #define OPT_MAC_ADDR_LENGTH 6 #define MAGIC_COOKIE_LENGTH 4 #define OPT_BROADCAST_FLAG_ENABLE 0 #define OFFER_MIN_INTERVAL_TIME 5 #define PENDING_DEFAULT_TIMEOUT 1200 #define PENDING_DEFAULT_INTERVAL 1 #define PENDING_INTERVAL_CHECKING_ENABLE 1 #define DHCP_MAGIC_COOKIE 0x63825363 #define RECV_BUFFER_SIZE 2048 #define ALLOW_NOBINDING_REQUEST 1 #define REUSE_ADDRESS_ENABLE 1 #define WAIT_STOPED_TIME 5 #define DHCP_SERVER_SLEEP_TIMEOUTS 600000 // 600ms #define VNEDOR_OPEN_HARMONY "OPEN_HARMONY" const uint8_t MAGIC_COOKIE_DATA[MAGIC_COOKIE_LENGTH] = {0x63, 0x82, 0x53, 0x63}; // Vendor Information "Magic Cookie" enum AssignedNumbers { ETHERNET = 1, // Ethernet (10Mb) EXPERIMENTAL_ETHERNET, // Experimental Ethernet (3Mb) AMATEUR_RADIO_AX_25, // Amateur Radio AX.25 PROTEON_PRONET_TOKEN_RING, // Proteon ProNET Token Ring CHAOS, IEEE802_NETWORKS, ARCNET, HYPERCHANNEL, LANSTAR }; struct ServerContext { int broadCastFlagEnable; DhcpAddressPool addressPool; DhcpServerCallback callback; DeviceConnectFun deviceConnectFun; DhcpConfig config; int serverFd; int looperState; int initialized; }; enum LooperState { LS_IDLE = 0, LS_STARING, LS_RUNNING, LS_RELOADNG, LS_STOPING, LS_STOPED }; typedef struct sockaddr_in sockaddr_in; int FillReply(PDhcpServerContext ctx, PDhcpMsgInfo received, PDhcpMsgInfo reply); static int OnReceivedDiscover(PDhcpServerContext ctx, PDhcpMsgInfo received, PDhcpMsgInfo reply); static int OnReceivedRequest(PDhcpServerContext ctx, PDhcpMsgInfo received, PDhcpMsgInfo reply); static int OnReceivedDecline(PDhcpServerContext ctx, PDhcpMsgInfo received, PDhcpMsgInfo reply); static int OnReceivedRelease(PDhcpServerContext ctx, PDhcpMsgInfo received, PDhcpMsgInfo reply); static int OnReceivedInform(PDhcpServerContext ctx, PDhcpMsgInfo received, PDhcpMsgInfo reply); static int SendDhcpOffer(PDhcpServerContext ctx, PDhcpMsgInfo reply); static int SendDhcpAck(PDhcpServerContext ctx, PDhcpMsgInfo reply); static int SendDhcpNak(PDhcpServerContext ctx, PDhcpMsgInfo reply); static int ParseMessageOptions(PDhcpMsgInfo msg); static int TransmitOfferOrAckPacket(PDhcpServerContext ctx, PDhcpMsgInfo reply); static int ParseReplyOptions(PDhcpMsgInfo reply); struct sockaddr_in *BroadcastAddrIn(void); using namespace OHOS::DHCP; static struct ServerContext *GetServerInstance(const DhcpServerContext *ctx) { if (!ctx || !ctx->instance) { return nullptr; } return (struct ServerContext *)ctx->instance; } int HasFixSocket(int fd) { int flags; if ((flags = fcntl(fd, F_GETFL)) == -1 || fcntl(fd, F_SETFL, static_cast(flags) | O_NONBLOCK) == -1) { return DHCP_FALSE; } return DHCP_TRUE; } typedef struct ifreq ifreq; typedef struct sockaddr sockaddr; int BindNetInterface(int fd, const char *ifname) { DHCP_LOGI("start %{public}s %{public}d ifname = %{public}s ", __func__, __LINE__, ifname); if (!fd || !ifname) { return RET_FAILED; } ifreq iface; if (memset_s(&iface, sizeof(iface), 0, sizeof(iface)) != EOK) { return RET_FAILED; } ssize_t ifnameSize = strlen(ifname); if (strncpy_s(iface.ifr_ifrn.ifrn_name, sizeof(iface.ifr_ifrn.ifrn_name), ifname, ifnameSize) != EOK) { DHCP_LOGE("start %{public}s %{public}d copy failed ", __func__, __LINE__); return RET_FAILED; }; if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (char *)&iface, sizeof(iface)) == -1) { DHCP_LOGE("failed to bind network device interface[%s].", ifname); return RET_FAILED; } DHCP_LOGI("start %{public}s %{public}d success ", __func__, __LINE__); return RET_SUCCESS; } int InitServer(const char *ifname) { DHCP_LOGI("start %{public}s %{public}d ifname = %{public}s ", __func__, __LINE__, ifname); sockaddr_in srvAddrIn = {0}; int optval = 1; int optrval = 0; srvAddrIn.sin_family = AF_INET; srvAddrIn.sin_port = htons(DHCP_SERVER_PORT); srvAddrIn.sin_addr.s_addr = INADDR_ANY; int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); if (fd == -1) { DHCP_LOGE("failed to create server socket!"); return -1; } if (!HasFixSocket(fd)) { DHCP_LOGD("failed to fcntl O_NONBLOCK flag!"); } if (BindNetInterface(fd, ifname) != RET_SUCCESS) { close(fd); return -1; } socklen_t optlen = sizeof(optrval); if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *)&optrval, &optlen) == -1) { DHCP_LOGI("failed to receive buffer size."); } else { DHCP_LOGI("receive buffer size is %d", optrval); } if (REUSE_ADDRESS_ENABLE && setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(optval)) == -1) { DHCP_LOGW("failed to setsockopt 'SO_REUSEADDR' for server socket!"); } if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) == -1) { DHCP_LOGE("failed to setsockopt 'SO_BROADCAST' for server socket!"); close(fd); return -1; } if (int ret = bind(fd, (sockaddr *)&srvAddrIn, sizeof(sockaddr)) == -1) { DHCP_LOGE("failed to bind server %{public}d!", ret); close(fd); return -1; } DHCP_LOGI("start %{public}s %{public}d SUCCESSs ", __func__, __LINE__); return fd; } struct sockaddr_in *BroadcastAddrIn(void) { static struct sockaddr_in broadcastAddrIn = {0}; if (broadcastAddrIn.sin_port == 0) { broadcastAddrIn.sin_port = htons(DHCP_CLIENT_PORT); broadcastAddrIn.sin_family = AF_INET; broadcastAddrIn.sin_addr.s_addr = INADDR_BROADCAST; } return &broadcastAddrIn; } struct sockaddr_in *SourceAddrIn(void) { static struct sockaddr_in sourceAddrIn = {0}; sourceAddrIn.sin_port = htons(DHCP_CLIENT_PORT); sourceAddrIn.sin_family = AF_INET; sourceAddrIn.sin_addr.s_addr = INADDR_ANY; return &sourceAddrIn; } struct sockaddr_in *ResetSourceAddr(void) { DHCP_LOGI("start %{public}s %{public}d", __func__, __LINE__); struct sockaddr_in *srcAddr = SourceAddrIn(); srcAddr->sin_port = htons(DHCP_CLIENT_PORT); srcAddr->sin_family = AF_INET; srcAddr->sin_addr.s_addr = INADDR_ANY; return srcAddr; } uint32_t SourceIpAddress(void) { uint32_t srcIp = SourceAddrIn()->sin_addr.s_addr; return srcIp; } struct sockaddr_in *DestinationAddrIn(void) { static struct sockaddr_in destAddrIn = {0}; if (destAddrIn.sin_port == 0) { destAddrIn.sin_port = htons(DHCP_CLIENT_PORT); destAddrIn.sin_family = AF_INET; } return &destAddrIn; } struct sockaddr_in *DestinationAddr(uint32_t ipAddress) { struct sockaddr_in *destAddr = DestinationAddrIn(); destAddr->sin_addr.s_addr = htonl(ipAddress); return destAddr; } int ReceiveDhcpMessage(int sock, PDhcpMsgInfo msgInfo) { static uint8_t recvBuffer[RECV_BUFFER_SIZE] = {0}; struct timeval tmt; fd_set recvFd; FD_ZERO(&recvFd); FD_SET(sock, &recvFd); tmt.tv_sec = 0; tmt.tv_usec = DHCP_SERVER_SLEEP_TIMEOUTS; // 600ms int ret = select(sock + 1, &recvFd, nullptr, nullptr, &tmt); if (ret < 0) { DHCP_LOGE("select error, %d", errno); return ERR_SELECT; } if (ret == 0) { return RET_SELECT_TIME_OUT; } if (!FD_ISSET(sock, &recvFd)) { DHCP_LOGE("failed to select isset."); return RET_ERROR; } socklen_t ssize = sizeof(sockaddr_in); struct sockaddr_in *srcAddrIn = ResetSourceAddr(); srcAddrIn->sin_addr.s_addr = INADDR_ANY; DHCP_LOGI("start recv from"); int rsize = recvfrom(sock, recvBuffer, RECV_BUFFER_SIZE, 0, (struct sockaddr *)srcAddrIn, (socklen_t *)&ssize); if (!rsize) { DHCP_LOGE("receive error, %d", errno); return RET_FAILED; } if (rsize > (int)sizeof(DhcpMessage) || rsize < DHCP_MSG_HEADER_SIZE) { DHCP_LOGW("message length error, received %d bytes.", rsize); return RET_FAILED; } DHCP_LOGI("recv over"); msgInfo->length = rsize; if (memcpy_s(&msgInfo->packet, sizeof(DhcpMessage), recvBuffer, rsize) != EOK) { return RET_FAILED; } if (msgInfo->packet.op != BOOTREQUEST) { DHCP_LOGW("dhcp message type error!"); return RET_FAILED; } if (msgInfo->packet.hlen > DHCP_HWADDR_LENGTH) { DHCP_LOGW("hlen error!"); return RET_FAILED; } if (IsEmptyHWAddr(msgInfo->packet.chaddr)) { DHCP_LOGW("client hardware address error!"); return RET_FAILED; } if (IsReserved(msgInfo->packet.chaddr)) { DHCP_LOGD("ignore client, %s", ParseLogMac(msgInfo->packet.chaddr)); return RET_FAILED; } DHCP_LOGI("start %{public}s %{public}d return success", __func__, __LINE__); return RET_SUCCESS; } void InitReply(PDhcpServerContext ctx, PDhcpMsgInfo received, PDhcpMsgInfo reply) { DHCP_LOGI("start %{public}s %{public}d", __func__, __LINE__); if (!reply) { DHCP_LOGE("reply message pointer is null!"); return; } reply->packet.op = BOOTREPLY; reply->packet.htype = ETHERNET; reply->packet.hlen = OPT_MAC_ADDR_LENGTH; reply->packet.secs = 0; reply->packet.ciaddr = 0; if (memset_s(reply->packet.sname, sizeof(reply->packet.sname), '\0', sizeof(reply->packet.sname)) != EOK) { DHCP_LOGE("failed to reset message packet[sname]!"); return; }; if (memset_s(reply->packet.file, sizeof(reply->packet.file), '\0', sizeof(reply->packet.file)) != EOK) { DHCP_LOGE("failed to reset message packet[file]!"); return; } if (FillReply(ctx, received, reply) != RET_SUCCESS) { DHCP_LOGW("failed to fill reply message."); } } void OnUpdateServerConfig(PDhcpServerContext ctx) { DHCP_LOGI("start %{public}s %{public}d", __func__, __LINE__); ServerContext *srvIns = GetServerInstance(ctx); if (!srvIns) { DHCP_LOGE("dhcp server context pointer is null."); return; } if (srvIns->callback) { srvIns->callback(ST_RELOADNG, 0, ctx->ifname); } } static void OnServerStoping(PDhcpServerContext ctx) { DHCP_LOGI("start %{public}s %{public}d", __func__, __LINE__); ServerContext *srvIns = GetServerInstance(ctx); if (!srvIns) { DHCP_LOGE("dhcp server context pointer is null."); return; } if (srvIns->callback) { srvIns->callback(ST_STOPING, 0, ctx->ifname); } } void OnServerStoped(PDhcpServerContext ctx, int code) { DHCP_LOGI("OnServerStoped."); ServerContext *srvIns = GetServerInstance(ctx); if (!srvIns) { DHCP_LOGE("dhcp server context pointer is null."); return; } if (srvIns->callback) { srvIns->callback(ST_STOPED, code, ctx->ifname); } } int SendDhcpReply(PDhcpServerContext ctx, int replyType, PDhcpMsgInfo reply) { if (!reply) { DHCP_LOGE("reply message pointer is null."); return RET_FAILED; } int sendRet = -1; ServerContext *srvIns = GetServerInstance(ctx); if (!srvIns) { DHCP_LOGE("dhcp server context pointer is null."); return RET_FAILED; } switch (replyType) { case REPLY_OFFER: DHCP_LOGD("<== send reply dhcp offer."); sendRet = SendDhcpOffer(ctx, reply); break; case REPLY_ACK: DHCP_LOGD("<== send reply dhcp ack."); sendRet = SendDhcpAck(ctx, reply); break; case REPLY_NAK: DHCP_LOGD("<== send reply dhcp nak."); sendRet = SendDhcpNak(ctx, reply); break; default: break; } if (replyType && sendRet != RET_SUCCESS) { return RET_FAILED; } return RET_SUCCESS; } static int MessageProcess(PDhcpServerContext ctx, PDhcpMsgInfo received, PDhcpMsgInfo reply) { DHCP_LOGI("start %{public}s %{public}d", __func__, __LINE__); int replyType = REPLY_NONE; if (!received) { return replyType; } PDhcpOption opt = GetOption(&received->options, DHCP_MESSAGE_TYPE_OPTION); if (!opt) { DHCP_LOGE("error dhcp message, missing required message type option."); return replyType; } uint8_t messageType = opt->data[0]; switch (messageType) { case DHCPDISCOVER: { DHCP_LOGD("==> Received DHCPDISCOVER message."); replyType = OnReceivedDiscover(ctx, received, reply); break; } case DHCPREQUEST: { DHCP_LOGD("==> Received DHCPREQUEST message."); replyType = OnReceivedRequest(ctx, received, reply); break; } case DHCPDECLINE: { DHCP_LOGD("==> Received DHCPDECLINE message."); replyType = OnReceivedDecline(ctx, received, reply); break; } case DHCPRELEASE: { DHCP_LOGD("==> Received DHCPRELEASE message."); replyType = OnReceivedRelease(ctx, received, reply); break; } case DHCPINFORM: { DHCP_LOGD("==> Received DHCPINFORM message."); replyType = OnReceivedInform(ctx, received, reply); break; } default: break; } return replyType; } int SaveLease(PDhcpServerContext ctx) { ServerContext *srvIns = GetServerInstance(ctx); if (!srvIns) { DHCP_LOGE("dhcp server context pointer is null."); return RET_FAILED; } int saveRet = SaveBindingRecoders(&srvIns->addressPool, 1); if (saveRet == RET_FAILED) { DHCP_LOGD("failed to save lease recoders. total: %zu", srvIns->addressPool.leaseTable.size()); } else if (saveRet == RET_SUCCESS) { DHCP_LOGD("lease recoders saved."); } return saveRet; } static int OnLooperStateChanged(PDhcpServerContext ctx) { ServerContext *srvIns = GetServerInstance(ctx); if (!srvIns) { DHCP_LOGE("dhcp server context pointer is null."); return RET_FAILED; } if (srvIns->looperState == LS_RELOADNG) { OnUpdateServerConfig(ctx); srvIns->looperState = LS_RUNNING; } else if (srvIns->looperState == LS_STOPING) { OnServerStoping(ctx); return RET_BREAK; } return RET_SUCCESS; } static int ContinueReceive(PDhcpMsgInfo from, int recvRet) { if (!from) { return DHCP_TRUE; } if (recvRet != RET_SUCCESS) { return DHCP_TRUE; } DHCP_LOGD("received, length:%{public}d", from->length); if (ParseMessageOptions(from) != 0) { DHCP_LOGE("invalid dhcp message."); return DHCP_TRUE; } if (!GetOption(&from->options, DHCP_MESSAGE_TYPE_OPTION)) { DHCP_LOGW("can't found 'message type' option."); return DHCP_TRUE; } return DHCP_FALSE; } static void *BeginLooper(void *argc) __attribute__((no_sanitize("cfi"))) { PDhcpServerContext ctx = (PDhcpServerContext)argc; DHCP_LOGI("start %{public}s %{public}d", __func__, __LINE__); DhcpMsgInfo from; DhcpMsgInfo reply; ServerContext *srvIns = GetServerInstance(ctx); if (!srvIns) { DHCP_LOGE("dhcp server context pointer is null."); return nullptr; } ctx->instance->serverFd = InitServer(ctx->ifname); if (ctx->instance->serverFd < 0) { DHCP_LOGE("failed to initialize server socket."); return nullptr; } InitOptionList(&from.options); InitOptionList(&reply.options); srvIns->looperState = LS_RUNNING; while (srvIns->looperState) { if (OnLooperStateChanged(ctx) != RET_SUCCESS) { DHCP_LOGI("OnLooperStateChanged break, looperState:%{public}d", srvIns->looperState); break; } ClearOptions(&from.options); ClearOptions(&reply.options); int recvRet = ReceiveDhcpMessage(ctx->instance->serverFd, &from); if (recvRet == RET_ERROR || recvRet == ERR_SELECT) { DHCP_LOGI("ReceiveDhcpMessage"); continue; } if (ContinueReceive(&from, recvRet)) { continue; } InitReply(ctx, &from, &reply); int replyType = MessageProcess(ctx, &from, &reply); if (replyType && SendDhcpReply(ctx, replyType, &reply) != RET_SUCCESS) { DHCP_LOGE("failed to send reply message."); } NotifyConnetDeviceChanged(replyType, ctx); } FreeOptionList(&from.options); FreeOptionList(&reply.options); DHCP_LOGI("dhcp server message looper stopped."); close(ctx->instance->serverFd); ctx->instance->serverFd = -1; srvIns->looperState = LS_STOPED; return nullptr; } void NotifyConnetDeviceChanged(int replyType, PDhcpServerContext ctx) { DHCP_LOGI("NotifyConnetDeviceChanged replyType:%{public}d", replyType); if (replyType == REPLY_ACK || replyType == REPLY_OFFER) { ServerContext *srvIns = GetServerInstance(ctx); if (srvIns == nullptr) { DHCP_LOGE("NotifyConnetDeviceChanged srvIns is nullptr"); return; } int saveRet = SaveBindingRecoders(&srvIns->addressPool, 1); if (saveRet != RET_SUCCESS && saveRet != RET_WAIT_SAVE) { DHCP_LOGW("SaveBindingRecoders failed to save lease recoders."); } if (replyType == REPLY_ACK && srvIns->deviceConnectFun != nullptr) { DHCP_LOGI("NotifyConnetDeviceChanged deviceConnectFun"); srvIns->deviceConnectFun(ctx->ifname); } } } static int CheckAddressRange(DhcpAddressPool *pool) { uint32_t serverNetwork = NetworkAddress(pool->serverId, pool->netmask); uint32_t firstNetwork = NetworkAddress(pool->addressRange.beginAddress, pool->netmask); uint32_t secondNetwork = NetworkAddress(pool->addressRange.endAddress, pool->netmask); if (!serverNetwork || !firstNetwork || !secondNetwork) { DHCP_LOGE("network config error."); return DHCP_FALSE; } if (serverNetwork != firstNetwork || serverNetwork != secondNetwork) { DHCP_LOGE("server network and address pool network belong to different networks."); return DHCP_FALSE; } return DHCP_TRUE; } void InitBindingRecoders(DhcpAddressPool *pool) { if (!pool) { DHCP_LOGE("address pool pointer is null."); return; } uint32_t realLeaseTotal = 0; for (auto current: pool->leaseTable) { int invalidBindig; AddressBinding *binding = ¤t.second; if (binding && !IsEmptyHWAddr(binding->chaddr) && binding->ipAddress) { AddBinding(binding); realLeaseTotal++; invalidBindig = 0; } else { DHCP_LOGE("bad binding recoder."); invalidBindig = 1; } if (!invalidBindig && binding && pool->distribution < binding->ipAddress) { pool->distribution = binding->ipAddress; } } DHCP_LOGD("lease recoder total: %u", realLeaseTotal); } void InitLeaseFile(DhcpAddressPool *pool) { const char *leasePath = GetFilePath(DHCPD_LEASE_FILE); if (!leasePath || strlen(leasePath) == 0) { DHCP_LOGE("failed to get lease file path."); return; } if (access(leasePath, 0) != 0) { DHCP_LOGD("lease file path does not exist."); if (!CreatePath(leasePath)) { DHCP_LOGE("failed to create lease file directory."); return; } else { DHCP_LOGD("lease file directory created."); } } if (LoadBindingRecoders(pool) != RET_SUCCESS) { DHCP_LOGW("failed to load lease recoders."); } InitBindingRecoders(pool); } static void ExitProcess(void) { DHCP_LOGD("dhcp server stopped."); } int StartDhcpServer(PDhcpServerContext ctx) { DHCP_LOGI("%{public}s %{public}d start", __func__, __LINE__); if (!ctx) { DHCP_LOGE("server context pointer is null."); return RET_FAILED; } if (strlen(ctx->ifname) == 0) { DHCP_LOGE("context interface is null or empty."); return RET_FAILED; } ServerContext *srvIns = GetServerInstance(ctx); if (!srvIns) { DHCP_LOGE("dhcp server context instance pointer is null."); return RET_FAILED; } if (atexit(ExitProcess) != 0) { DHCP_LOGW("failed to regiester exit process function."); } if (!srvIns->initialized) { DHCP_LOGE("dhcp server no initialized."); return RET_FAILED; } DHCP_LOGD("bind interface: %{public}s, begin dhcp message looper", ctx->ifname); if (srvIns->callback) { srvIns->callback(ST_STARTING, 1, ctx->ifname); } pthread_t threadId; int ret = pthread_create(&threadId, nullptr, BeginLooper, ctx); if (ret != RET_SUCCESS) { DHCP_LOGI("failed to start dhcp server."); return RET_FAILED; } pthread_detach(threadId); DHCP_LOGI("success to start dhcp server."); return RET_SUCCESS; } int StopDhcpServer(PDhcpServerContext ctx) { ServerContext *srvIns = GetServerInstance(ctx); if (!srvIns) { DHCP_LOGE("StopDhcpServer GetServerInstance failed!"); return RET_FAILED; } srvIns->looperState = LS_STOPING; DHCP_LOGI("StopDhcpServer looperState LS_STOPING!"); return RET_SUCCESS; } int GetServerStatus(PDhcpServerContext ctx) { ServerContext *srvIns = GetServerInstance(ctx); if (!srvIns) { DHCP_LOGE("dhcp server context pointer is null."); return -1; } return srvIns->looperState; } int FillReply(PDhcpServerContext ctx, PDhcpMsgInfo received, PDhcpMsgInfo reply) { if (!received || !reply) { return RET_ERROR; } ServerContext *srvIns = GetServerInstance(ctx); if (!srvIns) { DHCP_LOGE("dhcp server context pointer is null."); return RET_FAILED; } if (received->packet.ciaddr && received->packet.ciaddr != INADDR_BROADCAST) { reply->packet.ciaddr = received->packet.ciaddr; } reply->packet.flags = received->packet.flags; if (received->packet.xid) { reply->packet.xid = received->packet.xid; } if (received->packet.siaddr && received->packet.siaddr != INADDR_BROADCAST) { reply->packet.siaddr = received->packet.siaddr; } else { reply->packet.siaddr = srvIns->addressPool.serverId; } if (received->packet.giaddr && received->packet.giaddr != INADDR_BROADCAST) { reply->packet.giaddr = received->packet.giaddr; } else { if (srvIns->addressPool.gateway) { reply->packet.giaddr = srvIns->addressPool.gateway; } } if (received->packet.hlen) { reply->packet.hlen = received->packet.hlen; DHCP_LOGD("fill reply - chaddr:%s", ParseLogMac(received->packet.chaddr)); if (memset_s(reply->packet.chaddr, sizeof(reply->packet.chaddr), 0, sizeof(reply->packet.chaddr)) != EOK) { DHCP_LOGE("failed to reset message packet[chaddr]!"); return RET_ERROR; } if (memcpy_s(reply->packet.chaddr, sizeof(reply->packet.chaddr), received->packet.chaddr, sizeof(received->packet.chaddr)) != EOK) { DHCP_LOGE("failed to copy message packet[chaddr]!"); return RET_ERROR; } } if (received->packet.giaddr) { reply->packet.giaddr = received->packet.giaddr; } return 0; } int AppendReplyTimeOptions(PDhcpServerContext ctx, PDhcpOptionList options) { if (!ctx || !options) { DHCP_LOGE("server context or options pointer is null."); return RET_FAILED; } ServerContext *srvIns = GetServerInstance(ctx); if (!srvIns) { DHCP_LOGE("dhcp server context pointer is null."); return RET_FAILED; } uint32_t leaseTime = HostToNetwork(DHCP_LEASE_TIME); if (srvIns->addressPool.leaseTime) { leaseTime = HostToNetwork(srvIns->addressPool.leaseTime); } DhcpOption optLeaseTime = {IP_ADDRESS_LEASE_TIME_OPTION, OPT_TIME_LENGTH, {0}}; FillU32Option(&optLeaseTime, leaseTime); PushBackOption(options, &optLeaseTime); uint32_t t1Time = HostToNetwork(DHCP_RENEWAL_TIME); if (srvIns->addressPool.renewalTime) { t1Time = HostToNetwork(srvIns->addressPool.renewalTime); } DhcpOption optRenewTime = {RENEWAL_TIME_VALUE_OPTION, OPT_TIME_LENGTH, {0}}; FillU32Option(&optRenewTime, t1Time); PushBackOption(options, &optRenewTime); uint32_t t2Time = HostToNetwork(DHCP_REBINDING_TIME); if (srvIns->addressPool.rebindingTime) { t2Time = HostToNetwork(srvIns->addressPool.rebindingTime); } DhcpOption optRebindTime = {REBINDING_TIME_VALUE_OPTION, OPT_TIME_LENGTH, {0}}; FillU32Option(&optRebindTime, t2Time); PushBackOption(options, &optRebindTime); return RET_SUCCESS; } static int Repending(DhcpAddressPool *pool, AddressBinding *binding) { if (!pool) { return REPLY_NONE; } uint32_t bindingIp = binding->ipAddress; DHCP_LOGD(" binding found, bindIp:%s", ParseStrIp(bindingIp)); binding->pendingInterval = NextPendingInterval(binding->pendingInterval); uint64_t curTime = Tmspsec(); uint64_t tms = curTime > binding->pendingTime ? curTime - binding->pendingTime : 0; if (tms < binding->pendingInterval) { binding->pendingTime = curTime; DHCP_LOGW("message interval is too short, ignore the message."); return REPLY_NONE; } binding->pendingTime = curTime; binding->pendingInterval = 0; binding->bindingStatus = BIND_PENDING; uint32_t srcIp = SourceIpAddress(); if (srcIp && srcIp != INADDR_BROADCAST && bindingIp != INADDR_BROADCAST && srcIp != bindingIp) { DHCP_LOGW("source ip address and bound ip address inconsistency."); return REPLY_NAK; } if (srcIp && srcIp == bindingIp) { if (pool->leaseTable.count(srcIp) == 0) { DHCP_LOGD("can't find lease information."); pool->leaseTable[srcIp] = *binding; } else { pool->leaseTable[srcIp] = *binding; } } return REPLY_OFFER; } static int Rebinding(DhcpAddressPool *pool, AddressBinding *binding) { uint64_t pendingTime = binding->pendingTime; int replyType = Repending(pool, binding); binding->bindingStatus = BIND_ASSOCIATED; if (!binding->leaseTime) { binding->leaseTime = pool->leaseTime; } binding->bindingTime = Tmspsec(); binding->expireIn = binding->bindingTime + binding->leaseTime; binding->pendingTime = pendingTime; if (replyType == REPLY_OFFER) { replyType = REPLY_ACK; } return replyType; } static void AddAddressOption(PDhcpMsgInfo reply, uint8_t code, int32_t address) { if (!reply) { return; } DhcpOption optAddress = {0, 0, {0}}; optAddress.code = code; if (AppendAddressOption(&optAddress, address) != RET_SUCCESS) { DHCP_LOGE("failed to append address option."); return; }; PushBackOption(&reply->options, &optAddress); } int AddReplyServerIdOption(PDhcpOptionList options, uint32_t serverId) { if (!options) { DHCP_LOGE("option list pointer is null."); return RET_FAILED; } if (!serverId || serverId == INADDR_BROADCAST) { DHCP_LOGE("servier id error."); return RET_FAILED; } DhcpOption optSrvId = {SERVER_IDENTIFIER_OPTION, 0, {0}}; if (AppendAddressOption(&optSrvId, serverId) != RET_SUCCESS) { DHCP_LOGE("failed to append server id option."); return RET_FAILED; } if (GetOption(options, SERVER_IDENTIFIER_OPTION)) { DHCP_LOGD("server identifier option exists."); return RET_SUCCESS; } PushBackOption(options, &optSrvId); return RET_SUCCESS; } static void AddReplyMessageTypeOption(PDhcpMsgInfo reply, uint8_t replyMessageType) { if (!reply) { return; } DhcpOption optMsgType = {DHCP_MESSAGE_TYPE_OPTION, OPT_MESSAGE_TYPE_LEGTH, {replyMessageType, 0}}; PushBackOption(&reply->options, &optMsgType); } AddressBinding *GetBinding(DhcpAddressPool *pool, PDhcpMsgInfo received) { if (!pool) { return nullptr; } if (!received) { return nullptr; } AddressBinding *binding = pool->binding(received->packet.chaddr, &received->options); if (!binding) { binding = pool->newBinding(received->packet.chaddr, &received->options); if (binding == nullptr) { DHCP_LOGE("new binding is null"); return nullptr; } if (pool->leaseTime) { binding->leaseTime = pool->leaseTime; } binding->ipAddress = pool->distribue(pool, received->packet.chaddr); DHCP_LOGI("new binding ip"); } else { DHCP_LOGI("rebinding ip"); } return binding; } int ReplyCommontOption(PDhcpServerContext ctx, PDhcpMsgInfo reply) { if (!reply) { DHCP_LOGE("reply is nullptr!"); return REPLY_NONE; } ServerContext *srvIns = GetServerInstance(ctx); if (!srvIns) { DHCP_LOGE("srvIns is nullptr!"); return REPLY_NONE; } AddAddressOption(reply, SUBNET_MASK_OPTION, srvIns->addressPool.netmask); if (srvIns->addressPool.gateway) { AddAddressOption(reply, ROUTER_OPTION, srvIns->addressPool.gateway); } DhcpOption optVendorInfo = {VENDOR_SPECIFIC_INFO_OPTION, static_cast(strlen(VNEDOR_OPEN_HARMONY)), VNEDOR_OPEN_HARMONY}; PushBackOption(&reply->options, &optVendorInfo); uint32_t netAddress = reply->packet.yiaddr & srvIns->addressPool.netmask; uint32_t boastAddress = (~srvIns->addressPool.netmask) | netAddress; AddAddressOption(reply, BROADCAST_ADDRESS_OPTION, boastAddress); return REPLY_OFFER; } static int DiscoverReplyLeaseMessage(PDhcpServerContext ctx, PDhcpMsgInfo reply, ServerContext *srvIns, AddressBinding *binding) { if (!ctx) { DHCP_LOGE("ctx pointer is null."); return REPLY_NONE; } if (!reply) { DHCP_LOGE("reply message pointer is null."); return REPLY_NONE; } if (!srvIns) { DHCP_LOGE("get server instance is nullptr!"); return REPLY_NONE; } if (!binding) { DHCP_LOGI("Discover binding is null, reply none"); return REPLY_NONE; } AddressBinding *lease = GetLease(&srvIns->addressPool, binding->ipAddress); if (!lease) { DHCP_LOGI("Discover add lease, binging ip:%{public}s mac:%{public}s", IntIpv4ToAnonymizeStr(binding->ipAddress).c_str(), ParseLogMac(binding->chaddr)); AddLease(&srvIns->addressPool, binding); lease = GetLease(&srvIns->addressPool, binding->ipAddress); } if (!lease) { DHCP_LOGI("Discover lease is null, reply none"); return REPLY_NONE; } AddReplyMessageTypeOption(reply, DHCPOFFER); reply->packet.yiaddr = lease->ipAddress; ReplyCommontOption(ctx, reply); DHCP_LOGI("Discover reply offer"); return REPLY_OFFER; } static int OnReceivedDiscover(PDhcpServerContext ctx, PDhcpMsgInfo received, PDhcpMsgInfo reply) { if (!received || !reply) { DHCP_LOGE("receive or reply message pointer is null."); return REPLY_NONE; } DHCP_LOGI("received 'Discover' message from:%{public}s", ParseLogMac(received->packet.chaddr)); ServerContext *srvIns = GetServerInstance(ctx); if (!srvIns) { DHCP_LOGE("get server instance is nullptr!"); return REPLY_NONE; } uint32_t reqIp = 0; PDhcpOption optReqIp = GetOption(&received->options, REQUESTED_IP_ADDRESS_OPTION); if (optReqIp) { reqIp = ParseIp(optReqIp->data); if (reqIp) { DHCP_LOGI("Discover request ip:%{public}s", IntIpv4ToAnonymizeStr(reqIp).c_str()); } } uint32_t srcIp = SourceIpAddress(); if (!srvIns->broadCastFlagEnable) { if (srcIp) { DHCP_LOGI("Discover client repending:%{public}s", IntIpv4ToAnonymizeStr(srcIp).c_str()); } else { srcIp = INADDR_BROADCAST; } DestinationAddr(srcIp); } AddressBinding *binding = GetBinding(&srvIns->addressPool, received); if (!binding) { DHCP_LOGI("Discover binding is null, reply none"); return REPLY_NONE; } if (!binding->ipAddress) { DHCP_LOGI("Discover binding ipAddress is null, reply none"); return REPLY_NONE; } if (reqIp != 0 && reqIp != binding->ipAddress) { DHCP_LOGW("Discover package reqIp:%{public}s, binging ip:%{public}s", IntIpv4ToAnonymizeStr(reqIp).c_str(), IntIpv4ToAnonymizeStr(binding->ipAddress).c_str()); } DeleteMacInLease(&srvIns->addressPool, binding); return DiscoverReplyLeaseMessage(ctx, reply, srvIns, binding); } static uint32_t GetRequestIpAddress(PDhcpMsgInfo received) { uint32_t reqIp = 0; if (!received) { return reqIp; } PDhcpOption optReqIp = GetOption(&received->options, REQUESTED_IP_ADDRESS_OPTION); if (optReqIp) { reqIp = ParseIp(optReqIp->data); } return reqIp; } static int GetYourIpAddress(PDhcpMsgInfo received, uint32_t *yourIpAddr, DhcpAddressPool *pool) { uint32_t cliIp = received->packet.ciaddr; uint32_t srcIp = SourceIpAddress(); uint32_t reqIp = GetRequestIpAddress(received); DHCP_LOGI("cliIp:%{public}s srcIp:%{public}s reqIp:%{public}s", IntIpv4ToAnonymizeStr(cliIp).c_str(), IntIpv4ToAnonymizeStr(srcIp).c_str(), IntIpv4ToAnonymizeStr(reqIp).c_str()); if (cliIp && srcIp && cliIp != srcIp) { DHCP_LOGE("error dhcp request message, missing required request option."); return RET_FAILED; } if (reqIp && srcIp && reqIp != srcIp) { DHCP_LOGE("error dhcp request message, request ip error."); return RET_FAILED; } if (cliIp && reqIp && cliIp != reqIp) { DHCP_LOGE("error dhcp request message, client ip error."); return RET_FAILED; } if (srcIp && srcIp != INADDR_BROADCAST) { *yourIpAddr = srcIp; } else if (cliIp && cliIp != INADDR_BROADCAST) { *yourIpAddr = cliIp; } else if (reqIp && reqIp != INADDR_BROADCAST) { *yourIpAddr = reqIp; } if ((ntohl(*yourIpAddr) < ntohl(pool->addressRange.beginAddress)) || (ntohl(*yourIpAddr) > ntohl(pool->addressRange.endAddress))) { return RET_FAILED; } if (srcIp && srcIp != INADDR_BROADCAST) { DestinationAddr(srcIp); } else if (srcIp == INADDR_ANY) { DestinationAddr(INADDR_BROADCAST); } return RET_SUCCESS; } static int NotBindingRequest(DhcpAddressPool *pool, PDhcpMsgInfo received, PDhcpMsgInfo reply) { uint32_t yourIpAddr = 0; if (GetYourIpAddress(received, &yourIpAddr, pool) != RET_SUCCESS) { DHCP_LOGI("GetYourIpAddress REPLY_NONE"); return REPLY_NONE; } AddressBinding *lease = GetLease(pool, yourIpAddr); if (!lease) { if (SourceIpAddress()) { DHCP_LOGI("SourceIpAddress True REPLY_ACK"); return REPLY_ACK; } DHCP_LOGI("SourceIpAddress REPLY_NAK"); return REPLY_NAK; } int sameAddr = AddrEquels(lease->chaddr, received->packet.chaddr, MAC_ADDR_LENGTH); if (lease->bindingStatus == BIND_ASSOCIATED && !sameAddr) { if (!IsExpire(lease)) { DHCP_LOGI("Not IsExpire REPLY_NAK"); return REPLY_NAK; } DHCP_LOGI("RemoveLease lease"); RemoveLease(pool, lease); } AddressBinding *binding = pool->newBinding(received->packet.chaddr, &received->options); if (binding == nullptr) { DHCP_LOGE("Not binding request binding is null."); return REPLY_NONE; } binding->ipAddress = yourIpAddr; if (pool->leaseTime) { binding->leaseTime = pool->leaseTime; } int replyType = Repending(pool, binding); if (replyType != REPLY_OFFER) { DHCP_LOGI("replyType != REPLY_OFFER"); return replyType; } lease = GetLease(pool, yourIpAddr); if (!lease) { DHCP_LOGI("add new lease recoder."); AddLease(pool, binding); lease = GetLease(pool, binding->ipAddress); } if (!lease) { DHCP_LOGI("failed to get lease."); return REPLY_NONE; } lease->bindingStatus = BIND_ASSOCIATED; lease->bindingTime = Tmspsec(); lease->expireIn = lease->bindingTime + binding->leaseTime; reply->packet.yiaddr = lease->ipAddress; DHCP_LOGI("NotBindingRequest REPLY_ACK"); return REPLY_ACK; } static int ValidateRequestMessage(const PDhcpServerContext ctx, const PDhcpMsgInfo received, PDhcpMsgInfo reply, uint32_t *yourIp) { if (!received || !reply) { DHCP_LOGE("receive or reply message pointer is null."); return REPLY_NONE; } DHCP_LOGI("received 'Request' message from:%{public}s", ParseLogMac(received->packet.chaddr)); uint32_t yourIpAddr = INADDR_BROADCAST; ServerContext *srvIns = GetServerInstance(ctx); if (!srvIns) { DHCP_LOGI("get server instance failed!"); return RET_FAILED; } if (GetYourIpAddress(received, &yourIpAddr, &srvIns->addressPool) != RET_SUCCESS) { if (yourIpAddr && yourIpAddr != INADDR_BROADCAST) { AddressBinding *lease = GetLease(&srvIns->addressPool, yourIpAddr); if (lease) { RemoveLease(&srvIns->addressPool, lease); DHCP_LOGD("lease recoder has been removed."); } else { DHCP_LOGW("can't found lease recoder."); } RemoveBinding(received->packet.chaddr); return REPLY_NAK; } return REPLY_NONE; } PDhcpOption optReqSrvId = GetOption(&received->options, SERVER_IDENTIFIER_OPTION); if (optReqSrvId) { uint32_t reqSrvId = ParseIp(optReqSrvId->data); DHCP_LOGD(" reuquest server id is:%s", ParseStrIp(reqSrvId)); if (reqSrvId != srvIns->addressPool.serverId) { DHCP_LOGW("other dhcp server process."); return REPLY_NONE; } } else { DHCP_LOGW("request message not specified server identifier option."); } *yourIp = yourIpAddr; return REPLY_ACK; } static int HasNobindgRequest(PDhcpServerContext ctx, PDhcpMsgInfo received, PDhcpMsgInfo reply) { if (!received || !reply) { DHCP_LOGE("receive or reply message pointer is null."); return REPLY_NONE; } ServerContext *srvIns = GetServerInstance(ctx); if (!srvIns) { DHCP_LOGE("dhcp server context pointer is null."); return REPLY_NONE; } AddressBinding *binding = srvIns->addressPool.binding(received->packet.chaddr, &received->options); if (!binding && ALLOW_NOBINDING_REQUEST) { uint32_t srcIp = SourceIpAddress(); uint32_t reqIp = GetRequestIpAddress(received); DHCP_LOGD("allow no binding request mode."); if (reqIp == 0 && srcIp == 0) { DHCP_LOGE("error dhcp message."); return REPLY_NONE; } if (!IpInNetwork(reqIp, srvIns->addressPool.serverId, srvIns->addressPool.netmask)) { DHCP_LOGE("error request ip."); return REPLY_NAK; } return NotBindingRequest(&srvIns->addressPool, received, reply); } return REPLY_NONE; } int GetVendorIdentifierOption(PDhcpMsgInfo received) { PDhcpOption optVendorIdentifier = GetOption(&received->options, VENDOR_CLASS_IDENTIFIER_OPTION); if (optVendorIdentifier) { char strVendorIdentifier[DEVICE_NAME_STRING_LENGTH] = {0}; if (memcpy_s(strVendorIdentifier, DEVICE_NAME_STRING_LENGTH, (char*)optVendorIdentifier->data, optVendorIdentifier->length) != EOK) { DHCP_LOGE("GetVendorIdentifierOption strClientIdentifier memcpy_s failed!"); return REPLY_NONE; } DHCP_LOGD("GetVendorIdentifierOption strClientIdentifier:%{public}s", strVendorIdentifier); } else { DHCP_LOGD("GetVendorIdentifierOption pClientIdentifier is null"); } return REPLY_NAK; } int GetHostNameOption(PDhcpMsgInfo received, AddressBinding *bindin) { if (!bindin) { DHCP_LOGE("GetHostNameOption bindin is nullptr!"); return REPLY_NONE; } PDhcpOption optHostName = GetOption(&received->options, HOST_NAME_OPTION); if (optHostName) { if (memcpy_s(bindin->deviceName, DEVICE_NAME_STRING_LENGTH, (char*)optHostName->data, optHostName->length) != EOK) { DHCP_LOGE("GetHostNameOption pHost memcpy_s failed!"); return REPLY_NONE; } DHCP_LOGI("GetHostNameOption deviceName:%{public}s", bindin->deviceName); } else { DHCP_LOGD("GetHostNameOption pHost is null"); } return REPLY_NAK; } int GetUserClassOption(PDhcpMsgInfo received, AddressBinding *bindin) { if (!bindin) { DHCP_LOGE("GetUserClassOption bindin is nullptr!"); return REPLY_NONE; } PDhcpOption option = GetOption(&received->options, USER_CLASS_OPTION); if (option) { if (memcpy_s(bindin->userClass, DEVICE_NAME_STRING_LENGTH, (char*)option->data, option->length) != EOK) { DHCP_LOGE("GetUserClassOption memcpy_s failed!"); return REPLY_NONE; } DHCP_LOGD("GetUserClassOption userClass:%{public}s", bindin->userClass); } else { DHCP_LOGD("GetUserClassOption pHost is null"); } return REPLY_ACK; } int GetRapidCommitOption(PDhcpMsgInfo received, AddressBinding *bindin) { if (!bindin) { DHCP_LOGE("GetRapidCommitOption bindin is nullptr!"); return REPLY_NONE; } PDhcpOption option = GetOption(&received->options, RAPID_COMMIT_OPTION); if (option) { char value[DEVICE_NAME_STRING_LENGTH] = {0}; if (memcpy_s(value, DEVICE_NAME_STRING_LENGTH, (char*)option->data, option->length) != EOK) { DHCP_LOGE("GetRapidCommitOption memcpy_s failed!"); return REPLY_NONE; } DHCP_LOGD("GetRapidCommitOption value:%{public}s", value); } else { DHCP_LOGD("GetRapidCommitOption pHost is null"); } return REPLY_ACK; } int GetOnlyIpv6Option(PDhcpMsgInfo received, AddressBinding *bindin) { if (!bindin) { DHCP_LOGE("GetOnlyIpv6Option bindin is nullptr!"); return REPLY_NONE; } PDhcpOption option = GetOption(&received->options, IPV6_ONLY_PREFERRED_OPTION); if (option) { char value[DEVICE_NAME_STRING_LENGTH] = {0}; if (memcpy_s(value, DEVICE_NAME_STRING_LENGTH, (char*)option->data, option->length) != EOK) { DHCP_LOGE("GetOnlyIpv6Option memcpy_s failed!"); return REPLY_NONE; } DHCP_LOGD("GetOnlyIpv6Option value:%{public}s", value); } else { DHCP_LOGD("GetOnlyIpv6Option pHost is null"); } return REPLY_ACK; } int GetPortalUrlOption(PDhcpMsgInfo received, AddressBinding *bindin) { if (!bindin) { DHCP_LOGE("GetPortalUrlOption bindin is nullptr!"); return REPLY_NONE; } PDhcpOption option = GetOption(&received->options, IPV6_ONLY_PREFERRED_OPTION); if (option) { char value[DEVICE_NAME_STRING_LENGTH] = {0}; if (memcpy_s(value, DEVICE_NAME_STRING_LENGTH, (char*)option->data, option->length) != EOK) { DHCP_LOGE("GetPortalUrlOption memcpy_s failed!"); return REPLY_NONE; } DHCP_LOGD("GetPortalUrlOption value:%{public}s", value); } else { DHCP_LOGD("GetPortalUrlOption pHost is null"); } return REPLY_ACK; } int ParseDhcpOption(PDhcpMsgInfo received, AddressBinding *bindin) { if (!bindin) { DHCP_LOGE("ParseDhcpOption bindin is nullptr!"); return REPLY_NONE; } DHCP_LOGE("enter ParseDhcpOption"); GetHostNameOption(received, bindin); GetVendorIdentifierOption(received); GetUserClassOption(received, bindin); GetRapidCommitOption(received, bindin); GetOnlyIpv6Option(received, bindin); GetPortalUrlOption(received, bindin); return REPLY_ACK; } static int OnReceivedRequest(PDhcpServerContext ctx, PDhcpMsgInfo received, PDhcpMsgInfo reply) { int ret; uint32_t yourIpAddr; if ((ret = ValidateRequestMessage(ctx, received, reply, &yourIpAddr)) != REPLY_ACK) { DHCP_LOGE("Request validateRequestMessage ret:%{public}d", ret); return ret; } ServerContext *srvIns = GetServerInstance(ctx); if (srvIns == nullptr) { DHCP_LOGE("OnReceivedRequest, srvIns is null"); return REPLY_NONE; } AddressBinding *binding = srvIns->addressPool.binding(received->packet.chaddr, &received->options); if (binding == nullptr) { DHCP_LOGI("Request enter HasNobindgRequest!"); return HasNobindgRequest(ctx, received, reply); } Rebinding(&srvIns->addressPool, binding); AddressBinding *lease = GetLease(&srvIns->addressPool, yourIpAddr); if (lease) { ParseDhcpOption(received, lease); DHCP_LOGI("request in lease, yourIpAddr:%{public}s, mac:%{public}s", IntIpv4ToAnonymizeStr(yourIpAddr).c_str(), ParseLogMac(lease->chaddr)); int sameAddr = AddrEquels(lease->chaddr, received->packet.chaddr, MAC_ADDR_LENGTH); if (!sameAddr && !IsExpire(lease)) { DHCP_LOGW("invalid request ip address, reply nak, sameAddr:%{public}d", sameAddr); return REPLY_NAK; } if (!sameAddr && IsExpire(lease)) { if (memcpy_s(lease->chaddr, DHCP_HWADDR_LENGTH, binding->chaddr, MAC_ADDR_LENGTH) != EOK) { DHCP_LOGW("failed to update lease client address, sameAddr:%{public}d", sameAddr); } } lease->bindingStatus = BIND_ASSOCIATED; lease->bindingTime = binding->bindingTime; lease->expireIn = binding->expireIn; DHCP_LOGI("Request found lease recoder, sameAddr:%{public}d", sameAddr); } else { DHCP_LOGW("Request can not found lease recoder."); } uint32_t bindingIp = binding->ipAddress; if (bindingIp && yourIpAddr != INADDR_BROADCAST && yourIpAddr != bindingIp) { DHCP_LOGE("error request ip binding. reply nak"); return REPLY_NAK; } reply->packet.yiaddr = bindingIp; ReplyCommontOption(ctx, reply); DHCP_LOGI("Request reply ack!"); return REPLY_ACK; } static int OnReceivedDecline(PDhcpServerContext ctx, PDhcpMsgInfo received, PDhcpMsgInfo reply) { if (!received || !reply) { return REPLY_NONE; } ServerContext *srvIns = GetServerInstance(ctx); if (!srvIns) { return REPLY_NONE; } DHCP_LOGI("received 'Decline' message from: %s.", ParseLogMac(received->packet.chaddr)); uint32_t reqIp = 0; PDhcpOption optReqIp = GetOption(&received->options, REQUESTED_IP_ADDRESS_OPTION); if (optReqIp) { reqIp = ParseIp(optReqIp->data); } if (!reqIp) { DHCP_LOGD("invalid request ip address."); return REPLY_NONE; } AddressBinding* binding = srvIns->addressPool.binding(received->packet.chaddr, &received->options); if (!binding) { DHCP_LOGD("client not binding."); return REPLY_NONE; } if (binding->ipAddress != reqIp) { DHCP_LOGD("invalid request ip address."); return REPLY_NONE; } if (srvIns->addressPool.leaseTable.count(reqIp) > 0) { AddressBinding *lease = &srvIns->addressPool.leaseTable[reqIp]; if (lease) { lease->bindingStatus = BIND_MODE_RESERVED; lease->expireIn = Tmspsec() + lease->leaseTime; } else { DHCP_LOGE("failed to get lease info."); } } RemoveBinding(received->packet.chaddr); return REPLY_NONE; } static int OnReceivedRelease(PDhcpServerContext ctx, PDhcpMsgInfo received, PDhcpMsgInfo reply) { if (!received || !reply) { return REPLY_NONE; } DHCP_LOGI("received 'Release' message from: %s", ParseLogMac(received->packet.chaddr)); if (!ctx || !ctx->instance) { return RET_FAILED; } PDhcpOption optReqIp = GetOption(&received->options, REQUESTED_IP_ADDRESS_OPTION); if (!optReqIp) { DHCP_LOGW("missing required request option."); } ServerContext *srvIns = GetServerInstance(ctx); if (!srvIns) { DHCP_LOGE("dhcp server context pointer is null."); return RET_FAILED; } AddressBinding *binding = srvIns->addressPool.binding(received->packet.chaddr, &received->options); if (!binding) { DHCP_LOGD("client not binding."); return REPLY_NONE; } uint32_t bindIp = binding->ipAddress; uint32_t reqIp = 0; if (optReqIp) { reqIp = ParseIp(optReqIp->data); } uint32_t srcIp = SourceIpAddress(); if (srcIp != 0 && reqIp != 0 && reqIp != srcIp) { DHCP_LOGE("error release message, invalid request ip address."); return REPLY_NONE; } if (bindIp != 0 && reqIp != 0 && reqIp != bindIp) { DHCP_LOGE("error release message, invalid request ip address."); return REPLY_NONE; } AddressBinding *lease = GetLease(&srvIns->addressPool, bindIp); if (lease) { RemoveLease(&srvIns->addressPool, lease); DHCP_LOGD("lease recoder has been removed."); } else { DHCP_LOGW("can't found lease recoder."); } if (ReleaseBinding(received->packet.chaddr) != RET_SUCCESS) { DHCP_LOGW("failed to release client[%s] bind.", ParseLogMac(received->packet.chaddr)); } DHCP_LOGD("client released."); return REPLY_NONE; } static int OnReceivedInform(PDhcpServerContext ctx, PDhcpMsgInfo received, PDhcpMsgInfo reply) { if (!received || !reply) { return REPLY_NONE; } ServerContext *srvIns = GetServerInstance(ctx); if (!srvIns) { DHCP_LOGE("dhcp server context pointer is null."); return RET_FAILED; } DHCP_LOGI("received 'Inform' message from: %s", ParseLogMac(received->packet.chaddr)); if (IsEmptyHWAddr(received->packet.chaddr)) { DHCP_LOGD("error dhcp 'Inform' message."); } return REPLY_ACK; } static int AppendFixedOptions(PDhcpServerContext ctx, PDhcpMsgInfo reply) { ServerContext *srvIns = GetServerInstance(ctx); if (!srvIns) { return RET_FAILED; } if (!reply) { return RET_FAILED; } if (srvIns->addressPool.fixedOptions.size > 0) { DhcpOptionNode *pNode = srvIns->addressPool.fixedOptions.first->next; for (size_t i = 0; pNode != nullptr && i < srvIns->addressPool.fixedOptions.size; i++) { PDhcpOption opt = nullptr; if (pNode->option.code) { opt = GetOption(&reply->options, pNode->option.code); } if (opt == nullptr) { PushBackOption(&reply->options, &pNode->option); } pNode = pNode->next; } } return RET_SUCCESS; } int AppendReplyTypeOption(PDhcpMsgInfo reply, int replyType) { if (!reply) { return RET_FAILED; } if (!replyType) { return RET_FAILED; } uint8_t msgType = 0; switch (replyType) { case REPLY_OFFER: msgType = DHCPOFFER; break; case REPLY_ACK: msgType = DHCPACK; break; case REPLY_NAK: msgType = DHCPNAK; break; default: break; } PDhcpOption pOptMsgType = GetOption(&reply->options, DHCP_MESSAGE_TYPE_OPTION); if (!pOptMsgType) { DHCP_LOGD("append message type option for reply message, type:%hhu", msgType); DhcpOption optMsgType = {DHCP_MESSAGE_TYPE_OPTION, OPT_MESSAGE_TYPE_LEGTH, {msgType, 0}}; PushFrontOption(&reply->options, &optMsgType); } else { if (pOptMsgType->data[0] != msgType) { DHCP_LOGD("error dhcp nak message type."); return RET_FAILED; } } return RET_SUCCESS; } static int SendDhcpOffer(PDhcpServerContext ctx, PDhcpMsgInfo reply) { ServerContext *srvIns = GetServerInstance(ctx); if (!srvIns) { DHCP_LOGE("failed to get server instance"); return RET_FAILED; } if (AppendReplyTypeOption(reply, REPLY_OFFER) != RET_SUCCESS) { DHCP_LOGE("failed to append reply type options"); return RET_FAILED; } if (AppendReplyTimeOptions(ctx, &reply->options) != RET_SUCCESS || AddReplyServerIdOption(&reply->options, srvIns->addressPool.serverId) != RET_SUCCESS) { DHCP_LOGE("failed to append reply time options"); return RET_FAILED; } if (AppendFixedOptions(ctx, reply) != RET_SUCCESS) { DHCP_LOGW("failed to append fixed reply options."); } if (ParseReplyOptions(reply) != RET_SUCCESS) { DHCP_LOGE("failed to parse reply options."); return RET_FAILED; } if (TransmitOfferOrAckPacket(ctx, reply) != RET_SUCCESS) { DHCP_LOGE("send reply offer failed"); return RET_FAILED; } DHCP_LOGI("send reply offer, length:%d", reply->length); return RET_SUCCESS; } static bool GetBroadCastFlag(PDhcpMsgInfo reply) { bool broadcastFlag = false; if (reply->packet.flags >> (DHCP_MESSAGE_FLAG_LENGTH - 1)) { broadcastFlag = true; } if ((reply->packet.ciaddr == 0) && (broadcastFlag || (reply->packet.yiaddr == 0))) { return true; } return false; } static int32_t TransmitOfferOrAckPacket(PDhcpServerContext ctx, PDhcpMsgInfo reply) { ServerContext *srvIns = GetServerInstance(ctx); if (!srvIns) { DHCP_LOGE("TransmitOfferOrAckPacket failed to get server instance"); return RET_FAILED; } int ret; sockaddr_in *bcastAddrIn = BroadcastAddrIn(); sockaddr_in *destAddrIn = DestinationAddrIn(); if (srvIns->broadCastFlagEnable == 1 && destAddrIn) { bool broadCastFlag = GetBroadCastFlag(reply); DHCP_LOGI("TransmitOfferOrAckPacket, broadCastFlag: %{public}d", broadCastFlag); if (!broadCastFlag) { destAddrIn->sin_addr.s_addr = reply->packet.yiaddr; std::string ipAddr = Ip4IntConvertToStr(reply->packet.yiaddr, false); std::string macAddr = ParseStrMac(reply->packet.chaddr, sizeof(reply->packet.chaddr)); if (AddArpEntry(ctx->ifname, ipAddr, macAddr) < 0) { DHCP_LOGE("AddArpEntry failed"); return RET_FAILED; } ret = sendto(srvIns->serverFd, &reply->packet, reply->length, 0, (struct sockaddr *)destAddrIn, sizeof(*destAddrIn)); } else { ret = sendto(srvIns->serverFd, &reply->packet, reply->length, 0, (struct sockaddr *)bcastAddrIn, sizeof(*bcastAddrIn)); } } else { ret = sendto( srvIns->serverFd, &reply->packet, reply->length, 0, (struct sockaddr *)bcastAddrIn, sizeof(*bcastAddrIn)); } if (!ret) { DHCP_LOGE("failed to send dhcp message."); return RET_FAILED; } return RET_SUCCESS; } static int SendDhcpAck(PDhcpServerContext ctx, PDhcpMsgInfo reply) { if (AppendReplyTypeOption(reply, REPLY_ACK) != RET_SUCCESS) { DHCP_LOGE("failed to append reply type options"); return RET_FAILED; } if (AppendFixedOptions(ctx, reply) != RET_SUCCESS) { DHCP_LOGW("failed to append fixed reply options."); } if (!ctx || !ctx->instance) { DHCP_LOGE("dhcp server context pointer is null."); return RET_FAILED; } ServerContext *srvIns = GetServerInstance(ctx); if (AppendReplyTimeOptions(ctx, &reply->options) != RET_SUCCESS) { DHCP_LOGE("failed to append reply time options"); return RET_FAILED; } if (AddReplyServerIdOption(&reply->options, srvIns->addressPool.serverId) != RET_SUCCESS) { DHCP_LOGE("failed to add reply server options"); return RET_FAILED; } if (ParseReplyOptions(reply) != RET_SUCCESS) { DHCP_LOGE("failed to parse reply options"); return RET_FAILED; } if (TransmitOfferOrAckPacket(ctx, reply) != RET_SUCCESS) { DHCP_LOGE("failed to send dhcp ack message"); return RET_FAILED; } DHCP_LOGI("send reply ack, size:%d", reply->length); return RET_SUCCESS; } static int SendDhcpNak(PDhcpServerContext ctx, PDhcpMsgInfo reply) { if (AppendReplyTypeOption(reply, REPLY_NAK) != RET_SUCCESS) { DHCP_LOGE("failed to append reply type options"); return RET_FAILED; } ServerContext *srvIns = GetServerInstance(ctx); if (srvIns == nullptr) { DHCP_LOGE("SendDhcpNak, srvIns is null"); return RET_FAILED; } if (AddReplyServerIdOption(&reply->options, srvIns->addressPool.serverId) != RET_SUCCESS) { DHCP_LOGE("SendDhcpNak serverId fail!"); return RET_FAILED; } DhcpOption optVendorInfo = {MESSAGE_OPTION, static_cast(strlen("wrong network")), "wrong network"}; PushBackOption(&reply->options, &optVendorInfo); if (ParseReplyOptions(reply) != RET_SUCCESS) { DHCP_LOGE("failed to parse reply options"); return RET_FAILED; } struct sockaddr_in *destAddrIn = BroadcastAddrIn(); int ret = sendto(srvIns->serverFd, &reply->packet, reply->length, 0, (struct sockaddr *)destAddrIn, sizeof(*destAddrIn)); if (!ret) { DHCP_LOGD("failed to send dhcp ack message."); return RET_FAILED; } DHCP_LOGI("send reply nak, size:%d", reply->length); return RET_SUCCESS; } static int ParseMessageOptions(PDhcpMsgInfo msg) { DHCP_LOGI("start %{public}s %{public}d", __func__, __LINE__); if (msg->length < (DHCP_MSG_HEADER_SIZE + MAGIC_COOKIE_LENGTH)) { return RET_FAILED; } DhcpOption *current, *end; current = (DhcpOption *)msg->packet.options; end = (DhcpOption *)(((uint8_t *)msg->packet.options) + (msg->length - DHCP_MSG_HEADER_SIZE)); if (memcmp(current, MAGIC_COOKIE_DATA, sizeof(MAGIC_COOKIE_DATA)) != 0) { DHCP_LOGD("bad magic cookie."); return RET_FAILED; } current = (DhcpOption *)(((uint8_t *)current) + MAGIC_COOKIE_LENGTH); uint8_t *pos = (((uint8_t *)current) + MAGIC_COOKIE_LENGTH); uint8_t *maxPos = (((uint8_t *)current) + (DHCP_OPTION_SIZE - MAGIC_COOKIE_LENGTH - OPT_HEADER_LENGTH -1)); int optTotal = 0; while (current < end && current->code != END_OPTION) { if (((uint8_t *)end) - ((uint8_t *)current) < OPT_HEADER_LENGTH) { DHCP_LOGE("current->code out of option range."); return RET_FAILED; } pos += (OPT_HEADER_LENGTH + current->length); if (pos >= maxPos) { DHCP_LOGD("out of option max pos."); return RET_FAILED; } if (PushBackOption(&msg->options, current) != RET_SUCCESS) { DHCP_LOGD("failed to PushOption."); } current = (DhcpOption *)(((uint8_t *)current) + OPT_HEADER_LENGTH + current->length); optTotal++; } if (current < end && current->code == END_OPTION) { DHCP_LOGD("option list size:%zu xid:%u", msg->options.size, msg->packet.xid); return RET_SUCCESS; } DHCP_LOGD("option list parse failed."); return RET_FAILED; } static int ResetMessageOptions(PDhcpMsgInfo reply) { if (!reply || reply->options.size == 0) { DHCP_LOGE("message pointer is null."); return RET_ERROR; } if (memset_s(reply->packet.options, DHCP_OPTIONS_SIZE, 0, DHCP_OPTIONS_SIZE) != EOK) { DHCP_LOGE("failed to reset message options!"); return RET_ERROR; } return RET_SUCCESS; } static int ValidateReplyOptions(PDhcpMsgInfo reply) { if (!reply) { DHCP_LOGE("reply message pointer is null."); return RET_FAILED; } int ret = RET_FAILED; if ((ret = ResetMessageOptions(reply)) != RET_SUCCESS) { return ret; } reply->length = DHCP_MSG_HEADER_SIZE; PDhcpOptionNode pNode = reply->options.first; if (!pNode) { return RET_ERROR; } PDhcpOption pOptMsgType = GetOption(&reply->options, DHCP_MESSAGE_TYPE_OPTION); if (!pOptMsgType) { DHCP_LOGE("unknown reply message type."); return ret; } return RET_SUCCESS; } static int ParseReplyOptions(PDhcpMsgInfo reply) { int ret = RET_FAILED; if ((ret = ValidateReplyOptions(reply)) != RET_SUCCESS) { return ret; } PDhcpOptionNode pNode = reply->options.first->next; DhcpOption endOpt = {END_OPTION, 0, {0}}; PushBackOption(&reply->options, &endOpt); int replyOptsLength = 0; uint8_t *current = reply->packet.options, olen = MAGIC_COOKIE_LENGTH; size_t remainingSize = sizeof(reply->packet.options); uint32_t cookie = htonl(DHCP_MAGIC_COOKIE); if (memcpy_s(current, remainingSize, &cookie, olen) != EOK) { DHCP_LOGE("memcpy cookie out of options buffer!"); return RET_FAILED; } replyOptsLength += olen; remainingSize -= olen; current += olen; ret = RET_SUCCESS; while (pNode && (uint32_t)pNode->option.length < DHCP_OPTION_SIZE) { if ((uint32_t)pNode->option.code == END_OPTION) { olen = OPT_HEADER_LENGTH + 1; } else { olen = OPT_HEADER_LENGTH + pNode->option.length; } if (memcpy_s(current, remainingSize, &pNode->option, olen) != EOK) { DHCP_LOGE("memcpy current option out of options buffer!"); ret = RET_FAILED; break; } remainingSize -= olen; current += olen; replyOptsLength += olen; if ((uint32_t)pNode->option.code == END_OPTION) { break; } pNode = pNode->next; if (replyOptsLength >= DHCP_OPTIONS_SIZE) { DHCP_LOGE("current option out of options buffer!"); ret = RET_FAILED; break; } } reply->length += replyOptsLength; return ret; } void RegisterDhcpCallback(PDhcpServerContext ctx, DhcpServerCallback callback) { DHCP_LOGI("start %{public}s %{public}d.", __func__, __LINE__); ServerContext *srvIns = GetServerInstance(ctx); if (!srvIns) { DHCP_LOGE("dhcp server context pointer is null."); return; } srvIns->callback = callback; } void RegisterDeviceChangedCallback(PDhcpServerContext ctx, DeviceConnectFun func) { DHCP_LOGI("start %{public}s %{public}d.", __func__, __LINE__); ServerContext *srvIns = GetServerInstance(ctx); if (!srvIns) { DHCP_LOGE("dhcp server context pointer is null."); return; } srvIns->deviceConnectFun = func; } static int InitServerContext(DhcpConfig *config, DhcpServerContext *ctx) { if (!config) { DHCP_LOGE("server configure pointer is null."); return RET_FAILED; } ServerContext *srvIns = GetServerInstance(ctx); if (!srvIns) { DHCP_LOGE("dhcp server context pointer is null."); return RET_FAILED; } if (InitAddressPool(&srvIns->addressPool, config->ifname, nullptr) != RET_SUCCESS) { DHCP_LOGD("failed to init address pool."); return RET_FAILED; } if (memcpy_s(ctx->ifname, sizeof(ctx->ifname), config->ifname, strlen(config->ifname)) != EOK) { DHCP_LOGD("failed to set interface name."); return RET_FAILED; } srvIns->serverFd = 0; srvIns->callback = 0; srvIns->looperState = LS_IDLE; srvIns->broadCastFlagEnable = static_cast(config->broadcast); srvIns->addressPool.serverId = config->serverId; srvIns->addressPool.netmask = config->netmask; srvIns->addressPool.gateway = config->gateway; if (config->pool.beginAddress && config->pool.endAddress) { srvIns->addressPool.addressRange.beginAddress = config->pool.beginAddress; srvIns->addressPool.addressRange.endAddress = config->pool.endAddress; } else { srvIns->addressPool.addressRange.beginAddress = FirstIpAddress(config->serverId, config->netmask); srvIns->addressPool.addressRange.endAddress = LastIpAddress(config->serverId, config->netmask); } if (memcpy_s(srvIns->addressPool.ifname, sizeof(srvIns->addressPool.ifname), config->ifname, strlen(config->ifname)) != EOK) { DHCP_LOGD("failed to set interface name."); return RET_FAILED; } if (!CheckAddressRange(&srvIns->addressPool)) { DHCP_LOGE("failed to validate address range."); return RET_FAILED; } InitLeaseFile(&srvIns->addressPool); srvIns->addressPool.leaseTime = config->leaseTime; srvIns->addressPool.renewalTime = config->renewalTime; srvIns->addressPool.rebindingTime = config->rebindingTime; return RET_SUCCESS; } int InitServerFixedOptions(DhcpConfig *config, DhcpServerContext *ctx) { if (!config) { DHCP_LOGE("server configure pointer is null."); return RET_FAILED; } ServerContext *srvIns = GetServerInstance(ctx); if (!srvIns) { DHCP_LOGE("dhcp server context pointer is null."); return RET_FAILED; } if (!HasInitialized(&config->options)) { DHCP_LOGE("dhcp configure has not been initialized."); return RET_FAILED; } if (InitOptionList(&srvIns->addressPool.fixedOptions) != RET_SUCCESS) { return RET_FAILED; } if (config->options.first != nullptr && config->options.size > 0) { DhcpOptionNode *pNode = config->options.first->next; for (size_t i = 0; pNode != nullptr && i < config->options.size; i++) { PushBackOption(&srvIns->addressPool.fixedOptions, &pNode->option); DHCP_LOGD("append fixed option ==> %hhu,%d", pNode->option.code, pNode->option.length); pNode = pNode->next; } } return RET_SUCCESS; } PDhcpServerContext InitializeServer(DhcpConfig *config) { DHCP_LOGI("start %{public}s %{public}d.", __func__, __LINE__); DhcpServerContext *context = nullptr; if (!config) { DHCP_LOGE("dhcp server config pointer is null."); return nullptr; } if (strlen(config->ifname) == 0) { DHCP_LOGE("can't found interface name config."); return nullptr; } if (!config->serverId || !config->netmask) { DHCP_LOGE("missing required parameter or config item: \"serverId\", \"netmask\""); return nullptr; } if ((context = (DhcpServerContext *)calloc(1, sizeof(DhcpServerContext))) == nullptr) { DHCP_LOGE("failed to calloc server context."); return nullptr; } if ((context->instance = (ServerContext *)calloc(1, sizeof(ServerContext))) == nullptr) { DHCP_LOGE("failed to calloc server instance."); FreeServerContext(&context); return nullptr; } if (InitServerContext(config, context) != RET_SUCCESS) { DHCP_LOGE("failed initialize dhcp server context."); FreeServerContext(&context); return nullptr; } if (InitServerFixedOptions(config, context) != RET_SUCCESS) { DHCP_LOGE("failed initialize dhcp server fixed options."); FreeServerContext(&context); return nullptr; } DHCP_LOGI("server id: %{private}s", ParseStrIp(config->serverId)); DHCP_LOGI("netmask: %{private}s", ParseStrIp(config->netmask)); if (config->gateway) { DHCP_LOGI("gateway: %{private}s", ParseStrIp(config->gateway)); } DHCP_LOGI("address range begin of: %{private}s", ParseStrIp(config->pool.beginAddress)); DHCP_LOGI("address range end of: %{private}s", ParseStrIp(config->pool.endAddress)); context->instance->initialized = 1; return context; } int FreeServerContext(PDhcpServerContext *ctx) { if (ctx == nullptr || *ctx == nullptr) { DHCP_LOGE("dhcp server context pointer is null."); return RET_FAILED; } ServerContext *srvIns = GetServerInstance(*ctx); if (!srvIns) { DHCP_LOGE("dhcp server instance pointer is null."); return RET_FAILED; } int times = 5; while (srvIns->looperState != LS_STOPED && srvIns->looperState != LS_IDLE) { DHCP_LOGE("FreeServerContext wait 300ms."); usleep(300000); times--; if (times <= 0) { return RET_FAILED; } } FreeAddressPool(&srvIns->addressPool); if ((*ctx)->instance != nullptr) { free((*ctx)->instance); (*ctx)->instance = nullptr; } free(*ctx); *ctx = nullptr; return RET_SUCCESS; }