DHCP Server for testing PXE on Porteus 1.1

Post tutorials, HOWTO's and other useful resources here.
liguero
White ninja
White ninja
Posts: 14
Joined: 16 Mar 2012, 09:13
Location: FRANCE

PXE boot problem

Post#1 by liguero » 20 Mar 2012, 17:25

This software (DHCP server for PXE) is provided 'as-is', without any express or implied warranty.
There was certainly some missteps. I am not an expert in C.
The primary goal of this soft is to test the good execution of the various stages of PXE boot under Linux.
This various stages are printed on the screen as well as parameters, address and files.

# ./netboot 192.168.1.55 192.168.1.1 192.168.1.1 00-1b-38-6b-93-71
IP dhcp server -> 192.168.1.10
Subnet mask -> 255.255.255.0
IP dhcp client -> 192.168.1.55
Broadcast -> 192.168.1.255
Router -> 192.168.1.1
Dns -> 192.168.1.1
mac pattern -> 00 1b 38 6b 93 71

Code: Select all

/* http://brokestream.com/netboot.html

  To build it: gcc -o netboot netboot.c

  netboot.c
  Version 2010-01-19 Revision 2012-03-20 by liguero.

  Copyright (C) 2007-2010 Ivan Tikhonov

  This software (DHCP server for PXE) is provided 'as-is', without any express or implied
  warranty.  In no event will the authors be held liable for any damages
  arising from the use of this software.

  Permission is granted to anyone to use this software for any purpose,
  including commercial applications, and to alter it and redistribute it
  freely, subject to the following restrictions:

  1. The origin of this software must not be misrepresented; you must not
     claim that you wrote the original software. If you use this software
     in a product, an acknowledgment in the product documentation would be
     appreciated but is not required.
  2. Altered source versions must be plainly marked as such, and must not be
     misrepresented as being the original software.
  3. This notice may not be removed or altered from any source distribution.

  Revisions by liguero : automatic search IPserver, broadcast, subnet mask.
                                    addition : router and dns	

  Original source by Ivan Tikhonov, kefeer@brokestream.com

		# gcc -o netboot netboot.c	(on porteus 1.1)
		Don't forget : before, run monkey (web server)

"Usage  : 	# ./netboot <ip to assign> <ip router> <ip dns> <mac client>
		Example: ./netboot 192.168.0.55 192.168.0.1 192.168.0.1 00:11:ee:ff:66:ef
		Example: ./netboot 192.168.0.55 192.168.0.1 192.168.0.1 -66-ef
		To find out who requesting boot (mac address)  run: ./netboot
*/

#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <arpa/inet.h>

#include <pwd.h>
#include <ifaddrs.h>

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

#include <stdint.h>

void tftpd(int s) {
	static int f;
	static int blksize = 512;
	static int last_sent = 0;
	static int was_last = 0;
	{
		unsigned char p[1500];
		struct sockaddr_in peer;
		int peerl = sizeof(peer);
		int n = recvfrom(s,p,1500,0,(struct sockaddr *)&peer,&peerl);
		if(p[1] == 1) {
			unsigned char o[1500];
			unsigned char *pp = p+2;
			unsigned char *epp = p+n;
			while(pp<epp&&*pp) pp++; pp++;
			while(pp<epp&&*pp) pp++; pp++;

			printf("rrq %s\n", p+2);

			f = open(p+2,O_RDONLY);
			if(f == -1) {
				o[0] = 0; o[1] = 5;
				o[2] = 0; o[3] = 1;
				strcpy(o+4,"Not found");
				o[13] = 0;
				sendto(s,o,14,0,(struct sockaddr *)&peer,peerl);
				printf("error\n");
				return;
			}

			o[0] = 0;
			if(pp<epp&&*pp) {
				unsigned char *p = o+2;
				struct stat st;
				fstat(f,&st);
				o[1] = 6;
				while(pp<epp) {
					if(strcmp(pp,"tsize") == 0) {
						strcpy(p,"tsize"); p+=6;
						p+=sprintf(p,"%u", (int)st.st_size); *p++ = 0;
						pp+=6; pp+=strlen(pp)+1;
					} else if(strcmp(pp,"blksize") == 0) {
						strcpy(p,"blksize"); p+=8; pp+=8;
						blksize = atoi(pp);
						strcpy(p,pp); p+=strlen(p)+1; pp+=strlen(pp)+1;
					} else {
						pp+=strlen(pp)+1; pp+=strlen(pp)+1;
					}
				}
				sendto(s,o,p-o,0,(struct sockaddr *)&peer,peerl);
			}

		} else if(p[1] == 4) {
			int no = (p[2]<<8)|p[3];
			if(was_last) { printf("end\n"); was_last = 0; blksize = 512; last_sent = 0; close(f); f = -1; }
			if(no++ == last_sent) {
				int n;
				unsigned char o[10240] = {0,3,(no>>8)&0xff,no&0xff};
				lseek(f,(no-1)*blksize,SEEK_SET);
				n = read(f,o+4,blksize);
				sendto(s,o,n+4,0,(struct sockaddr *)&peer,peerl);
				last_sent = no;
				if(n<blksize) was_last = 1;
			}
		}
	}
}

