#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();
}
|