File:  [Plan 9 NeXT] / lucent / sys / src / 9 / boot / ip.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 18:01:02 2018 UTC (8 years, 1 month ago) by root
Branches: lucent, MAIN
CVS tags: plan9, HEAD
Plan 9 NeXT

#include <u.h>
#include <libc.h>
#include "../port/bootp.h"
#include "../port/arp.h"
#include "../boot/boot.h"

/*
 *  il 
 */
uchar	fsip[4];
uchar	auip[4];
uchar	gwip[4];
uchar	ipmask[4];
uchar	ipaddr[4];	/* our ip address */
uchar	eaddr[6];	/* our ether address */
uchar 	bcast[6];	/* our ether broadcast address */
uchar	ipnet[4];	/* our ip network number */

static void	arp(uchar*);
static int	arplisten(void);
static ushort	nhgets(uchar*);
static void	hnputs(uchar*, ushort);
static void	parseip(uchar*, char*);
static int	myetheraddr(uchar*, char*);
static int	parseether(uchar*, char*);
static int	mygetfields(char*, char**, int);
static char*	fmtaddr(uchar*);
static void	maskip(uchar*, uchar*, uchar*);
static int	equivip(uchar*, uchar*);
static void	etheripconfig(Method*);
static int	ipdial(int*, char*, uchar*, int);
static void	catchint(void*, char*);

uchar classmask[4][4] = {
	0xff, 0x00, 0x00, 0x00,
	0xff, 0x00, 0x00, 0x00,
	0xff, 0xff, 0x00, 0x00,
	0xff, 0xff, 0xff, 0x00,
};

void
configtcp(Method *mp)
{
	etheripconfig(mp);
}

int
authtcp(void)
{
	return -1;
}

int
connecttcp(void)
{
	int fd[2], rv;

	rv = ipdial(fd, "#Itcp/tcp", fsip, 564);
	if(cpuflag)
		sendmsg(fd[0], "push reboot");
	sendmsg(fd[0], "push fcall");
	if(rv >= 0)
		close(fd[0]);
	return fd[1];
}

void
configil(Method *mp)
{
	etheripconfig(mp);
}

int
authil(void)
{
	int fd[2]; 

	if(auip[0] == 0 || ipdial(fd, "#Iil/il", auip, 566) < 0)
		return -1;
	close(fd[0]);
	return fd[1];
}

int
connectil(void)
{
	int fd[2], rv;

	rv = ipdial(fd, "#Iil/il", fsip, 17008);
	if(cpuflag)
		sendmsg(fd[0], "push reboot");
	if(rv >= 0)
		close(fd[0]);
	return fd[1];
}

static void
etheripconfig(Method *mp)
{
	int efd[2];

	/* configure/open ip */
	myetheraddr(eaddr, "#l/ether");
/*print("my etheraddr is %2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux\n", eaddr[0], eaddr[1],
eaddr[2], eaddr[3], eaddr[4], eaddr[5]);/**/
	if(plumb("#l/ether", "0x800", efd, 0) < 0)
		fatal("opening ip ether");

	sendmsg(efd[0], "push arp");
	sendmsg(efd[0], "push internet");
	sendmsg(efd[0], "push permanent");

	/* do a bootp to find fs, auth server, & gateway */
	bootp(mp, efd[0], 0);

	/* done with the mux */
	close(efd[0]);
	close(efd[1]);
}

/*
 *  configure ip. use bootp to get ip address, net mask, file server ip address,
 *  authentication server ip address and gateway ip address.
 */