int main(int argc, char *argv[]) {
	int s = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
	int r = -1;
	int t = -1;

	struct ifaddrs *ifaddr, *ifa;
        int family;

	/* For printing IP byte per byte */
		union Adr_IP{
		struct { unsigned char ip1,ip2,ip3,ip4;} octets;
		unsigned long full_ip;
	};

	unsigned char macpat[6]; int macskip = 6;

		in_addr_t bc = 0;			/* broadcast */
		in_addr_t sc = 0;			/* server address */
		in_addr_t cc = 0;			/* client address */
		in_addr_t rc = 0;			/* router address */
		in_addr_t dc = 0;			/* dns address */
		in_addr_t mc = 0;			/* mask */
	
	if(argc > 1 && argc < 5) {
		fprintf(stderr, "Usage  : ./netboot <ip to assign> <ip router> <ip dns> <mac client>\n");
		fprintf(stderr, "Example: ./netboot 192.168.0.55 192.168.0.1 192.168.0.1 00:11:ee:ff:66:ef\n");
		fprintf(stderr, "Example: ./netboot 192.168.0.55 192.168.0.1 192.168.0.1 -66-ef\n");
		fprintf(stderr, "\nTo find out who requesting boot run: ./netboot\n");
		exit(1);
	}

	if(argc == 5) {

 		cc = inet_addr(argv[1]);
		rc = inet_addr(argv[2]);
 		dc = inet_addr(argv[3]);
		
		if (getifaddrs(&ifaddr) == -1) {
                perror("getifaddrs");
                exit(EXIT_FAILURE);
           }

           for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {	/* Search for interfaces */
               family = ifa->ifa_addr->sa_family;

              	if (strcmp(ifa->ifa_name, "lo")) {			/* Not for lo = 127.0.0.1 */
			
			if (family == AF_INET ) {			/*and only for IPv4 (AF_INET) */
				sc = (((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr);
				mc = (((struct sockaddr_in *)ifa->ifa_netmask)->sin_addr.s_addr);
				bc = (((struct sockaddr_in *)ifa->ifa_ifu.ifu_broadaddr)->sin_addr.s_addr);
               		}
		}
           }
           freeifaddrs(ifaddr);
		
		# define ipbytes testIP.octets.ip1,testIP.octets.ip2,testIP.octets.ip3,testIP.octets.ip4
		union Adr_IP testIP;
		testIP.full_ip = sc;
		printf ("IP dhcp server\t-> \t%d.%d.%d.%d \n",ipbytes);
		testIP.full_ip = mc;
		printf ("Subnet mask\t-> \t%d.%d.%d.%d \n",ipbytes);
		testIP.full_ip = cc;
		printf ("IP dhcp client\t-> \t%d.%d.%d.%d \n",ipbytes);
		testIP.full_ip = bc;
		printf ("Broadcast\t-> \t%d.%d.%d.%d \n",ipbytes);
		testIP.full_ip = rc;
		printf ("Router\t\t-> \t%d.%d.%d.%d \n",ipbytes);
		testIP.full_ip = dc;
		printf ("Dns\t\t-> \t%d.%d.%d.%d \n",ipbytes);

		{ char *p = argv[4]; while(*p) {
			int d;
			if(p[0]>='0'&&p[0]<='9') { d = (p[0]-'0')<<4; }
			else if(p[0]>='a'&&p[0]<='f') { d = (p[0]-'a'+0xa)<<4; }
			else if(p[0]>='A'&&p[0]<='F') { d = (p[0]-'A'+0xa)<<4; }
			else { p++; continue; }
			if(p[1]>='0'&&p[1]<='9') { d = d|(p[1]-'0'); }
			else if(p[1]>='a'&&p[1]<='f') { d = d|(p[1]-'a'+0xa); }
			else if(p[1]>='A'&&p[1]<='F') { d = d|(p[1]-'A'+0xa); }

			macpat[6-macskip] = d;
			macskip--;
			p+=2;
		}}
	}

	if(cc) {
		fprintf(stderr, "mac pattern\t->\t");
		{ int i = macskip;
		  while(i--) { fprintf(stderr,"?? "); }
		  i = 0; while(i<(6-macskip)) { fprintf(stderr, "%02x ",macpat[i]); i++; }
		}
		fprintf(stderr, "\n");
	}

	{ struct sockaddr_in b = { AF_INET, htons(67), {0xffffffff} };
	  if(bind(s, (struct sockaddr *)&b, sizeof(b)) != 0) {
		fprintf(stderr, "Can not bind broadcast address. DHCP will not work! Try run it as root?\n");
	  }
	}

	if(cc)
	{ struct sockaddr_in b = { AF_INET, htons(67), {sc} };
	  r = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
	  if(bind(r, (struct sockaddr *)&b, sizeof(b)) != 0) {
		fprintf(stderr, "Can not bind server address. DHCP WILL NOT WORK! Try run it as root?\n");
	  }
	}

	if(cc)
	{ struct sockaddr_in b = { AF_INET, htons(69), {sc} };
 	  t = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
	  if(bind(t, (struct sockaddr *)&b, sizeof(b)) != 0) {
		fprintf(stderr, "Can not bind tftp server address. TFTP WILL NOT WORK! Try run it as root?\n");
	  }
	}

	{ int v = 1;
	  setsockopt(r,SOL_SOCKET,SO_BROADCAST,&v,sizeof(v));
	}

	{
		struct passwd *nobody;
		nobody = getpwnam("nobody");
		if (nobody && nobody->pw_uid) setuid(nobody->pw_uid);
	}


	for(;;) {
		fd_set rset;
		int n;

		FD_ZERO(&rset);
		FD_SET(s,&rset);
		if(t != -1) FD_SET(t,&rset);
		n = select((t>s?t:s)+1,&rset,0,0,0);

		if(cc&&FD_ISSET(t,&rset)) {
			tftpd(t);
		}

		if(FD_ISSET(s,&rset)) {
			int type, pos97 = -1, pos12=-1;
			unsigned char p[1500] = {0xff};
			recv(s,p,1500,0);
			if(p[0] == 2) continue;


			{ int i = 240;
			  for(;i<1500;) {
				if(p[i] == 0xff) break;
				if(p[i] == 0x0) { i++; continue; }
				if(p[i] == 53) { type = p[i+2]; }
				if(p[i] == 97) { pos97 = i+1; }
				if(p[i] == 12) { pos12 = i+1; }
				i += p[i+1] + 2;
			  }
			}

			printf("request %u from %02x-%02x-%02x-%02x-%02x-%02x ",type,p[28],p[29],p[30],p[31],p[32],p[33]);
			if(pos12!=-1) { printf("(%.*s)\n", p[pos12],p+pos12+1);
			} else { printf("(%s)\n", p+44); }
			if(!cc) continue;

			if(memcmp(p+28+macskip,macpat,(6-macskip))) continue;

			printf("matched\n");

			if(type == 1 || type == 3) {
				int i,m;
				p[0] = 2;
				p[12] = 0; p[13] = 0; p[14] = 0; p[15] = 0;

				*(uint32_t*)(p+16)=(uint32_t)cc;
				*(uint32_t*)(p+20)=(uint32_t)sc;

				i = 240;


				if(pos97 != -1) {
					p[i++] = 97;
					{ int n = p[pos97++];
					  p[i++] = n;
					  while(n--) { p[i++] = p[pos97++]; }
					}
				}

				p[i++] = 53; p[i++] = 1;
				p[i++] = type==1 ? 2 : 5;

				p[i++] = 6; p[i++] = 4;						/*dns address*/
				*(uint32_t*)(p+i)=(uint32_t)dc; i+=4;	

				p[i++] = 51; p[i++] = 4;					/* Lease time infinite */
				p[i++] = 255; p[i++] = 255; p[i++] = 255; p[i++] = 255;

				p[i++] = 54; p[i++] = 4;					/* dhcp server address */
				*(uint32_t*)(p+i)=(uint32_t)sc; i+=4;

				p[i++] = 60; p[i++] = 9;
				p[i++] = 'P'; p[i++] = 'X'; p[i++] = 'E'; p[i++] = 'C'; p[i++] = 'l'; p[i++] = 'i'; p[i++] = 'e'; p[i++] = 'n'; p[i++] = 't';

				if(type == 3) {
					p[i++] = 1; p[i++] = 4;					/* subnet mask */
					*(uint32_t*)(p+i)=(uint32_t)mc; i+=4;
					/*p[i++] = 255; p[i++] = 255; p[i++] = 255; p[i++] = 0;*/
					p[i++] = 3; p[i++] = 4;					/* router address */
					*(uint32_t*)(p+i)=(uint32_t)rc; i+=4;
					p[i++] = 12; p[i++] = 9;
					p[i++] = 'p'; p[i++] = 'x'; p[i++] = 'e'; p[i++] = 'c'; p[i++] = 'l'; p[i++] = 'i'; p[i++] = 'e'; p[i++] = 'n'; p[i++] = 't';
					p[i++] = 67; m = i++;					/* Boot file name */
					p[i++] = 'p'; p[i++] = 'x'; p[i++] = 'e'; p[i++] = 'l'; p[i++] = 'i'; p[i++] = 'n'; p[i++] = 'u'; p[i++] = 'x'; p[i++] = '.'; 
                                        p[i++] = '0'; p[i++] = '\0';
					p[m] = i - m - 2;
				}
				
				p[i++] = 43; m = i++;
				{
					if(type == 1) {
						p[i++] = 6; p[i++] = 1; p[i++] = 0;

						p[i++] = 8; p[i++] = 7;
						p[i++] = 0; p[i++] = 0; p[i++] = 1;
						*(uint32_t*)(p+i)=(uint32_t)sc; i+=4;
					}
					p[i++] = 255;
				}
				p[m] = i - m - 2;
				p[i++] = 255;

				{ struct sockaddr_in a = { AF_INET, htons(68), {bc} };
				  sendto(r,p,i,0,(struct sockaddr *)&a,sizeof(a));
				}
			}
		}
	}
}

User avatar
Hamza
Warlord
Warlord
Posts: 1908
Joined: 28 Dec 2010, 07:41
Distribution: Porteus
Location: France

Re: DHCP Server for testing PXE on Porteus 1.1

Post#2 by Hamza » 20 Mar 2012, 17:35

I think, there is already an option for PXE boot at boot menu list of Porteus.

Anyway, Thanks for sharing! Need to be compiled with gcc before ;)
NjVFQzY2Rg==

liguero
White ninja
White ninja
Posts: 14
Joined: 16 Mar 2012, 09:13
Location: FRANCE

Re: DHCP Server for testing PXE on Porteus 1.1

Post#3 by liguero » 20 Mar 2012, 20:43

This soft is intended to be only used as test. It is not intended to replace the existing software.
It is an explanatory approach of PXE for newbies. (and others ...)

Important clarification : if we want to try this little soft (dhcp-tftp server) :
( For security reasons.)
1. Create a new folder : /home/guest/netboot ,for instance.
2. Copy netboot.c in this folder.
3. Create executable file netboot. ( gcc -o netboot netboot.c)
4. Copy from /boot to /home/guest/netboot :
- pxelinux.0
- vmlinuz
- initrd.xz
- porteus.jpg
5. Create newfolders : /home/guest/netboot/pxelinux.cfg and /home/guest/netboot/syslinux
6. Copy /boot/pxelinux.cfg/default /home/guest/netboot/pxelinux.cfg/default
7. Copy /boot/syslinux/vesamenu.c32 /home/guest/netboot/syslinux/vesamenu.c32
8. Modify file /home/guest/netboot/pxelinux.cfg/default
- DEFAULT /syslinux/vesamenu.c32 ~ -> DEFAULT syslinux/vesamenu.c32 ~
- MENU BACKGROUND /porteus.png -> MENU BACKGROUND porteus.jpg

Suppress all the "/" for all files in each entry of the menu.
LABEL xconf - KERNEL /vmlinuz -> KERNEL vmlinuz
- APPEND /initrd=initrd.xz vga=791 -> APPEND initrd=initrd.xz vga=791
Same for LABEL lxde, LABEL cp2ram, LABEL startx, LABEL text.

Before the first testing, don't forget to run monkey web server. ( /boot/pxelinux.cfg/web/start )
Then, choose "Text Mode" when the menu is displayed. Log in as root and after, run startx.

I hope that this experience achieve concrete results and wish everyone the best of luck.
I hope to have forgotten nothing. Liguero.

User avatar
Hamza
Warlord
Warlord
Posts: 1908
Joined: 28 Dec 2010, 07:41
Distribution: Porteus
Location: France

Re: DHCP Server for testing PXE on Porteus 1.1

Post#4 by Hamza » 21 Mar 2012, 09:30

Suppress all the "/" for all files in each entry of the menu.
LABEL xconf - KERNEL /vmlinuz -> KERNEL vmlinuz
- APPEND /initrd=initrd.xz vga=791 -> APPEND initrd=initrd.xz vga=791
Same for LABEL lxde, LABEL cp2ram, LABEL startx, LABEL text.
Why does we need to do this step ?
- DEFAULT /syslinux/vesamenu.c32 ~ -> DEFAULT syslinux/vesamenu.c32 ~
- MENU BACKGROUND /porteus.png -> MENU BACKGROUND porteus.jpg
Seems to have some bugs on your C code with realpath.
NjVFQzY2Rg==

liguero
White ninja
White ninja
Posts: 14
Joined: 16 Mar 2012, 09:13
Location: FRANCE

Re: DHCP Server for testing PXE on Porteus 1.1

Post#5 by liguero » 21 Mar 2012, 14:14

This sofware, originaly written by Ivan Tikhonov, is relatively simple.
The files to download by the PXE client are in the same directory as the
server itself. But for this server the root is always the system root (/)

When, in the file "default" it read : DEFAULT /syslinux/vesamenu.c32 it search
under the system root "/" and not under /home/guest/netboot/.

But, if we modify the file "default" in this way : DEFAULT syslinux/vesamenu.c32.
The file path is now relative to /home/guest/netboot

We therefore need to modify the path of all the files in "default".

For example, MENU BACKGROUND /porteus.jpg becomes MENU BACKGROUND porteus.jpg.

In this sofware i have only offered a way to automatically retrieve IP parameters
as IP server, broadcast and subnet mask. I have also added the router, dns dhcp-options and few comments.

This afternoon (2012-03-21) i start all over again :

1. Open the forum and select HOW TO's & Resources and then DHCP Server for testing PXE on Porteus 1.1.

2. SELECT ALL the code, copy and paste it in KWrite.

3. Save this new file under /home/guest/netboot/netboot.c

4. Compile with gcc : # gcc -o netboot netboot.c

5. root@porteus:/boot/pxelinux.cfg/web/start

6. root@porteus:/home/guest/netboot# ./netboot 192.168.1.55 192.168.1.1 192.168.1.1 00-1b-38-6b-93-71
IP dhcp server -> 192.168.1.10
Subnet mask -> 255.255.255.0
IP dhcp client -> 192.168.1.55
Broadcast -> 192.168.1.255
Router -> 192.168.1.1
Dns -> 192.168.1.1
mac pattern -> 00 1b 38 6b 93 71
request 1 from 00-1b-38-6b-93-71 ()
matched
request 3 from 00-1b-38-6b-93-71 ()
matched
rrq pxelinux.0
No error is detected in the processus.
Sorry, i don't know realpath.

I sincerely hope that such guidance shall be clear and easily comprehensible.-liguero.

User avatar
Hamza
Warlord
Warlord
Posts: 1908
Joined: 28 Dec 2010, 07:41
Distribution: Porteus
Location: France

Re: DHCP Server for testing PXE on Porteus 1.1

Post#6 by Hamza » 21 Mar 2012, 14:57

Thanks for explanation! Instead of use this, you may use ./path/to/boot
root@porteus:/home/guest/netboot# ./netboot 192.168.1.55 192.168.1.1 192.168.1.1 00-1b-38-6b-93-71
In your example, you're an pirvate IP of Class C which it is not the private range of every routers. That would be good if it can detect itself the private range of the dhcp server if there is one.
Router -> 192.168.1.1
Also, in the country where I lives, there is some ISP who are using 192.168.1.254 and 192.168.0.1
So, it must detect itself a lot of informations with ifconfig by example.

I hope that will help someone! Again, Thanks for sharing this wonderful resource! :wink:
NjVFQzY2Rg==

liguero
White ninja
White ninja
Posts: 14
Joined: 16 Mar 2012, 09:13
Location: FRANCE

Re: DHCP Server for testing PXE on Porteus 1.1

Post#7 by liguero » 21 Mar 2012, 17:05

I see a mistake in my message (21 Mar 2012, 14:14). Formulating is ambiguous.

I wrote : 5. root@porteus:/boot/pxelinux.cfg/web/start

You must read : 5. root@porteus:/home/guest/netboot# /boot/pxelinux.cfg/web/start.
or 5. root@porteus:/# /boot/pxelinux.cfg/web/start.

Router and DNS server :
To simplify the problem, there is no configuration file. Parameters are provided by the command line.
You can choose any addresses for router and dns. But the router address must stay in the same network as
the client PXE.
This software is not designed for complex networks. In this case you have to provide the address of DHCP-relay.
This is another story...

User avatar
Hamza
Warlord
Warlord
Posts: 1908
Joined: 28 Dec 2010, 07:41
Distribution: Porteus
Location: France

Re: DHCP Server for testing PXE on Porteus 1.1

Post#8 by Hamza » 21 Mar 2012, 17:15

To simplify the problem, there is no configuration file. Parameters are provided by the command line.
Ahh, Sorry. I thought this tool was written to be used only with Private IP Address Range of C Class.

Wonderful tool for people who want to boot up Porteus from another server with tftp :)
NjVFQzY2Rg==

