diff --git a/src/Cedar/Account.c b/src/Cedar/Account.c index 67f45e46..31b5397a 100644 --- a/src/Cedar/Account.c +++ b/src/Cedar/Account.c @@ -1318,3 +1318,40 @@ bool GetUserMacAddressFromUserNote(UCHAR *mac, wchar_t *note) return ret; } + +// Get the static IPv4 address from the user's note string +UINT GetUserIPv4AddressFromUserNote32(wchar_t *note) +{ + bool ret = false; + UINT ip32 = 0; + + UINT i = UniSearchStrEx(note, USER_IPV4_STR_PREFIX, 0, false); + if (i != INFINITE) + { + wchar_t *ipv4str_start = ¬e[i + UniStrLen(USER_IPV4_STR_PREFIX)]; + wchar_t ipv4str2[MAX_SIZE]; + UNI_TOKEN_LIST *tokens; + + UniStrCpy(ipv4str2, sizeof(ipv4str2), ipv4str_start); + UniTrim(ipv4str2); + + tokens = UniParseToken(ipv4str2, L" ,/()[]"); + if (tokens != NULL) + { + if (tokens->NumTokens >= 1) + { + wchar_t *ipv4str = tokens->Token[0]; + if (UniIsEmptyStr(ipv4str) == false) + { + char ipv4str_a[MAX_SIZE]; + UniToStr(ipv4str_a, sizeof(ipv4str_a), ipv4str); + ip32 = StrToIP32(ipv4str_a); + } + } + + UniFreeToken(tokens); + } + } + + return ip32; +} diff --git a/src/Cedar/Account.h b/src/Cedar/Account.h index 37a55c31..633e9919 100644 --- a/src/Cedar/Account.h +++ b/src/Cedar/Account.h @@ -9,6 +9,7 @@ #define ACCOUNT_H #define USER_MAC_STR_PREFIX L"MAC:" +#define USER_IPV4_STR_PREFIX L"IPv4:" // Policy item struct POLICY_ITEM @@ -205,7 +206,6 @@ POLICY_ITEM *GetPolicyItem(UINT id); void GetPolicyValueRangeStr(wchar_t *str, UINT size, UINT id); void FormatPolicyValue(wchar_t *str, UINT size, UINT id, UINT value); bool GetUserMacAddressFromUserNote(UCHAR *mac, wchar_t *note); +UINT GetUserIPv4AddressFromUserNote32(wchar_t *note); #endif // ACCOUNT_H - - diff --git a/src/Cedar/Session.c b/src/Cedar/Session.c index b48a3067..59a3d5a9 100644 --- a/src/Cedar/Session.c +++ b/src/Cedar/Session.c @@ -33,6 +33,8 @@ void SessionMain(SESSION *s) SOCK *nicinfo_sock = NULL; bool is_server_session = false; bool lock_receive_blocks_queue = false; + UINT static_ip = 0; + // Validate arguments if (s == NULL) { @@ -300,6 +302,13 @@ void SessionMain(SESSION *s) if (b->Size >= 14) { + UINT ip; + if( (ip = PrepareDHCPRequestForStaticIPv4( s, b )) != 0 ) + { + // Remember the static IP address to remove it from the leased IP address list later + static_ip = ip; + } + if (b->Buf[0] & 0x01) { if (is_server_session == false) @@ -603,6 +612,9 @@ CLEANUP: // Update the user information IncrementUserTraffic(s->Hub, s->UserNameReal, s); + // Clear the DHCP lease record if assigned as a static client IP address + ClearDHCPLeaseRecordForIPv4(s, static_ip); + DelSession(s->Hub, s); } @@ -2309,3 +2321,140 @@ void Notify(SESSION *s, UINT code) } +UINT PrepareDHCPRequestForStaticIPv4(SESSION *s, BLOCK *b) +{ + PKT *pkt = NULL; + DHCPV4_HEADER *dhcp = NULL; + UCHAR *data = NULL; + UINT size = 0; + UINT dhcp_header_size = 0; + UINT dhcp_data_offset = 0; + UINT magic_cookie = Endian32(DHCP_MAGIC_COOKIE); + DHCP_OPTION_LIST *opt = NULL; + USER *user = NULL; + UINT ret_ip = 0; + + if ((s->Username == NULL) || (StrLen(s->Username) == 0) || (StrCmpi(s->Username, SNAT_USER_NAME_PRINT) == 0) || + (StrCmpi( s->Username, BRIDGE_USER_NAME_PRINT) == 0) || (StrCmpi(s->Username, LINK_USER_NAME_PRINT) == 0)) + { + return ret_ip; + } + + pkt = ParsePacket(b->Buf, b->Size); + if (pkt == NULL) + { + return ret_ip; + } + + if (pkt->TypeL3 == L3_IPV4 && pkt->TypeL4 == L4_UDP && pkt->TypeL7 == L7_DHCPV4) + { + if (pkt->L7.DHCPv4Header->OpCode != 1) + { + goto CLEANUP_TP; + } + + dhcp = pkt->L7.DHCPv4Header; + dhcp_header_size = sizeof(DHCPV4_HEADER); + dhcp_data_offset = (UINT)(((UCHAR *)pkt->L7.DHCPv4Header) - ((UCHAR *)pkt->MacHeader) + dhcp_header_size); + data = ((UCHAR *)dhcp) + dhcp_header_size; + size = pkt->PacketSize - dhcp_data_offset; + + if (dhcp_header_size < 5) + { + goto CLEANUP_TP; + } + + // Search for Magic Cookie + while (size >= 5) + { + if (Cmp(data, &magic_cookie, sizeof(magic_cookie)) == 0) + { + // Found + data += 4; + size -= 4; + opt = ParseDhcpOptionList(data, size); + break; + } + + ++data; + --size; + } + + if (opt == NULL) + { + goto CLEANUP_TP; + } + + if (opt->Opcode == DHCP_DISCOVER || opt->Opcode == DHCP_REQUEST) + { + if (s->Hub != NULL) + { + user = AcGetUser( s->Hub, s->Username ); + if (user != NULL) + { + dhcp->ServerIP = GetUserIPv4AddressFromUserNote32(user->Note); + ReleaseUser(user); + if (s->Hub->SecureNAT != NULL && s->Hub->SecureNAT->Nat != NULL) + { + VH *v = s->Hub->SecureNAT->Nat->Virtual; + if (v != NULL && v->UseDhcp == true && v->DhcpLeaseList != NULL) + { + DHCP_LEASE *d = SearchDhcpLeaseByIp(v, dhcp->ServerIP); + + // The given static IP address is not used - it's OK + if (d == NULL) + { + ret_ip = dhcp->ServerIP; + } + } + } + } + } + } + } + +CLEANUP_TP: + if (opt != NULL) + { + Free(opt); + } + + if (pkt != NULL) + { + FreePacket(pkt); + } + + return ret_ip; +} + +void ClearDHCPLeaseRecordForIPv4(SESSION *s, UINT static_ip) +{ + if (s == NULL || static_ip == 0) + { + return; + } + + if (s->Hub == NULL || s->Hub->SecureNAT == NULL || s->Hub->SecureNAT->Nat == NULL) + { + return; + } + + VH *v = s->Hub->SecureNAT->Nat->Virtual; + if (v == NULL || v->DhcpLeaseList == NULL) + { + return; + } + + DHCP_LEASE *d = SearchDhcpLeaseByIp(v, static_ip); + if (d == NULL) + { + return; + } + + LockList(v->DhcpLeaseList); + { + FreeDhcpLease(d); + Delete(v->DhcpLeaseList, d); + } + UnlockList( v->DhcpLeaseList); +} diff --git a/src/Cedar/Session.h b/src/Cedar/Session.h index 1b687c3f..15dc2322 100644 --- a/src/Cedar/Session.h +++ b/src/Cedar/Session.h @@ -339,6 +339,9 @@ void CancelList(LIST *o); bool IsPriorityHighestPacketForQoS(void *data, UINT size); UINT GetNextDelayedPacketTickDiff(SESSION *s); +UINT PrepareDHCPRequestForStaticIPv4(SESSION *s, BLOCK *b); +void ClearDHCPLeaseRecordForIPv4(SESSION *s, UINT static_ip); + #endif // SESSION_H diff --git a/src/Cedar/Virtual.c b/src/Cedar/Virtual.c index d5c88d47..ddae4dea 100644 --- a/src/Cedar/Virtual.c +++ b/src/Cedar/Virtual.c @@ -9198,6 +9198,11 @@ PENDING_LIST_CLEANUP: // Correspond to the DHCP REQUEST UINT ServeDhcpRequest(VH *v, UCHAR *mac, UINT request_ip) +{ + return ServeDhcpRequestEx(v, mac, request_ip, false); +} + +UINT ServeDhcpRequestEx(VH *v, UCHAR *mac, UINT request_ip, bool is_static_ip) { UINT ret; // Validate arguments @@ -9206,7 +9211,7 @@ UINT ServeDhcpRequest(VH *v, UCHAR *mac, UINT request_ip) return 0; } - ret = ServeDhcpDiscover(v, mac, request_ip); + ret = ServeDhcpDiscoverEx(v, mac, request_ip, is_static_ip); if (ret != request_ip) { if (request_ip != 0) @@ -9309,6 +9314,34 @@ UINT ServeDhcpDiscover(VH *v, UCHAR *mac, UINT request_ip) return ret; } +UINT ServeDhcpDiscoverEx(VH *v, UCHAR *mac, UINT request_ip, bool is_static_ip) +{ + if (is_static_ip == false) + { + return ServeDhcpDiscover(v, mac, request_ip ); + } + + if (v == NULL || mac == NULL || request_ip == 0) + { + return 0; + } + + DHCP_LEASE *d = SearchDhcpLeaseByIp(v, request_ip); + if (d != NULL) + { + // The requested IP address is used already + return 0; + } + + // For static IP, the requested IP address must NOT be within the range of the DHCP pool + if (Endian32(request_ip) < Endian32(v->DhcpIpStart) || Endian32(request_ip) > Endian32(v->DhcpIpEnd)) + { + return request_ip; + } + + return 0; +} + // Take an appropriate IP addresses that can be assigned newly UINT GetFreeDhcpIpAddress(VH *v) { @@ -9469,21 +9502,30 @@ void VirtualDhcpServer(VH *v, PKT *p) if (dhcp->OpCode == 1 && (opt->Opcode == DHCP_DISCOVER || opt->Opcode == DHCP_REQUEST || opt->Opcode == DHCP_INFORM)) { // Operate as the server - UINT ip = 0; + UINT ip = 0, ip_static = dhcp->ServerIP; + dhcp->ServerIP = 0; if (opt->RequestedIp == 0) { - opt->RequestedIp = p->L3.IPv4Header->SrcIP; + opt->RequestedIp = (ip_static ? ip_static : p->L3.IPv4Header->SrcIP); } if (opt->Opcode == DHCP_DISCOVER) { // Return an IP address that can be used - ip = ServeDhcpDiscover(v, p->MacAddressSrc, opt->RequestedIp); + ip = ServeDhcpDiscoverEx(v, p->MacAddressSrc, opt->RequestedIp, ip_static); } else if (opt->Opcode == DHCP_REQUEST) { // Determine the IP address - ip = ServeDhcpRequest(v, p->MacAddressSrc, opt->RequestedIp); + if (ip_static && opt->RequestedIp != ip_static) + { + // Don't allow opt->RequestedIp other than the IP written in user's note + ip = 0; + } + else + { + ip = ServeDhcpRequestEx(v, p->MacAddressSrc, opt->RequestedIp, ip_static); + } } if (ip != 0 || opt->Opcode == DHCP_INFORM) diff --git a/src/Cedar/Virtual.h b/src/Cedar/Virtual.h index 5577e1ad..f4086cbe 100644 --- a/src/Cedar/Virtual.h +++ b/src/Cedar/Virtual.h @@ -514,9 +514,11 @@ DHCP_LEASE *SearchDhcpPendingLeaseByMac(VH *v, UCHAR *mac); DHCP_LEASE *SearchDhcpLeaseByIp(VH *v, UINT ip); DHCP_LEASE *SearchDhcpPendingLeaseByIp(VH *v, UINT ip); UINT ServeDhcpDiscover(VH *v, UCHAR *mac, UINT request_ip); +UINT ServeDhcpDiscoverEx(VH *v, UCHAR *mac, UINT request_ip, bool is_static_ip); UINT GetFreeDhcpIpAddress(VH *v); UINT GetFreeDhcpIpAddressByRandom(VH *v, UCHAR *mac); UINT ServeDhcpRequest(VH *v, UCHAR *mac, UINT request_ip); +UINT ServeDhcpRequestEx(VH *v, UCHAR *mac, UINT request_ip, bool is_static_ip); void VirtualDhcpSend(VH *v, UINT tran_id, UINT dest_ip, UINT dest_port, UINT new_ip, UCHAR *client_mac, BUF *b, UINT hw_type, UINT hw_addr_size); void VLog(VH *v, char *str); @@ -587,7 +589,4 @@ void NnDeleteOldestNatSessionIfNecessary(NATIVE_NAT *t, UINT ip, UINT protocol); void NnSetSecureNatTargetHostname(char *name); - #endif // VIRTUAL_H - -