void
bootp(Method *mp, int muxctlfd, uchar *useipaddr)
{
	int fd;
	int ufd[2];
	int n;
	int tries;
	Bootp req, *rp;
	char *field[4];
	char buf[1600];
	char buf2[64];
	uchar ipbcast[4];	/* ip broadcast address for bootp */

	/* determine bootp broadcast address from specified file system address */
	if(*sys){
		parseip(fsip, sys);
		parseip(gwip, sys);
		memmove(ipmask, classmask[fsip[0]>>6], sizeof(ipmask));
		for(n = 0; n < sizeof(ipbcast); n++)
			ipbcast[n] = (ipmask[n] & fsip[n]) | ((~ipmask[n])&0xff);
		sprint(buf2, "%s!67", fmtaddr(ipbcast));
	} else {
		memmove(ipmask, classmask[3], sizeof(ipmask));
		strcpy(buf2, "255.255.255.255!67");
	}

	memset(bcast, 0xff, sizeof(bcast));	/* ether broadcast address */

	/* open a udp connection for bootp and fill in a packet */
	if(plumb("#Iudp/udp", buf2, ufd, "68") < 0)
		fatal("opening bootp udp");
	close(ufd[0]);
	memset(&req, 0, sizeof(req));
	req.op = Bootrequest;
	req.htype = 1;		/* ethernet */
	req.hlen = 6;		/* ethernet */
	memmove(req.chaddr, eaddr, sizeof(req.chaddr));
	if(useipaddr != 0)
		memmove(req.ciaddr, useipaddr, sizeof(req.ciaddr));
	memset(req.file, 0, sizeof(req.file));
	strcpy(req.vend, "p9  ");

	/* broadcast bootp's till we get a reply, or 3 times around the loop */
	notify(catchint);
	tries = 0;
	field[0] = 0;
	for(rp = 0; rp == 0 && tries++ < 10;){
		alarm(1000);
		if(write(ufd[1], &req, sizeof(req)) < 0)
			fatal("sending bootp");
		for(;;){
			rp = 0;
			memset(buf, 0, sizeof(buf));
			n = read(ufd[1], buf, sizeof(buf));
			if(n <= 0)
				break;
			rp = (Bootp*)buf;
			memset(field, 0, sizeof field);
			if(memcmp(req.chaddr, rp->chaddr, 6) == 0
			&& rp->htype == 1
			&& rp->hlen == 6
			&& mygetfields(rp->vend+4, field, 4) == 4){
				if(strncmp(rp->vend, "p9  ", 4) == 0){
					memmove(ipaddr, rp->yiaddr, sizeof(ipaddr));
					parseip(ipmask, field[0]);
					if(*sys == 0){
						strcpy(sys, field[1]);
						parseip(fsip, field[1]);
					}
					parseip(auip, field[2]);
					parseip(gwip, field[3]);
					maskip(ipaddr, ipmask, ipnet);
					if(bootfile[0] == 0){
						strncpy(bootfile, rp->file,
							 3*NAMELEN);
						bootfile[3*NAMELEN-1] = 0;
					}
					break;
				}
			}
		}
		alarm(0);
	}
	close(ufd[1]);

	if(field[0])
		/*print("I am %s sub %s fs %s au %s gw %s\n",
		fmtaddr(ipaddr), field[0], sys, field[2], field[3])/**/;
	else {
		errstr(buf);	/* Clear timeout error from alarm */

		if(readfile("#e/ipaddr", buf2, sizeof(buf2)) < 0)
			strcpy(buf2, "");
		outin(0, "My IP address", buf2, sizeof(buf2));
		parseip(ipaddr, buf2);

		if(readfile("#e/ipmask", buf2, sizeof(buf2)) < 0)
			strcpy(buf2, "");
		outin(0, "My IP mask", buf2, sizeof(buf2));
		parseip(ipmask, buf2);
		maskip(ipaddr, ipmask, ipnet);

		if(readfile("#e/ipgw", buf2, sizeof(buf2)) < 0)
			strcpy(buf2, "");
		outin(0, "My IP gateway", buf2, sizeof(buf2));
		parseip(gwip, buf2);

		if(*sys)
			strcpy(buf2, sys);
		else {
			if(readfile("#e/fs", buf2, sizeof(buf2)) < 0)
				strcpy(buf2, "");
			outin(0, "filesystem IP address", buf2, sizeof(buf2));
		}
		parseip(fsip, buf2);

		if(readfile("#e/auth", buf2, sizeof(buf2)) < 0)
			strcpy(buf2, "0.0.0.0");
		outin(0, "authentication server IP address", buf2, sizeof(buf2));
		parseip(auip, buf2);
	}

	if(auip[0] == 0 && auip[1] == 0)
		mp->auth = 0;

	/* set our ip address and mask */
	n = sprint(buf2, "setip %s ", fmtaddr(ipaddr));
	sprint(buf2+n, "%s", fmtaddr(ipmask));
	sendmsg(muxctlfd, buf2);

	/* specify a routing gateway */
	if(*gwip){
		sprint(buf2, "add 0.0.0.0 0.0.0.0 %s", fmtaddr(gwip));
		fd = open("#P/iproute", OWRITE);
		if(fd < 0)
			fatal("opening iproute");
		if(sendmsg(fd, buf2) < 0)
			print("%s failed\n", buf2);
		close(fd);
	}
}

