Plan 9 from Bell Labs’s /usr/web/sources/contrib/steve/root/sys/src/cmd/ndb/glean/dhcp.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#include <u.h>
#include <libc.h>
#include <ip.h>
#include <bio.h>
#include <ndb.h>
#include "nbcache.h"

enum {
	Tnop,
	Tip,
	TNip,
	Tname,
	Tint8,
	Tint16,
	Tint32,
	Tlower,

	Omask = 1,		/* netmask */
	Osysname = 12,		/* system name */
	Odnsdom = 15,		/* fully qualified domain name */
	Olease = 51, 		/* lease time */
	Oip = 50,		/* ip address */
};

typedef struct {
	int id;
	char *name;
	int type;
	int len;
	union {
		int num;
		char *str;
		uchar *ip;
	};
} Opt;

static Opt Opts[] = {
	
	{ 0,	"nop",		Tnop },		/* no length, NOP / padding */
	{ 1,	"mask",		Tip },		/* no length, Net mask */
	{ 2,	"time-diff",	Tint32 },	/* no length, GMT time, 32 bits */
	{ 3,	"gateway",	TNip },		/* N/4 Router addresses */
	{ 6,	"dns",		TNip },		/* N/4 DNS Server addresses */
	{ 9,	"print",	TNip },		/* N/4 Printer Server addresses */
	{ 12,	"sys",		Tlower },	/* Hostname string */
	{ 15,	"dnsdom",	Tlower },	/* The DNS domain name of the client */
	{ 42,	"ntp",		TNip },		/* NTP Server Addresses */
//	{ 43,	nil,		Traw },		/* Vendor specific information */
	{ 44,	"nbns",		Tlower },	/* NETBIOS Name Servers */
	{ 45,	"nbdd",		Tlower },	/* NETBIOS datagram service */
	{ 46,	"nbtype",	Tlower },	/* NETBIOS node type */
	{ 47,	"nbscope",	Tlower },	/* NETBIOS Name scope */
	{ 50,	"ip",		TNip },		/* requested IP address */
	{ 51,	"ttl",		Tint32 },	/* IP address lease time */
	{ 53,	nil,		Tint8 },	/* DHCP subtype */
//
// 	{ 55,	nil,		Traw },		/* parameter request list */
// 	{ 60,	nil,		Traw },		/* vendor class identifier  */
// 	{ 61,	nil,		Traw },		/* Client identifier */
//
	{ 66,	"tftp",		Tlower },	/* TFTP Server Name */
	{ 67,	"bootf",	Tname },	/* Boot File Name */
	{ 69,	"smtp",		TNip },		/* Simple Mail Server Addresses */
	{ 70,	"pop3",		TNip },		/* Post Office Server Addresses */
	{ 71,	"nntp",		TNip },		/* Network News Server Addresses */
	{ 81,	"dom",		Tlower },	/* Fully Qualified Domain Name */
};

static Opt *
lookopt(int n)
{
	Opt *o;

	for(o = Opts; o < &Opts[nelem(Opts)]; o++)
		if(n == o->id)
			return o;
	return nil;
}

static char *
stropt(int n)
{
	Opt *o;

	for(o = Opts; o < &Opts[nelem(Opts)]; o++)
		if(n == o->id && o->len != 0)
			return o->str;
	return nil;
}

static int
numopt(int n)
{
	Opt *o;

	for(o = Opts; o < &Opts[nelem(Opts)]; o++)
		if(n == o->id && o->len != 0)
			return o->num;
	return -1;
}

static uchar *
ipopt(int n)
{
	Opt *o;

	for(o = Opts; o < &Opts[nelem(Opts)]; o++)
		if(n == o->id && o->len != 0)
			return o->ip;
	return nil;
}

static uchar *
ipnopt(int n, int *num)
{
	Opt *o;

	for(o = Opts; o < &Opts[nelem(Opts)]; o++)
		if(n == o->id && o->len != 0){
			*num = o->len;
			return o->ip;
		}
	return nil;
}

static void
freeopts(void)
{
	Opt *o;

	for(o = Opts; o < &Opts[nelem(Opts)]; o++)
		switch(o->type){
		case Tnop:
		case Tint8:
		case Tint16:
		case Tint32:
			o->len = 0;
			break;
		case Tip:
		case TNip:
			free(o->ip);
			o->ip = nil;
			o->len = 0;
			break;
		case Tname:
		case Tlower:
			free(o->str);
			o->str = nil;
			o->len = 0;
			break;
		default:
			sysfatal("dhcp: type=%d unknown\n", o->type);
			break;
		}
}

