..back /*****************************************************************************/ /** Microsoft Windows **/ /** Copyright (c) 1997-2000 Microsoft Corporation. All rights reserved. **/ /*****************************************************************************/ /* packet.c DESCRIPTION: DHCP packet routines */ #include "dhcpp.h" #include "dhcp.h" #include "protocol.h" #ifdef DEBUG LPWSTR PacketType( uchar Type ) { switch (Type) { case DHCPDISCOVER: return L"DISCOVER"; case DHCPREQUEST: return L"REQUEST"; case DHCPINFORM: return L"INFORM"; case DHCPDECLINE: return L"DECLINE"; case DHCPRELEASE: return L"RELEASE"; default: return L"UNKNOWN"; } } #endif void BuildDhcpPkt(DhcpInfo *pDhcp, DhcpPkt *pPkt, uchar Type, int Flags, uchar *aOptionList, int *pcPkt) { int Cookie = MAGIC_COOKIE; uchar *p, *pEnd; DEBUGMSG(ZONE_INIT, (TEXT("+BuildDhcpPkt: %s pDhcp 0x%X/0x%X\r\n"), PacketType(Type), pDhcp, pDhcp->Socket)); pEnd = pPkt->aOptions + OPTIONS_LEN; // byte right after end of pkt if (Flags & NEW_PKT_FL) { pPkt->Op = 1; pPkt->Htype = 1; // 10Mb Ethernet pPkt->Hlen = ETH_ADDR_LEN; pPkt->Hops = 0; // now we getXid here - OM pPkt->Xid = GetXid(pDhcp->PhysAddr); pPkt->Xid = net_long(pPkt->Xid); // Secs, Flags, Yiaddr, Siaddr, Giaddr should all be set to 0 pPkt->Secs = pPkt->Flags = pPkt->Ciaddr = pPkt->Yiaddr = pPkt->Siaddr = pPkt->Giaddr = 0; // erase any previous value memset(pPkt->aChaddr, 0, CHADDR_LEN + SNAME_LEN + FILE_LEN); memcpy(pPkt->aChaddr, pDhcp->PhysAddr, ETH_ADDR_LEN); memcpy(pPkt->aOptions, &Cookie, 4); } if ((Flags & RIP_PKT_FL) == 0) pPkt->Ciaddr = pDhcp->IPAddr; p = pPkt->aOptions + 4; // Set the Message Type *p++ = DHCP_MSG_TYPE_OP; *p++ = 1; // option length *p++ = Type; switch(Type) { case DHCPREQUEST: // Server Identifier if (Flags & SID_PKT_FL) { *p++ = DHCP_SERVER_ID_OP; *p++ = 4; memcpy(p, &pDhcp->DhcpServer, 4); p += 4; } // no break, fall-thru case DHCPDISCOVER: // Requested Addr if (pDhcp->IPAddr && (Flags & RIP_PKT_FL)) { *p++ = DHCP_REQ_IP_OP; *p++ = 4; memcpy(p, &pDhcp->IPAddr, 4); p += 4; } // IP address lease should also be done before DHCPINFORM // no break, fall-thru case DHCPINFORM: // Set the Client Id *p++ = DHCP_CLIENT_ID_OP; *p++ = 7; // op len *p++ = 1; // Type - 10Mb Ethernet memcpy(p, &pDhcp->PhysAddr, ETH_ADDR_LEN); p += ETH_ADDR_LEN; /* Host Name Option *p++ = DHCP_HOST_NAME_OP; *p++ = 0; // len of name memcpy(); */ // Parameter Request List if (aOptionList && aOptionList[0] && aOptionList[0] <= (pEnd - p)) { *p++ = DHCP_PARAMETER_REQ_OP; memcpy (p, aOptionList, aOptionList[0] + 1); // +1 for len p += aOptionList[0] + 1; // +1 for length } if (pDhcp->pSendOptions) { memcpy(p, pDhcp->pSendOptions, pDhcp->cSendOptions); p += pDhcp->cSendOptions; } // End Option *p++ = DHCP_END_OP; break; case DHCPDECLINE: // Requested Addr *p++ = DHCP_REQ_IP_OP; *p++ = 4; memcpy(p, &pDhcp->IPAddr, 4); p += 4; // no break, fall-thru case DHCPRELEASE: // Set the Client Id *p++ = DHCP_CLIENT_ID_OP; *p++ = 7; // op len *p++ = 1; // Type - 10Mb Ethernet memcpy(p, pDhcp->PhysAddr, ETH_ADDR_LEN); p += ETH_ADDR_LEN; // Server Identifier *p++ = DHCP_SERVER_ID_OP; *p++ = 4; memcpy(p, &pDhcp->DhcpServer, 4); p += 4; // we should also do Message Option // End Option *p++ = DHCP_END_OP; break; default: DEBUGMSG(ZONE_ERROR, (TEXT("\t!BuildDhcpPkt: unknown packet type (%d) requested\r\n"), Type)); break; } // switch(Type) if (p < pEnd) memset(p, DHCP_PAD_OP, pEnd - p); if (pcPkt) *pcPkt = p - (char *)pPkt; DEBUGMSG(ZONE_INIT, (TEXT("-BuildDhcpPkt: pDhcp 0x%X/0x%X Len %d\r\n"), pDhcp, pDhcp->Socket, (p - (char *)pPkt))); } // BuildDhcpPkt() void Tvert(uchar c, TCHAR *p) { if (c >= 100) p++; if (c >= 10) p++; *(p+1) = TEXT('\0'); do { *p-- = TEXT('0') + (c % 10); } while (c /= 10); } #define MIN_LEASE_TIME 1 STATUS TranslatePkt(DhcpInfo *pDhcp, DhcpPkt *pPkt, int cPkt, uchar *pType, unsigned int Xid) { STATUS Status = DHCP_FAILURE; // assume failure int i, fSaveReg, Cookie = MAGIC_COOKIE; uchar *p, Type = 0; TCHAR szBuf[MAX_REG_STR]; HKEY hKey; LONG hRes; DWORD cData; uint NewSubnetMask, fSubnetMask; uint NewGateway, fGateway; uint NewLease, fLease; uint NewServer, fServer; uint NewT1, fT1; uint NewT2, fT2; uint NewDNS[2], fDNS; uint NewWins[2], fWins; uchar * pnew; DEBUGMSG(ZONE_RECV, (TEXT("DHCP:+TranslatePkt: pDhcp %X: OP %d, Htype %d, Xid %x/%x\r\n"), pDhcp, pPkt->Op, pPkt->Htype, pPkt->Xid, Xid)); fSubnetMask = fGateway = fLease = fServer = fT1 = fT2 = fDNS = fWins = fSaveReg = FALSE; if (cPkt >= (sizeof(*pPkt) - OPTIONS_LEN + 4) && 2 == pPkt->Op && 1 == pPkt->Htype && Xid == pPkt->Xid && 0 == memcmp(&Cookie, pPkt->aOptions, 4) && ((pDhcp->Flags & NO_MAC_COMPARE_FL) || (pPkt->Hlen == 6 && (0 == memcmp(pDhcp->PhysAddr, pPkt->aChaddr, 6))))) { pDhcp->SFlags |= DHCPSTATE_SAW_DHCP_SRV; // There is a DHCP server on the net. // // Scan through the options once without storing config options so we can // perform some sanity checks first. // (The number of options checked will increase as we get more input from QA and customers) // p = &pPkt->aOptions[4]; while (p < (char *)pPkt + cPkt) { i = 4; pnew = NULL; switch (*p) { // // These are the 4 byte (i=4) cases // case DHCP_SUBNET_OP: pnew = (uchar *) &NewSubnetMask; fSubnetMask++; break; case DHCP_IP_LEASE_OP: pnew = (uchar *) &NewLease; fLease++; break; case DHCP_SERVER_ID_OP: pnew = (uchar *) &NewServer; fServer++; break; case DHCP_ROUTER_OP: pnew = (uchar *) &NewGateway; fGateway++; break; case DHCP_T1_VALUE_OP: pnew = (uchar *) &NewT1; fT1++; break; case DHCP_T2_VALUE_OP: pnew = (uchar *) &NewT2; fT2++; break; // // Up to 8 byte options // case DHCP_DNS_OP: NewDNS[0] = 0; NewDNS[1] = 0; if ((i = p[1]) > 8) { i = 8; } pnew = (uchar *)NewDNS; fDNS++; break; case DHCP_NBT_SRVR_OP: NewWins[0] = 0; NewWins[1] = 0; if ((i = p[1]) > 8) { i = 8; } pnew = (uchar *)NewWins; fWins++; break; // // Special cases // case DHCP_MSG_TYPE_OP: Type = p[2]; if (*pType && Type != *pType) { Status = DHCP_SUCCESS; DEBUGMSG(ZONE_RECV, (TEXT("DHCP:TranslatePkt: Type = %d, expecting %d\n"), Type, *pType)); goto BreakLoop; } break; case DHCP_MSFT_AUTOCONF: if (p[1] != 1) { DEBUGMSG(ZONE_RECV, (TEXT("DHCP:TranslatePkt: AutoConfig Option Len = %d, expecting 1\n"), p[1])); goto BreakLoop; } break; case DHCP_PAD_OP: p++; continue; break; case DHCP_END_OP: goto VerifyOptions; break; } if (pnew) { // verify length if (p + i > (char *)pPkt + cPkt) { DEBUGMSG(ZONE_RECV, (TEXT("DHCP:TranslatePkt: Length %d too long for option %d\n"), i, *p)); goto BreakLoop; } memcpy(pnew, p+2, i); } p += p[1] + 2; } VerifyOptions: // // Make sure config options seem reasonable // if (fSubnetMask) { if (NewSubnetMask == 0) { DEBUGMSG(ZONE_RECV, (TEXT("DHCP:TranslatePkt: Invalid subnet mask %d\n"), NewSubnetMask)); goto BreakLoop; } } if (fLease) { if (NewLease < MIN_LEASE_TIME && Type == DHCPACK) { DEBUGMSG(ZONE_RECV, (TEXT("DHCP:TranslatePkt: Invalid lease time %d < %d\n"), NewLease, MIN_LEASE_TIME)); goto BreakLoop; } } else { if (Type == DHCPACK) { DEBUGMSG(ZONE_RECV, (TEXT("DHCP:TranslatePkt: No lease specified in DHCPACK!\n"))); goto BreakLoop; } } if (fServer) { if (NewServer == 0) { DEBUGMSG(ZONE_RECV, (TEXT("DHCP:TranslatePkt: Invalid server id %d\n"), NewServer)); goto BreakLoop; } } // // Make sure the ipaddr and subnet mask match if this is a renewal // if (*pType == DHCPACK && pDhcp->IPAddr) { if (pDhcp->IPAddr != pPkt->Yiaddr) { DEBUGMSG(ZONE_RECV,(TEXT("DHCP:TranslatePkt: Mismatch in IP addr - rcvd 0x%X, expect 0x%X\r\n"), pPkt->Yiaddr,pDhcp->IPAddr)); goto BreakLoop; } if (pDhcp->SubnetMask != NewSubnetMask) { DEBUGMSG(ZONE_RECV,(TEXT("DHCP:TranslatePkt: Mismatch in subnet - rcvd 0x%X, expect 0x%X\r\n"), NewSubnetMask,pDhcp->SubnetMask)); goto BreakLoop; } } // // Open registry key to save new values // if (DHCPACK == Type) { _tcscpy (szBuf, COMM_REG_KEY); _tcscat (szBuf, pDhcp->Name); _tcscat (szBuf, TEXT("\\Parms\\TcpIp\\DhcpOptions")); hRes = RegOpenKeyEx (HKEY_LOCAL_MACHINE, szBuf, 0, 0, &hKey); if (ERROR_SUCCESS == hRes) { DEBUGMSG(ZONE_RECV, (TEXT("DHCP:TranslatePacket: Save in registry\r\n"))); fSaveReg = TRUE; } } // // Now run through the options again and store away results // p = &pPkt->aOptions[4]; while (p < (char *)pPkt + cPkt) { switch(*p) { case DHCP_PAD_OP: p++; continue; break; case DHCP_END_OP: Status = DHCP_SUCCESS; goto BreakLoop; break; case DHCP_SUBNET_OP: pDhcp->SubnetMask = NewSubnetMask; break; case DHCP_ROUTER_OP: pDhcp->Gateway = NewGateway; break; case DHCP_DNS_OP: pDhcp->DNS[0] = NewDNS[0]; pDhcp->DNS[1] = NewDNS[1]; break; case DHCP_DOMAIN_NAME_OP: if (p[1] > MAX_DOMAIN_NAME) p[1] = (uchar)MAX_DOMAIN_NAME; memcpy(pDhcp->aDomainName, p+1, p[1] + 1); break; case DHCP_NBT_SRVR_OP: pDhcp->WinsServer[0] = NewWins[0]; pDhcp->WinsServer[1] = NewWins[1]; break; case DHCP_IP_LEASE_OP: pDhcp->Lease = net_long(NewLease); break; case DHCP_SERVER_ID_OP: pDhcp->DhcpServer = NewServer; break; case DHCP_T1_VALUE_OP: pDhcp->T1 = net_long(NewT1); break; case DHCP_T2_VALUE_OP: pDhcp->T2 = net_long(NewT2); break; case DHCP_MSFT_AUTOCONF: if (p[2]) { pDhcp->Flags |= AUTO_IP_ENABLED_FL; } else { pDhcp->Flags &= ~AUTO_IP_ENABLED_FL; } break; case DHCP_OVERLOAD_OP: ASSERT(0); break; case DHCP_MSG_TYPE_OP: case DHCP_MESSAGE_OP: // NYI case DHCP_NBT_NODE_TYPE_OP: // NYI case DHCP_HOST_NAME_OP: // NYI break; default: DEBUGMSG(ZONE_RECV, (TEXT("DHCP:TranslatePkt: Unexpected option %d\r\n"), *p)); break; } // switch(*p) if (fSaveReg) { Tvert(*p, szBuf); cData = 0; hRes = RegQueryValueEx(hKey, szBuf, NULL, NULL, NULL, &cData); if (ERROR_SUCCESS == hRes && cData) SetRegBinaryValue(hKey, szBuf, p, p[1] + 2); } p += p[1] + 2; } // while (...) DEBUGMSG(ZONE_RECV, (TEXT("DHCP:TranslatePkt: No DHCP_END_OP in packet!!\n"))); BreakLoop: if (Status == DHCP_SUCCESS && (Type == DHCPOFFER || Type == DHCPACK)) { if (fLease) { if (! fT1) pDhcp->T1 = pDhcp->Lease >> 1; // default half lease time if (! fT2) pDhcp->T2 = (pDhcp->Lease * 7) >> 3; // dft: 7/8 lease time memcpy(&pDhcp->IPAddr, &pPkt->Yiaddr, 4); } } } else { #if DEBUG if (cPkt < (sizeof(*pPkt) - OPTIONS_LEN + 4)) DEBUGMSG(ZONE_RECV, (TEXT("DHCP:Translate: packet too small %d\r\n"), cPkt)); // we can continue--since pPkt is a ptr to our own packet which we know is // larger than the minimum dhcp packet if (2 != pPkt->Op) DEBUGMSG(ZONE_RECV, (TEXT("DHCP:TranslatePkt: Op %d != 2\n"), pPkt->Op)); if (1 != pPkt->Htype) DEBUGMSG(ZONE_RECV, (TEXT("DHCP:TranslatePkt: Htype %d != 1\n"), pPkt->Htype)); if (Xid != pPkt->Xid) DEBUGMSG(ZONE_RECV, (TEXT("DHCP:TranslatePkt: Xid 0x%x != 0x%x\n"), pPkt->Xid, Xid)); if (0 != memcmp(&Cookie, pPkt->aOptions, 4)) DEBUGMSG(ZONE_RECV, (TEXT("DHCP:TranslatePkt: Cookie %B%B%B%B != %X\n"), pPkt->aOptions[3], pPkt->aOptions[2], pPkt->aOptions[1], pPkt->aOptions[0], Cookie)); #endif } if (fSaveReg) RegCloseKey(hKey); if (pType) *pType = Type; DEBUGMSG(ZONE_RECV, (TEXT("DHCP:-TranslatePkt: pDhcp %X Type %d, Ret: %d\r\n"), pDhcp, Type, Status)); return Status; } // TranslatePkt() STATUS DhcpSendDown(DhcpInfo *pDhcp, DhcpPkt *pPkt, int cPkt, int DestIP) { SOCKADDR_IN SockAddr; STATUS Status; int cSent; if (cPkt < 300) cPkt = 300; memset(&SockAddr, 0, sizeof(SockAddr)); SockAddr.sin_family = AF_INET; SockAddr.sin_port = net_short(DHCP_SERVER_PORT); SockAddr.sin_addr.S_un.S_addr = DestIP; Status = CallSock(Send)(pDhcp->Socket, (char *)pPkt, cPkt, 0, (SOCKADDR *)&SockAddr, sizeof(SockAddr), &cSent, NULL); DEBUGMSG(ZONE_SEND, (TEXT("*DhcpSendDown: Dest %d/%x, Sent %d/%d Status %d\r\n"), DHCP_SERVER_PORT, pDhcp->IPAddr, cSent, cPkt, Status)); return Status; } // DhcpSendDown() #define MAX_LOOP 4 STATUS SendDhcpPkt(DhcpInfo *pDhcp, DhcpPkt *pPkt, int cPkt, uchar WaitforType, int Flags) { STATUS Status = DHCP_FAILURE; int cLoop, cTimeLeft, cTimeout, cMultiplier; int cFuzz = 0; DWORD cBeginTime, cZeroTime; struct timeval Timeval; LPSOCK_INFO Sock; SOCK_LIST ReadList; DhcpPkt RcvPkt; int cRcvd, cRcvPkt; uchar Type; int fSend = TRUE; int cMaxLoop; int DestIP; DEBUGMSG(ZONE_SEND, (TEXT("+SendDhcpPkt: pDhcp %X pPkt %X\r\n"), pDhcp, pPkt)); Sock = pDhcp->Socket; if (BCAST_FL & Flags) DestIP = INADDR_BROADCAST; else DestIP = pDhcp->DhcpServer; cRcvPkt = sizeof(RcvPkt); CTEFreeLock(&pDhcp->Lock, 0); cMaxLoop = Flags & LOOP_MASK_FL; cLoop = 0; if (pDhcp->Flags & AUTO_IP_ENABLED_FL) { cTimeout = 1500; cMultiplier = 1; } else { cTimeout = 4000; cMultiplier = 2; } cZeroTime = GetTickCount(); // add random fuzz of +/- 1 sec. cFuzz = (GetTickCount() % 2000) - 1000; cTimeLeft = cTimeout + cFuzz; pDhcp->SFlags &= ~DHCPSTATE_SAW_DHCP_SRV; do { cBeginTime = GetTickCount(); if (fSend) { pPkt->Secs = (unsigned short)((cBeginTime - cZeroTime) / 1000); if (Status = DhcpSendDown(pDhcp, pPkt, cPkt, DestIP)) break; } // wait only 8 secs (2 x min time) after last packet if ((cLoop != 1) && ((cLoop + 1) == cMaxLoop) && (cTimeLeft > 8000)) cTimeLeft = 8000 + cFuzz; ASSERT((cTimeLeft >= 0) && (cTimeLeft < 62000)); if (cTimeLeft < 0) cTimeLeft = 0; if (cTimeLeft > 62000) cTimeLeft = 62000; // do select w/ timeout Timeval.tv_sec = cTimeLeft / 1000; Timeval.tv_usec = (cTimeLeft % 1000)*1000; ReadList.hSocket = 0; ReadList.Socket = Sock; ReadList.EventMask = READ_EVENTS; Status = CallAfd(Select)(1, &ReadList, 0, NULL, 0, NULL, &Timeval, NULL); if (Status) break; if (ReadList.Socket) { // there is data cRcvPkt = sizeof(RcvPkt); Status = CallSock(Recv)(Sock, (char *)&RcvPkt, cRcvPkt, 0, NULL, NULL, &cRcvd, NULL); if (Status) { DEBUGMSG(ZONE_SEND, (TEXT("SendDhcpPkt: Recv() failed %d\n"), Status)); break; } Type = WaitforType; Status = TranslatePkt(pDhcp, &RcvPkt, cRcvd, &Type, pPkt->Xid); if (DHCP_SUCCESS == Status) { if (Type == WaitforType) break; else if (DHCPNACK == Type) { Status = DHCP_NACK; break; } } // this should work as long as the difference is not huge cTimeLeft -= GetTickCount() - cBeginTime; } else { // Select timed out DEBUGMSG(ZONE_SEND, (TEXT("SendDhcpPkt: Select() timed out\n"))); Status = DHCP_FAILURE; cTimeLeft = 0; } if (cTimeLeft <= 0) { cFuzz = (GetTickCount() % 2000) - 1000; cTimeLeft = (cTimeout *= cMultiplier) + cFuzz; cLoop++; fSend = TRUE; } else fSend = FALSE; } while (cLoop < cMaxLoop); DEBUGMSG(ZONE_SEND, (TEXT("-SendDhcpPkt: pDhcp %X pPkt %X Ret: %d\r\n"), pDhcp, pPkt, Status)); CTEGetLock(&pDhcp->Lock, 0); return Status; } // SendDhcpPkt()


Legal Declaration: it is for studying wince(MicroSoft Windows CE) only! : http://www.vxworks6.com