static int
ipdial(int *ifd, char *dev, uchar *ip, int service)
{
	uchar tmp[4];
	char buf[64];
	int arpnotefd;

	/* start a process to answer arps */
	arpnotefd = arplisten();

	/* arp for first hop */
	maskip(ip, ipmask, tmp);
	if(equivip(tmp, ipnet))
		arp(ip);
	else
		arp(gwip);

	/* make the call */
	sprint(buf, "%s!%d", fmtaddr(ip), service);
	if(plumb(dev, buf, ifd, 0) < 0){
		fprint(2, "error dialing %s\n", buf);
		ifd[1] = -1;
	}

	fprint(arpnotefd, "kill");

	return ifd[1];
}

/* send an arprequest, wait for a reply */
static void
arp(uchar *addr)
{
	int afd[2];
	int arpdev;
	int n;
	Arpentry entry;
	Arppkt req, *rp;
	char buf[1600];

	if(plumb("#l/ether", "0x806", afd, 0) < 0)
		fatal("opening ip ether");
	close(afd[0]);
	arpdev = open("#a/arp/data", OWRITE);
	if(arpdev < 0)
		fatal("opening arp/data");

	/* arp for the file server or the gateway */
	memset(&req, 0, sizeof(req));
	memmove(req.tpa, addr, sizeof(req.tpa));
	memset(req.d, 0xff, sizeof(req.d));
	memmove(req.spa, ipaddr, sizeof(ipaddr));
	memmove(req.sha, eaddr, sizeof(eaddr));
	hnputs(req.type, ET_ARP);
	hnputs(req.hrd, 1);
	hnputs(req.pro, 0x800);
	req.hln = sizeof(req.sha);
	req.pln = sizeof(req.spa);
	hnputs(req.op, ARP_REQUEST);
	for(rp = 0; rp == 0;){
		if(write(afd[1], &req, sizeof(req)) < 0)
			fatal("sending arpreq");
		alarm(1000);
		for(;;){
			rp = 0;
			memset(buf, 0, sizeof(buf));
			n = read(afd[1], buf, sizeof(buf));
			if(n <= 0)
				break;
			rp = (Arppkt*)buf;
			if(nhgets(rp->op) != ARP_REPLY)
				continue;
			memcpy(entry.etaddr, rp->sha, sizeof(entry.etaddr));
			memcpy(entry.ipaddr, rp->spa, sizeof(entry.ipaddr));
			if(write(arpdev, &entry, sizeof(entry)) < 0)
				warning("write arp entry");
print("arp: rcvd for %s\n", fmtaddr(rp->spa));/**/
			if(equivip(rp->spa, addr))
				break;
		}
		alarm(0);
	}

	close(arpdev);
	close(afd[1]);
}

/*
 *  fork a process to answer arp requests for us.  since
 *  we will kill it after changing user id, we need to open the
 *  note process here.  arplisten returns the fd of the open note
 *  process.
 */
static int
arplisten(void)
{
	int afd[2];
	int n;
	int pid;
	Arppkt reply, *rp;
	char buf[1600];

	alarm(0);
	notify(catchint);

	switch(pid = fork()){
	case -1:
		fatal("forking arplisten");
	case 0:
		break;
	default:
		sprint(buf, "#p/%d/note", pid);
		return open(buf, OWRITE);
	}

	if(plumb("#l/ether", "0x806", afd, 0) < 0)
		fatal("opening ip ether");

	for(;;){
		memset(buf, 0, sizeof(buf));
		n = read(afd[1], buf, sizeof(buf));
		if(n < 0)
			break;
		if(n == 0)
			continue;
		rp = (Arppkt*)buf;
		if(nhgets(rp->op) != ARP_REQUEST)
			continue;
		if(memcmp(rp->tpa, ipaddr, sizeof(ipaddr)) != 0)
			continue;

		memset(&reply, 0, sizeof(reply));
		hnputs(reply.type, ET_ARP);
		hnputs(reply.hrd, 1);
		hnputs(reply.pro, 0x800);
		reply.hln = sizeof(reply.sha);
		reply.pln = sizeof(reply.spa);
		hnputs(reply.op, ARP_REPLY);
		memmove(reply.tha, rp->sha, sizeof(reply.tha));
		memmove(reply.tpa, rp->spa, sizeof(reply.tpa));
		memmove(reply.sha, eaddr, sizeof(eaddr));
		memmove(reply.spa, ipaddr, sizeof(ipaddr));
		memmove(reply.d, rp->s, sizeof(rp->s));
print("arplisten: reply to %s\n", fmtaddr(reply.tpa));/**/
		if(write(afd[1], &reply, ARPSIZE) < 0)
			warning("write arp reply");
	}
	exits(0);
	return 0;	/* not reached */
}

