#include #include #include #include #include #include #include #include #include #include // the chunk size for each alloc int chunknum = 200; int doreload = 0; int verbose = 0; char logmsg[50]; // the struct to store the winpe configuration for each node struct nodecfg { char node[50]; char data[150]; }; char *data = NULL; // the ptr to the array of all node config int nodenum = 0; // trigger the main program to reload configuration file void reload(int sig) { doreload = 1; } // the subroutine which is used to load configuration from // /var/lib/xcat/proxydhcp.cfg to *data void loadcfg () { nodenum = 0; free(data); data = NULL; doreload = 0; char *dp = NULL; FILE *fp; fp = fopen("/var/lib/xcat/proxydhcp.cfg", "r"); if (fp) { int num = chunknum; int rtime = 1; while (num == chunknum) { // realloc the chunknum size of memory each to save memory usage data = realloc(data, sizeof(struct nodecfg) * chunknum * rtime); if (NULL == data) { fprintf (stderr, "Cannot get enough memory.\n"); free (data); return; } dp = data + sizeof(struct nodecfg) * chunknum * (rtime - 1); memset(dp, 0, sizeof(struct nodecfg) * chunknum); num = fread(dp, sizeof (struct nodecfg), chunknum, fp); nodenum += num; rtime++; } fclose(fp); } } // get the path of winpe from configuration file which is stored in *data char *getwinpepath(char *node) { int i; struct nodecfg *nc = (struct nodecfg *)data; for (i=0; inode, node)) { return nc->data; } nc++; } return NULL; } int main(int argc, char *argv[]) { int i; for(i = 0; i < argc; i++) { if (strcmp(argv[i], "-V") == 0) { verbose = 1; setlogmask(LOG_UPTO(LOG_DEBUG)); openlog("proxydhcp", LOG_NDELAY, LOG_LOCAL0); } } // regist my pid to /var/run/xcat/proxydhcp.pid int pid = getpid(); FILE *pidf = fopen ("/var/run/xcat/proxydhcp.pid", "w"); if (pidf) { fprintf(pidf, "%d", pid); fclose (pidf); } else { fprintf (stderr, "Cannot open /var/run/xcat/proxydhcp.pid\n"); return 1; } // load configuration at first start loadcfg(); // regist signal SIGUSR1 for triggering reload configuration from outside struct sigaction sigact; sigact.sa_handler = &reload; sigaction(SIGUSR1, &sigact, NULL); int serverfd,port; int getpktinfo = 1; struct addrinfo hint, *res; char cmsg[CMSG_SPACE(sizeof(struct in_pktinfo))]; char clientpacket[1024]; struct sockaddr_in clientaddr; struct msghdr msg; struct cmsghdr *cmsgptr; struct iovec iov[1]; unsigned int myip, clientip; char *txtptr; iov[0].iov_base = clientpacket; iov[0].iov_len = 1024; memset(&msg,0,sizeof(msg)); memset(&clientaddr,0,sizeof(clientaddr)); msg.msg_name=&clientaddr; msg.msg_namelen = sizeof(clientaddr); msg.msg_iov = iov; msg.msg_iovlen = 1; msg.msg_control=&cmsg; msg.msg_controllen = sizeof(cmsg); char defaultwinpe[20] = "Boot/bootmgfw.efi"; char bootpmagic[4] = {0x63,0x82,0x53,0x63}; int pktsize; int doexit=0; port = 4011; memset(&hint,0,sizeof(hint)); hint.ai_family = PF_INET; /* Would've done UNSPEC, but it doesn't work right and this is heavily v4 specific anyway */ hint.ai_socktype = SOCK_DGRAM; hint.ai_flags = AI_PASSIVE; getaddrinfo(NULL,"4011",&hint,&res); serverfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (!serverfd) { fprintf(stderr,"That's odd...\n"); } setsockopt(serverfd,IPPROTO_IP,IP_PKTINFO,&getpktinfo,sizeof(getpktinfo)); if (bind(serverfd,res->ai_addr ,res->ai_addrlen) < 0) { fprintf(stderr,"Unable to bind 4011"); exit(1); } while (!doexit) { // use select to wait for the 4011 request packages coming fd_set fds; FD_ZERO(&fds); FD_SET(serverfd, &fds); struct timeval timeout; timeout.tv_sec = 30; timeout.tv_usec = 0; int rc; if ((rc = select(serverfd+1,&fds,0,0, &timeout)) <= 0) { if (doreload) { loadcfg(); fprintf(stderr, "load in select\n"); } if (verbose) {syslog(LOG_DEBUG, "reload /var/lib/xcat/proxydhcp.cfg\n");} continue; } if (doreload) { loadcfg(); if (verbose) {syslog(LOG_DEBUG, "reload /var/lib/xcat/proxydhcp.cfg\n");} } pktsize = recvmsg(serverfd,&msg,0); if (pktsize < 320) { continue; } if (clientpacket[0] != 1 || memcmp(clientpacket+0xec,bootpmagic,4)) { continue; } for (cmsgptr = CMSG_FIRSTHDR(&msg); cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(&msg,cmsgptr)) { if (cmsgptr->cmsg_level == IPPROTO_IP && cmsgptr->cmsg_type == IP_PKTINFO) { myip = ((struct in_pktinfo*)(CMSG_DATA(cmsgptr)))->ipi_addr.s_addr; } } // get the ip of dhcp client clientip = 0; int i; for (i = 0; i< 4; i++) { clientip = clientip << 8; clientip += (unsigned char)clientpacket[15-i]; } // get the winpe path struct hostent *host = gethostbyaddr(&clientip, sizeof(clientip), AF_INET); char *winpepath = defaultwinpe; if (host) { if (host->h_name) { // remove the domain part from hostname char *place = strstr(host->h_name, "."); if (place) { *place = '\0'; } winpepath = getwinpepath(host->h_name); if (winpepath == NULL) { winpepath = defaultwinpe; } if (verbose) { sprintf(logmsg, "Received proxydhcp request from %s\n", host->h_name); syslog(LOG_DEBUG, logmsg); } } } else { winpepath = defaultwinpe; } // get the Vendor class identifier char *arch = NULL; unsigned char *p = clientpacket + 0xf0; while (*p != 0xff && p < (unsigned char *)clientpacket + pktsize) { if (*p == 60) { arch = p + 0x11; break; } else { p += *(p+1) + 2; } } char winboot[50]; // the bootload of winpe memset(winboot, 0, 50); if (0 == memcmp(arch, "00000", 5)) { // bios boot mode strcpy(winboot, winpepath); strcat(winboot, "Boot/pxeboot.0"); } else if (0 == memcmp(arch, "00007", 5)) { // uefi boot mode strcpy(winboot, winpepath); strcat(winboot, "Boot/bootmgfw.efi"); } clientpacket[0] = 2; //change to a reply myip = htonl(myip); //endian neutral change clientpacket[0x14] = (myip>>24)&0xff; //maybe don't need to do this, maybe assigning the whole int would be better clientpacket[0x15] = (myip>>16)&0xff; clientpacket[0x16] = (myip>>8)&0xff; clientpacket[0x17] = (myip)&0xff; txtptr = clientpacket+0x6c; strncpy(txtptr, winboot ,128); // keeping 128 in there just in case someone changes the string //strncpy(txtptr,"winboot/new/Boot/bootmgfw.efi",128); // keeping 128 in there just in case someone changes the string //strncpy(txtptr,"Boot/pxeboot.0",128); // keeping 128 in there just in case someone changes the string clientpacket[0xf0]=0x35; //DHCP MSG type clientpacket[0xf1]=0x1; // LEN of 1 clientpacket[0xf2]=0x5; //DHCP ACK clientpacket[0xf3]=0x36; //DHCP server identifier clientpacket[0xf4]=0x4; //DHCP server identifier length clientpacket[0xf5] = (myip>>24)&0xff; //maybe don't need to do this, maybe assigning the whole int would be better clientpacket[0xf6] = (myip>>16)&0xff; clientpacket[0xf7] = (myip>>8)&0xff; clientpacket[0xf8] = (myip)&0xff; char winBCD[50]; strcpy(winBCD, winpepath); strcat(winBCD, "Boot/BCD"); clientpacket[0xf9] = 0xfc; // dhcp 252 'proxy', but coopeted by bootmgfw, it's actually suggesting the boot config file clientpacket[0xfa] = strlen(winBCD) + 1; //length of 9 txtptr = clientpacket+0xfb; strncpy(txtptr, winBCD, strlen(winBCD)); clientpacket[0xfa + strlen(winBCD) + 1] = 0; clientpacket[0xfa + strlen(winBCD) + 2] = 0xff; sendto(serverfd,clientpacket,pktsize,0,(struct sockaddr*)&clientaddr,sizeof(clientaddr)); if (verbose) { sprintf(logmsg, "Path of bootloader:%s. Path of BCD:%s\n", winboot, winBCD); syslog(LOG_DEBUG, logmsg); } } if (verbose) { closelog();} }