liguero
White ninja
White ninja
Posts: 14
Joined: 16 Mar 2012, 09:13
Location: FRANCE

Re: DHCP Server for testing PXE on Porteus 1.1

Post#9 by liguero » 22 Mar 2012, 08:36

I just tested this software under Porteus-v1.2-rc1.
All is good, at least for that part, ie PXE!

That's all folks!

Publié after 14 hours 58 minutes 45 seconds:
netboot - simple pxe-compatible dhcp and tftp server

With netboot you could easily bootstrap ubuntu or debian netinstaller. Or assign address to network device like ILO to configure it. Very simple in use and need almost zero configuration. I always hated dhcpd and tftp-ha.

Download: netboot.c
To build it: gcc -o netboot netboot.c

Github: http://github.com/ITikhonov/netboot

For looking who is asking for boot:

kef@flash:~$ sudo ./netboot
request 1 from 00-e0-91-02-9e-24 ()
If you are lucky it will show hostname in parens.


Bootfile is hardcoded to pxelinux.0. You could put any file under this name. It will use current directory for serving tftp requests.

Both dhcp and tftp implementations are simplified. For example, tftp can not serve more then one file at time. It has zero security, use it in safe environment only.

ChangeLog:

2008-05-02: show dhcp option 12 hostname if exists

2009-12-02: fixed hanging on nonimplemented options. Thanks to Ian Jeffray for spotting.

2010-01-19: now works on big-endian (ARM for example) cpus. Thanks to Costin Raducanu for testing.

2012-03-21: IP server, Broadcast, subnet mask auto. DHCP options 3 (router) and 6 (dns)

Post Reply