/* * PXE daemon - enable the remote booting of PXE enabled machines. * Copyright (C) 2000 Tim Hurman (kano@kano.org.uk) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ /****************************************************************************** * packetstore.cc - decode and store a packet in memory * ******************************************************************************/ #include "packetstore.h" /* some global variables */ const char *DHCP_types[DHCP_MAX_TYPES] = { "INVALID", "DHCPDISCOVER", "DHCPOFFER", "DHCPREQUEST", "DHCPDECLINE", "DHCPACK", "DHCPNACK", "DHCPRELEASE", "DHCPINFORM" }; /****************************************************************************** * PacketStore - null packet constructor * ******************************************************************************/ PacketStore::PacketStore(LogFile *_logger) { this->logger = _logger; head = head43 = NULL; Initalise(); } /****************************************************************************** * PacketStore - full packet analyse * ******************************************************************************/ PacketStore::PacketStore(LogFile *_logger, struct sockaddr_in *_address, uint8_t *bootp_pkt, int bootp_pkt_len) { this->logger = _logger; memcpy(&(this->address), _address, sizeof(this->address)); ReadPacket(bootp_pkt, bootp_pkt_len); } /****************************************************************************** * ~PacketStore - descructor * ******************************************************************************/ PacketStore::~PacketStore() { Initalise(); } /****************************************************************************** * Initalise - init/blank a packet * ******************************************************************************/ void PacketStore::Initalise(void) { option *optptr, *optnext; // basic stuff op=htype=hlen=hops=0; secs=0; xid=0; memset(chaddr, 0, 16); sname[0] = file[0] = 0; magic_cookie = 0; memset(&ciaddr, 0, sizeof(ciaddr)); memset(&yiaddr, 0, sizeof(yiaddr)); memset(&siaddr, 0, sizeof(siaddr)); memset(&giaddr, 0, sizeof(giaddr)); memset(&address, 0, sizeof(giaddr)); // options for(optptr=head; optptr != NULL; optptr=optnext) { optnext = optptr->next; delete[] optptr->data; delete optptr; } head = NULL; // options 43 for(optptr=head43; optptr != NULL; optptr=optnext) { optnext = optptr->next; delete[] optptr->data; delete optptr; } head43 = NULL; } /****************************************************************************** * ReadPacket - read a bootp packet * ******************************************************************************/ int PacketStore::ReadPacket(uint8_t *bootp_pkt, int bootp_pkt_len) { if(bootp_pkt_len < 300) throw new SysException(0, "PacketStore::ReadPacket", "Invalid packet length"); op = bootp_pkt[0]; htype = bootp_pkt[1]; hlen = bootp_pkt[2]; hops = bootp_pkt[3]; // xid - store in network byte order xid = bootp_pkt[4]; xid <<= 8; xid |= bootp_pkt[5]; xid <<= 8; xid |= bootp_pkt[6]; xid <<= 8; xid |= bootp_pkt[7]; xid = htonl(xid); // no of secs secs = bootp_pkt[8]; secs <<= 8; secs |= bootp_pkt[9]; secs = htons(secs); // client IP addr (filled in by client) ciaddr.s_addr = bootp_pkt[12]; ciaddr.s_addr <<= 8; ciaddr.s_addr |= bootp_pkt[13]; ciaddr.s_addr <<= 8; ciaddr.s_addr |= bootp_pkt[14]; ciaddr.s_addr <<= 8; ciaddr.s_addr |= bootp_pkt[15]; ciaddr.s_addr = htonl(ciaddr.s_addr); // client IP addr (filled in by server) yiaddr.s_addr = bootp_pkt[16]; yiaddr.s_addr <<= 8; yiaddr.s_addr |= bootp_pkt[17]; yiaddr.s_addr <<= 8; yiaddr.s_addr |= bootp_pkt[18]; yiaddr.s_addr <<= 8; yiaddr.s_addr |= bootp_pkt[19]; yiaddr.s_addr = htonl(yiaddr.s_addr); // server IP address (filled in by server) siaddr.s_addr = bootp_pkt[20]; siaddr.s_addr <<= 8; siaddr.s_addr |= bootp_pkt[21]; siaddr.s_addr <<= 8; siaddr.s_addr |= bootp_pkt[22]; siaddr.s_addr <<= 8; siaddr.s_addr |= bootp_pkt[23]; siaddr.s_addr = htonl(siaddr.s_addr); // gateway IP address giaddr.s_addr = bootp_pkt[24]; giaddr.s_addr <<= 8; giaddr.s_addr |= bootp_pkt[25]; giaddr.s_addr <<= 8; giaddr.s_addr |= bootp_pkt[26]; giaddr.s_addr <<= 8; giaddr.s_addr |= bootp_pkt[27]; giaddr.s_addr = htonl(giaddr.s_addr); // client hardware address memcpy(chaddr, bootp_pkt+28, 16); // the servername if(bootp_pkt[107] != 0) throw new SysException(0, "PacketStore::ReadPacket", "Invalid servername"); strcpy(sname, (char*)bootp_pkt+44); // the file if(bootp_pkt[235] != 0) throw new SysException(0, "PacketStore::ReadPacket", "Invalid filename"); strcpy(file, (char*)bootp_pkt+108); // the magic cookie magic_cookie = bootp_pkt[236]; magic_cookie <<= 8; magic_cookie |= bootp_pkt[237]; magic_cookie <<= 8; magic_cookie |= bootp_pkt[238]; magic_cookie <<= 8; magic_cookie |= bootp_pkt[239]; magic_cookie = htonl(magic_cookie); if(bootp_pkt[236] == 0xff) return(0); // read the options head=head43=NULL; return(ReadOptions(bootp_pkt+240, bootp_pkt_len-240)); } /****************************************************************************** * print the packet to stdout * ******************************************************************************/ std::ostream& operator<< (std::ostream& os, PacketStore &pkt) { int i; option *optptr; // basic info os << "BOOTP type : " << (int)pkt.op << "\n"; os << "Hardware type : " << (int)pkt.htype << "\n"; os << "Hardware Length : " << (int)pkt.hlen << "\n"; os << "Hops : " << (int)pkt.hops << "\n"; os << "Transaction ID : 0x" << std::hex << ntohl(pkt.xid) << std::dec <<"\n"; os << "Seconds : " << ntohs(pkt.secs) << "\n"; os << "Client IP (From Client): " << inet_ntoa(pkt.ciaddr) << "\n"; os << "Client IP (From Server): " << inet_ntoa(pkt.yiaddr) << "\n"; os << "Server IP : " << inet_ntoa(pkt.siaddr) << "\n"; os << "Gateway IP : " << inet_ntoa(pkt.giaddr) << "\n"; os << "Client Hardware address: " ; // client hardware addr for(i=0; inext) { os << "Option major:" << (int)optptr->major_no << " minor: " << (int)optptr->minor_no << ", Length: " << (int)optptr->len << ", Data: "; for(i=0; ilen; i++) if((optptr->data[i] >0x20) && (optptr->data[i] < 0x7f)) os << "[" << (int)optptr->data[i] << "," << std::hex << (int)optptr->data[i] << std::dec << "," << (char)optptr->data[i] << "] "; else os << "[" << (int)optptr->data[i] << "," << std::hex << (int)optptr->data[i] << std::dec << ", ] "; os << "\n"; } if(pkt.head43 == NULL) return os; // option 43 os << "Options (sub-packed) :\n"; for(optptr = pkt.head43; optptr != NULL; optptr = optptr->next) { os << "Option [43] " << (int)optptr->major_no << " minor: " << (int)optptr->minor_no << ", Length: " << (int)optptr->len << ", Data: "; for(i=0; ilen; i++) if((optptr->data[i] >0x20) && (optptr->data[i] < 0x7f)) os << "[" << (int)optptr->data[i] << "," << std::hex << (int)optptr->data[i] << std::dec << "," << (char)optptr->data[i] << "] "; else os << "[" << (int)optptr->data[i] << "," << std::hex << (int)optptr->data[i] << std::dec << ", ] "; os << "\n"; } return os; } /****************************************************************************** * ReadOptions - read all the options contained within a packet * ******************************************************************************/ int PacketStore::ReadOptions(uint8_t *bootp_pkt, int bootp_pkt_len) { // everything will always be null here int pos; option *optptr = NULL; option *optprev = NULL; for(pos=0; pos < bootp_pkt_len; pos++) { if(bootp_pkt[pos] == 0) goto ReadOptions_next; // pad if(bootp_pkt[pos] == 255) break; // end options // lots of sub options if(bootp_pkt[pos] == 43) { ReadOptions43(bootp_pkt+pos+2, bootp_pkt[pos+1]); pos += bootp_pkt[pos+1]+1; goto ReadOptions_next; // next } // default optptr = new option; optptr->major_no = bootp_pkt[pos++]; optptr->minor_no = 0; optptr->len = bootp_pkt[pos++]; optptr->data = new uint8_t[optptr->len]; optptr->next = NULL; memcpy(optptr->data, bootp_pkt+pos, optptr->len); // increment pos, accounting to cyclic increment pos += (optptr->len-1); if(head != NULL) { optprev->next = optptr; // assign optprev = optprev->next; // advance } else optprev = head = optptr; ReadOptions_next: pos=pos; } return(0); } /****************************************************************************** * ReadOptions43 - read all the options contained within a packet * ******************************************************************************/ int PacketStore::ReadOptions43(uint8_t *bootp_pkt, int bootp_pkt_len) { // everything will always be null here int pos; option *optptr=NULL; option *optprev=NULL; for(pos=0; pos < bootp_pkt_len; pos++) { if(bootp_pkt[pos] == 0) goto ReadOptions43_next; // pad if(bootp_pkt[pos] == 255) break; // end options // lots of sub options if(bootp_pkt[pos] == 43) { std::cerr << "Option 43 detected\n"; break; } // default optptr = new option; optptr->major_no = 43; optptr->minor_no = bootp_pkt[pos++]; optptr->len = bootp_pkt[pos++]; optptr->data = new uint8_t[optptr->len]; optptr->next = NULL; memcpy(optptr->data, bootp_pkt+pos, optptr->len); // increment pos, accounting to cyclic increment pos += (optptr->len-1); if(head43 != NULL) { optprev->next = optptr; // assign optprev = optprev->next; // advance } else optprev = head43 = optptr; ReadOptions43_next: pos=pos; } return(0); } /****************************************************************************** * operator() - get an option from the list * ******************************************************************************/ option * PacketStore::operator()(int major_opt=0, int minor_opt=0) { return(GetOption(major_opt, minor_opt)); } /****************************************************************************** * GetOption - get an option fromthe option list * ******************************************************************************/ option * PacketStore::GetOption(int major_opt=0, int minor_opt=0) { option *opt = new option; option *optptr; if(major_opt == 43) optptr = head43; else optptr = head; for(; optptr != NULL; optptr = optptr->next) if((major_opt == optptr->major_no) && (minor_opt == optptr->minor_no)) break; if(optptr == NULL) return(NULL); opt->major_no = optptr->major_no; opt->minor_no = optptr->minor_no; opt->len = optptr->len; opt->next = NULL; opt->data = new uint8_t[opt->len]; memcpy(opt->data, optptr->data, opt->len); return(opt); } /****************************************************************************** * DelOption - delete an option from the list * ******************************************************************************/ int PacketStore::DelOption(int major_opt=0, int minor_opt=0) { option *optptr, *optprev; if(major_opt == 43) optptr = optprev = head43; else optptr = optprev = head; for(; ((major_opt != optptr->major_no) && (minor_opt != optptr->minor_no)) || (optptr != NULL); optptr = optptr->next) optprev = optptr; if(optptr != NULL) // option found { // re-arrange the pointers if(optprev == optptr) // head if(optptr == head43) head43 = optptr->next; else head = optptr->next; else optprev->next = optptr->next; // delete the memory delete[] optptr->data; delete optptr; } else return(1); // no found return(0); } /****************************************************************************** * AddOption - add an option to the list (overwrite) * ******************************************************************************/ int PacketStore::AddOption(const option *opt) { option *optptr, *optprev; if(opt->major_no == 43) optptr = optprev = head43; else optptr = optprev = head; // find the entry or null; while(optptr != NULL) { if((opt->major_no != optptr->major_no) && (opt->minor_no != optptr->minor_no)) break; optprev = optptr; optptr = optptr->next; } if(optptr == NULL) // new item { optptr = new option; optptr->major_no = opt->major_no; optptr->minor_no = opt->minor_no; optptr->next = NULL; if((43 == opt->major_no) && (NULL == head43)) head43 = optptr; else if(NULL == head) head = optptr; else optprev->next = optptr; } else // old item { delete[] optptr->data; } optptr->len = opt->len; optptr->data = new uint8_t[optptr->len]; memcpy(optptr->data, opt->data, opt->len); return(0); } /****************************************************************************** * htois - host to intel order (short) * ******************************************************************************/ uint16_t PacketStore::htois(uint16_t value) { uint8_t t1, t2; uint16_t val; // convert to a standard val = htons(value); // check if we need to go any further // since Intel order != network order if(val != value) return(value); // we now have a standard, can you read this Intel?, this is // STANDARD! // we know that Intel ordering is the opposite to network byte ordering // so just swap bytes all the way through // out t1 = (val & 0x00ff); t2 = (val & 0xff00) >> 8; // in val = (t1<<8); val |= t2; // we are now in a non-standard return val; } /****************************************************************************** * htoil - host to intel order (long) * ******************************************************************************/ uint32_t PacketStore::htoil(uint32_t value) { uint8_t t1, t2, t3, t4; uint32_t val; // convert to a standard val = htonl(value); // check if we need to go any further // since Intel order != network order if(val != value) return(value); // we now have a standard, can you read this Intel?, this is // STANDARD! // we know that Intel ordering is the opposite to network byte ordering // so just swap bytes all the way through // out t1 = (val & 0x000000ff); t2 = (val & 0x0000ff00) >> 8; t3 = (val & 0x00ff0000) >> 16; t4 = (val & 0xff000000) >> 24; // in val = (t1<<24); val |= (t2<<16); val |= (t3<<8); val |= (t4); return(val); } /****************************************************************************** * SetAddress - set the address fiel of the packet (from/to) * ******************************************************************************/ void PacketStore::SetAddress(const struct sockaddr_in *_address) { memcpy(&address, _address, sizeof(address)); } /****************************************************************************** * MakeReply - generate a reply to an incoming request * ******************************************************************************/ int PacketStore::MakeReply(PacketStore &request, Options *config, struct sockaddr_in *server_addr) { // local vars option *opt = NULL; option *req_list = NULL; int pkttype = 0; uint16_t reqCSA; // first, check for a valid bootp packet if(BOOTREQUEST != request.op) { logger->Event(LEVEL_INFO, "MakeReply", 1, "Packet is not a bootp request"); return(-1); } // valid client system arch? reqCSA = request.GetCSA(); if((uint16_t)-1 == reqCSA) { logger->Event(LEVEL_INFO, "MakeReply", 1, "packet contains an unknown client system architecture"); return(-1); } // see if there are any menus for this packet if((uint16_t)-1 == config->CheckMenu(reqCSA)) return(-1); // next, check what the packet type was opt = request(43,71); if(opt == NULL) { logger->Event(LEVEL_INFO, "MakeReply", 1, "Received proxy DHCP packet"); pkttype = 1; } else { delete[] opt->data; delete opt; logger->Event(LEVEL_INFO, "MakeReply", 1, "Received PXE request packet"); pkttype = 2; } // was it a valid request packet? opt = request(53); if((NULL == opt) || (DHCPREQUEST != opt->data[0])) { logger->Event(LEVEL_INFO, "MakeReply", 1, "Packet does not contain a valid DHCP message type"); return(-1); } opt->data[0] = DHCPACK; opt->len = 1; AddOption(opt); delete[] opt->data; delete opt; // is the param request list ok? req_list = request(55); if(NULL == req_list) { logger->Event(LEVEL_INFO, "MakeReply", 1, "No parameter request list found"); return(-1); } // good packet ok, build reply memset(file, 0, 128); memset(sname, 0, 64); // copy over the basic info op = BOOTREPLY; htype = request.htype; hlen = request.hlen; hops = request.hops; xid = request.xid; secs = request.secs; magic_cookie = htonl(MAGIC_COOKIE); // slightly harder memcpy(chaddr, request.chaddr, 16); memset(&ciaddr, 0, sizeof(ciaddr)); memset(&yiaddr, 0, sizeof(yiaddr)); memcpy(&siaddr, &(server_addr->sin_addr), sizeof(siaddr)); memset(&giaddr, 0, sizeof(yiaddr)); // set the destination address memcpy(&address, &(request.address), sizeof(address)); // good, now go through the option list for(int i=0; i < req_list->len; i++) switch(req_list->data[i]) { case 43: if(-1 == GenOpt43(config, reqCSA, request, pkttype)) return(-1); break; case 54: GenOpt54(); break; case 60: GenOpt60(); break; } return(0); } /****************************************************************************** * GenOpt43 - generate reply options * ******************************************************************************/ int PacketStore::GenOpt43(Options *config, int reqCSA, PacketStore &request, int pkttype) { option opt, *optptr; struct sockaddr_in t_addr; char *tmpc; int arch_id, menu_id; uint8_t req_layer; int tmpi; opt.major_no = 43; // Proxy DHCP packet if(1 == pkttype) { // minor 1 opt.minor_no = 1; t_addr.sin_addr.s_addr = htonl(config->GetMTFTPAddr()); opt.len = sizeof(t_addr.sin_addr.s_addr); opt.data = new uint8_t[opt.len]; memcpy(opt.data, &(t_addr.sin_addr.s_addr), opt.len); AddOption(&opt); delete [] opt.data; // minor 2 - n.b. Intel byte ordering - do intel have no common sense? opt.minor_no = 2; t_addr.sin_port = htois(config->GetMTFTPcport()); opt.len = sizeof(t_addr.sin_port); opt.data = new uint8_t[opt.len]; memcpy(opt.data, &(t_addr.sin_port), opt.len); AddOption(&opt); delete [] opt.data; // minor 3 - again Intel's bel^Wbig endian opt.minor_no = 3; t_addr.sin_port = htois(config->GetMTFTPsport()); opt.len = sizeof(t_addr.sin_port); opt.data = new uint8_t[opt.len]; memcpy(opt.data, &(t_addr.sin_port), opt.len); AddOption(&opt); delete [] opt.data; // minor 4 opt.minor_no = 4; opt.len = 1; opt.data = new uint8_t[opt.len]; opt.data[0] = MTFTP_OPEN_TIMEOUT; AddOption(&opt); // minor 5 opt.minor_no = 5; opt.data[0] = MTFTP_REOPEN_TIMEOUT; AddOption(&opt); // minor 6 opt.minor_no = 6; opt.data[0] = 0; // check for disabled broad/multicast if(0 == config->UseMulticast()) opt.data[0] |= DISABLE_MULTICAST; if(0 == config->UseBroadcast()) opt.data[0] |= DISABLE_BROADCAST; AddOption(&opt); delete [] opt.data; // minor 7 opt.minor_no = 7; t_addr.sin_addr.s_addr = htonl(config->GetMulticast()); opt.len = sizeof(t_addr.sin_addr.s_addr); opt.data = new uint8_t[opt.len]; memcpy(opt.data, &(t_addr.sin_addr.s_addr), opt.len); AddOption(&opt); delete [] opt.data; // minor 9 - will always be ok, as we checked the menus earlier optptr = config->MakeBootMenu(reqCSA, &arch_id, &menu_id); optptr->major_no = 43; optptr->minor_no = 9; AddOption(optptr); delete [] optptr->data; delete optptr; // minor 10 - the menu opt.minor_no = 10; tmpc = config->GetMenuPrompt(); opt.len = strlen(tmpc)+1; opt.data = new uint8_t[opt.len]; opt.data[0] = config->GetMenuTimeout(); memcpy(opt.data+1, tmpc, opt.len-1); AddOption(&opt); delete [] opt.data; delete [] tmpc; // minor 71 opt.minor_no = 71; opt.len = 4; opt.data = new uint8_t[4]; tmpi = htons(PXE_SERVER_TYPE); memcpy(opt.data, &tmpi, 2); opt.data[2] = 0; req_layer = opt.data[3] = config->GetMinLayer(arch_id, menu_id); AddOption(&opt); delete [] opt.data; } else // PXE layer request { arch_id = reqCSA; // get option 71 fromthe request packet optptr = request(43, 71); menu_id = optptr->data[0]; menu_id <<= 8; menu_id |= optptr->data[1]; // don't worry about the third byte, it is for 'credentials' req_layer = optptr->data[3]; // make the reply option opt.minor_no = 71; opt.len = 4; opt.data = new uint8_t[opt.len]; memcpy(opt.data, optptr->data, 2); opt.data[2] = 0; delete [] optptr->data; delete optptr; // which layer to get next/any if(config->CheckLayer(menu_id, arch_id, req_layer) == 0) { opt.data[3] = req_layer; } else { delete [] opt.data; return(-1); } AddOption(&opt); delete [] opt.data; } // make filename tmpc = config->MakeFilename(menu_id, arch_id, req_layer); if(strlen(tmpc) > 127) // gotta think of a better way tmpi = 127; else tmpi = strlen(tmpc); memcpy(file, tmpc, tmpi); file[tmpi] = 0; delete [] tmpc; // cool, send the reply return(0); } /****************************************************************************** * GenOpt54 - generate reply options * ******************************************************************************/ int PacketStore::GenOpt54(void) { option opt; opt.len = sizeof(siaddr.s_addr); opt.major_no = 54; opt.minor_no = 0; opt.data = new uint8_t[opt.len]; memcpy(opt.data, &(siaddr.s_addr), opt.len); AddOption(&opt); delete [] opt.data; return(0); } /****************************************************************************** * GenOpt60 - generate reply options * ******************************************************************************/ int PacketStore::GenOpt60(void) { option opt; opt.major_no=60; opt.minor_no=0; opt.len = strlen(DHCP_T60); opt.data = new uint8_t[opt.len]; memcpy(opt.data, DHCP_T60, opt.len); AddOption(&opt); delete [] opt.data; return(0); } /****************************************************************************** * GetCSA - get the Client System Archetecture of the packet * ******************************************************************************/ uint16_t PacketStore::GetCSA(void) { uint16_t csa1, csa2; option *opt; char csac[6]; int csa1_nok=0; int csa2_nok=0; // should have received two copies of the csa, compare csa1 = csa2 = 0; // the binary packed copy opt = GetOption(93); if(NULL == opt) csa1_nok = 1; else { memcpy(&csa1, opt->data, 2); csa1 = ntohs(csa1); delete [] opt->data; delete opt; } // get the ascii packed version opt = GetOption(60); if(NULL == opt) csa2_nok = 1; else if(32 != opt->len) { csa2_nok = 1; delete [] opt->data; delete opt; } else { memcpy(csac, opt->data+15, 5); csac[5] = 0; csa2 = atoi(csac); delete [] opt->data; delete opt; } // check details if((0 == csa1_nok) && (0 == csa2_nok ) && (csa1 == csa2)) return(checkCSA(csa1)); else if ((1 == csa1_nok) && (0 == csa2_nok )) return(checkCSA(csa2)); else if ((0 == csa1_nok) && (1 == csa2_nok )) return(checkCSA(csa1)); return((uint16_t)-1); } /****************************************************************************** * checkCSA - see if we are supporting the type of arch * ******************************************************************************/ uint16_t PacketStore::checkCSA(uint16_t reqCSA) { int i; for(i=0; (uint16_t)-1 != CSA_types[i].arch_id; i++) if(CSA_types[i].arch_id == reqCSA) break; return(CSA_types[i].arch_id); } /****************************************************************************** * MakeReply - pack the packet into a raw data form * ******************************************************************************/ bootp_packet_t * PacketStore::PackPacket(void) { int pktlen, pos, len43; option *optptr; bootp_packet_t *opt = new bootp_packet_t; // first go through the packet and see how big it is pktlen = 241; // 255 closing tag optptr = head; while(NULL != optptr) { pktlen += optptr->len+2; // ,, optptr = optptr->next; } optptr = head43; if(NULL != optptr) pktlen += 3; // 43,,...,255 len43 = 0; while(NULL != optptr) { pktlen += optptr->len+2; // ,, len43 = pktlen; optptr = optptr->next; } // initalise opt->data = new uint8_t[pktlen]; pos = 0; // basic packet info opt->data[0] = op; opt->data[1] = htype; opt->data[2] = hlen; opt->data[3] = hops; memcpy(opt->data+4, &xid, 4); memcpy(opt->data+8, &secs, 2); memset(opt->data+10, 0, 2); // the addresses memcpy(opt->data+12, &(ciaddr.s_addr), 4); memcpy(opt->data+16, &(yiaddr.s_addr), 4); memcpy(opt->data+20, &(siaddr.s_addr), 4); memcpy(opt->data+24, &(giaddr.s_addr), 4); // hardware addr memcpy(opt->data+28, chaddr, 16); // server name memcpy(opt->data+44, sname, 64); // filename memcpy(opt->data+108, file, 128); // magic cookie memcpy(opt->data+236, &magic_cookie, 4); // basic options pos = 240; optptr = head; while(NULL != optptr) { opt->data[pos] = optptr->major_no; opt->data[pos+1] = optptr->len; memcpy(opt->data+pos+2, optptr->data, optptr->len); pos += optptr->len+2; optptr = optptr->next; } // option 43 optptr = head43; if(NULL != optptr) { opt->data[pos] = 43; opt->data[pos+1] = len43; pos+=2; } while(NULL != optptr) { opt->data[pos] = optptr->minor_no; opt->data[pos+1] = optptr->len; memcpy(opt->data+pos+2, optptr->data, optptr->len); pos += optptr->len+2; optptr = optptr->next; } if(NULL != head43) { opt->data[pos] = 255; pos++; } // close the packet off opt->data[pos] = 255; opt->len=pos; return(opt); }