static void
network(void)
{
	Node *n;
	Attr *a;
	char *serv, ipstr[20];
	int lease, i, ndns, ttl;
	uchar net[IPv4addrlen], *mask, *gate, *dhcp, *dns;

	mask = ipopt(1);
	gate = ipopt(3);
	dhcp = ipopt(54);
	ttl = numopt(54);
	dns = ipnopt(6, &ndns);

	if(gate == nil || mask == nil)
		return;

	for(i = 0; i < IPv4addrlen; i++)
		net[i] = gate[i] & mask[i];

	n = getnode(Thost, ttl, "net-%V", net);
	if((lease = numopt(54)) != -1)
		setval(n, "leases", "%d", lease);
	setval(n, "ipgw", "%V", gate);
	setval(n, "ipmask", "%V", mask);

	SET(a);
	if(ndns > 0)
		a = setval(n, "dns", "%V", dns);
	for(i = 1; i < ndns; i++)
		addval(a, "dns", "%V", dns + i*IPv4addrlen);

	if(dhcp){
		snprint(ipstr, sizeof(ipstr), "%V", dhcp);
		serv = csgetvalue(Netdir, "ip", ipstr, "sys", nil);
		if(serv == nil)
			serv = csgetvalue(Netdir, "ip", ipstr, "dom", nil);
		if(serv == nil)
			setval(n, "dhcp", "%s", ipstr);
		else
			setval(n, "dhcp", "%d", serv);
	}
	if(Debug)
		setval(n, "src", "dhcp-net");
}

static void
host(uchar mac[Etheraddrlen], uchar ip[IPv4addrlen])
{
	Node *n;
	Attr *a;
	char *name, *vendor;

	if((name = stropt(12)) == nil)
		return;

	n = getnode(Thost, -1, "%s", name);
	a = setval(n, "ip", "%V", ip);
	addval(a, "ether", "%E", mac);
	if(Debug)
		addval(a, "src", "dhcp-host");

	if((vendor = nicvendor(mac)) != nil){
		addval(a, "vendor", "%s", vendor);
		free(vendor);
	}
}
void
dhcp(Pkt *p)
{
	Opt *o;
	int subtype, x, id;
	char bp_servname[64], bp_bootfile[128];
	uchar bp_myip[IPv4addrlen], bp_mac[Etheraddrlen];
	uchar bp_yourip[IPv4addrlen], bp_srvip[IPv4addrlen], bp_gateip[IPv4addrlen];
	static uchar zeros[IPv4addrlen];

	r8(p);					/* DHCP message type */
	if(r8(p) != 1)				/* Phys medium (Ethernet) */
		return;
	if(r8(p) != Etheraddrlen)		/* MAC address length (Ethernet) */
		return;

	skip(p, 1);				/* hops */
	skip(p, 4);				/* transaction ID */
	skip(p, 2);				/* uptime of client */
	skip(p, 2);				/* flags */

	rmem(p, bp_myip, IPv4addrlen);		/* client's view of its IP addr */
	rmem(p, bp_yourip, IPv4addrlen);	/* server's view of client's IP addr */
	rmem(p, bp_srvip, IPv4addrlen);		/* server's IP address */
	rmem(p, bp_gateip, IPv4addrlen);	/* gateway's IP addr */
	rmem(p, bp_mac, Etheraddrlen);		/* client's MAC address */
	skip(p, 16-Etheraddrlen);
	rmem(p, bp_servname, 64);		/* server's hostname */
	rmem(p, bp_bootfile, 128);		/* server's hostname */

	if(rb32(p) != 0x63825363)		/* DHCP magic */
		return;

	if(p->end - p->pos < 1)			/* No more data (??!) */
		return;

	while((id = r8(p)) != 255){
		if((o = lookopt(id)) == nil){
			x = r8(p);
			skip(p, x);
			/* fprint(2, "dhcp: ignored id=%d len=%d\n", id, x); */
			continue;
		}
		switch(o->type){
		case Tnop:
			continue;
		case Tip:
			o->len = IPv4addrlen;
			if((o->ip = malloc(IPv4addrlen)) == nil)
				sysfatal("No memory %r\n");
			rmem(p, o->ip, IPv4addrlen);
			break;
		case TNip:
			x = r8(p);
			o->len = x;
			if((o->ip = malloc(x)) == nil)
				sysfatal("No memory %r\n");
			rmem(p, o->ip, x);
			break;
		case Tname:
			x = r8(p);
			o->len = x;
			if((o->str = malloc(x+1)) == nil)
				sysfatal("No memory %r\n");
			o->str[x] = 0;
			rmem(p, o->str, x);
			break;
		case Tlower:
			x = r8(p);
			o->len = x;
			if((o->str = malloc(x+1)) == nil)
				sysfatal("No memory %r\n");
			rmem(p, o->str, x);
			o->str[x] = 0;
			strlwr(o->str);
			break;
		case Tint8:
			o->len = r8(p);
			o->num = r8(p);
			break;
		case Tint16:
			o->len = r8(p);
			o->num = rb16(p);
			break;
		case Tint32:
			o->len = r8(p);
			o->num = rb32(p);
			break;
		default:
			sysfatal("dhcp: type=%d unknown\n", o->type);
			break;
		}
	}

	subtype = numopt(53);
	switch(subtype){
	case 5:			/* ack */
		network();
		break;
	case 8:			/* inform */
		host(bp_mac, bp_myip);
		break;
	case 2:			/* offer	(more info in following ack) */
	case 3:			/* request	(more info in following ack) */
	case 1:			/* discover	(more info in following ack) */
	case 4:			/* decline	(never seen one) */
	case 6:			/* nak		(never seen one) */
	default:
		break;
	}
	freeopts();

}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].