static ushort
nhgets(uchar *val)
{
	return (val[0]<<8) | val[1];
}

static void
hnputs(uchar *ptr, ushort val)
{
	ptr[0] = val>>8;
	ptr[1] = val;
}

int
myipaddr(uchar *to, char *dev)
{
	char buf[256];
	int n, fd, clone;
	char *ptr;

	/* Opening clone ensures the 0 connection exists */
	sprint(buf, "%s/clone", dev);
	clone = open(buf, OREAD);
	if(clone < 0)
		return -1;

	sprint(buf, "%s/0/local", dev);
	fd = open(buf, OREAD);
	close(clone);
	if(fd < 0)
		return -1;
	n = read(fd, buf, sizeof(buf)-1);
	close(fd);
	if(n <= 0)
		return -1;
	buf[n] = 0;

	ptr = strchr(buf, ' ');
	if(ptr)
		*ptr = 0;

	parseip(to, buf);
	return 0;
}

#define CLASS(p) ((*(uchar*)(p))>>6)
static void
parseip(uchar *to, char *from)
{
	int i;
	char *p;

	p = from;
	memset(to, 0, 4);
	for(i = 0; i < 4 && *p; i++){
		to[i] = strtoul(p, &p, 0);
		if(*p == '.')
			p++;
	}

	switch(CLASS(to)){
	case 0:	/* class A - 1 byte net */
	case 1:
		if(i == 3){
			to[3] = to[2];
			to[2] = to[1];
			to[1] = 0;
		} else if (i == 2){
			to[3] = to[1];
			to[1] = 0;
		}
		break;
	case 2:	/* class B - 2 byte net */
		if(i == 3){
			to[3] = to[2];
			to[2] = 0;
		}
		break;
	}
}

static int
myetheraddr(uchar *to, char *dev)
{
	char buf[256];
	int n, fd;
	char *ptr;

	sprint(buf, "%s/1/stats", dev);
	fd = open(buf, OREAD);
	if(fd < 0)
		return -1;
	n = read(fd, buf, sizeof(buf)-1);
	close(fd);
	if(n <= 0)
		return -1;
	buf[n] = 0;

	ptr = strstr(buf, "addr: ");
	if(!ptr)
		return -1;
	ptr += 6;

	parseether(to, ptr);
	return 0;
}

static int
parseether(uchar *to, char *from)
{
	char nip[4];
	char *p;
	int i;

	p = from;
	for(i = 0; i < 6; i++){
		if(*p == 0)
			return -1;
		nip[0] = *p++;
		if(*p == 0)
			return -1;
		nip[1] = *p++;
		nip[2] = 0;
		to[i] = strtoul(nip, 0, 16);
		if(*p == ':')
			p++;
	}
	return 0;
}

static int
mygetfields(char *lp, char **fields, int n)
{
	int i;

	for(i=0; lp && *lp && i<n; i++){
		while(*lp == ' ' || *lp == '\t')
			*lp++=0;
		if(*lp == 0)
			break;
		fields[i]=lp;
		while(*lp && *lp != ' ' && *lp != '\t')
			lp++;
	}
	return i;
}

static char*
fmtaddr(uchar *a)
{
	static char buf[32];

	sprint(buf, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]);
	return buf;
}

static void
catchint(void *a, char *note)
{
	USED(a);
	if(strstr(note, "alarm"))
		noted(NCONT);
	else
		noted(NDFLT);
}

static void
maskip(uchar *a, uchar *m, uchar *n)
{
	int i;

	for(i = 0; i < 4; i++)
		n[i] = a[i] & m[i];
}

static int
equivip(uchar *a, uchar *b)
{
	int i;

	for(i = 0; i < 4; i++)
		if(a[i] != b[i])
			return 0;
	return 1;
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.