ip/ipconfig: implement dhcpv6 prefix delegation, dynamic client

Implement prefix delegation by requesting a
prefix and populate ipnet=val entry (val
passed from -i option).

Before, DHCPv6 was just implemented for stateless
one-shot operation, just exiting once we got out
IA address.

Moodies mediacom-enterprise-enterprise-ISP...
... they actually do enterprise-grade dyanmic dhcpv6
so here we are, implementing renewals...
This commit is contained in:
cinap_lenrek 2024-10-28 19:06:47 +00:00
parent 3e4e1b0211
commit 5f59652ae1
5 changed files with 442 additions and 132 deletions

View file

@ -16,6 +16,8 @@ ipconfig, rip, linklocal \- Internet configuration and routing
.IR gateway ]
.RB [ -h
.IR host ]
.BR [ -i
.IR ipnet ]
.RB [ -m
.IR mtu ]
.RB [ -o
@ -192,6 +194,15 @@ the hostname to add to DHCP requests. Some DHCP
servers, such as the one used by Comcast, will not respond
unless a correct hostname is in the request.
.TP
.B i
when writing
.B /net/ndb
configuration, create an
.B ipnet=
tuple with the value of
.I ipnet
and the gathered network attributes.
.TP
.B m
the maximum IP packet size to use on this interface.
.TP

View file

@ -63,15 +63,59 @@ openlisten(void)
}
static int
transaction(int fd, int type, int timeout)
findopt(int id, uchar **sp, uchar *e)
{
uchar *p;
int opt;
int len;
p = *sp;
while(p + 4 <= e) {
opt = (int)p[0] << 8 | p[1];
len = (int)p[2] << 8 | p[3];
p += 4;
if(p + len > e)
break;
if(opt == id){
*sp = p;
return len;
}
p += len;
}
return -1;
}
static int
badstatus(int type, int opt, uchar *p, uchar *e)
{
int len, status;
len = findopt(13, &p, e);
if(len < 0)
return 0;
if(len < 2)
return 1;
status = (int)p[0] << 8 | p[1];
if(status == 0)
return 0;
warning("dhcpv6: bad status in request/response type %x, option %d, status %d: %.*s",
type, opt, status, len - 2, (char*)p + 2);
return status;
}
static int
transaction(int fd, int type, int irt, int retrans, int timeout)
{
union {
Udphdr;
uchar buf[4096];
} ipkt, opkt;
int tra, opt, len, status, sleepfor, jitter;
uchar *p, *e, *x;
int tra, opt, len, sleepfor;
ulong t1, apflt;
conf.lease = ~0UL; /* infinity */
tra = lrand() & 0xFFFFFF;
@ -98,8 +142,8 @@ transaction(int fd, int type, int timeout)
/* IA for non-temporary address */
len = 12;
if(validip(conf.laddr))
len += 4 + IPaddrlen + 2*4;
if(validv6prefix(conf.laddr))
len += 4 + IPaddrlen+2*4;
*p++ = 0x00; *p++ = 0x03;
*p++ = len >> 8;
*p++ = len;
@ -110,19 +154,53 @@ transaction(int fd, int type, int timeout)
*p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;
if(len > 12){
*p++ = 0x00; *p++ = 0x05;
*p++ = 0x00; *p++ = IPaddrlen + 2*4;
*p++ = 0x00; *p++ = IPaddrlen+2*4;
memmove(p, conf.laddr, IPaddrlen);
p += IPaddrlen;
memset(p, 0xFF, 2*4);
p += 2*4;
}
/* IA for prefix delegation */
len = 12;
if(validv6prefix(conf.v6pref))
len += 4 + 2*4+1+IPaddrlen;
*p++ = 0x00; *p++ = 0x19;
*p++ = len >> 8;
*p++ = len;
/* IAID */
*p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x02; /* lies */
/* T1, T2 */
*p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;
*p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;
if(len > 12){
*p++ = 0x00; *p++ = 0x1a;
*p++ = 0x00; *p++ = 2*4+1+IPaddrlen;
*p++ = conf.preflt >> 24;
*p++ = conf.preflt >> 16;
*p++ = conf.preflt >> 8;
*p++ = conf.preflt;
*p++ = conf.validlt >> 24;
*p++ = conf.validlt >> 16;
*p++ = conf.validlt >> 8;
*p++ = conf.validlt;
*p++ = conf.prefixlen;
ipmove(p, conf.v6pref);
p += IPaddrlen;
}
/* Option Request */
*p++ = 0x00; *p++ = 0x06;
*p++ = 0x00; *p++ = 0x02;
*p++ = 0x00; *p++ = 0x17; /* DNS servers */
if(sidlen > 0){
/* server identifier */
if(sidlen > 0
&& type != SOLICIT && type != CONFIRM && type != REBIND){
*p++ = 0x00; *p++ = 0x02;
/* len */
*p++ = sidlen >> 8;
@ -132,9 +210,13 @@ transaction(int fd, int type, int timeout)
}
len = -1;
for(sleepfor = 500; timeout > 0; sleepfor <<= 1){
for(sleepfor = irt; timeout > 0; sleepfor <<= 1){
DEBUG("sending dhcpv6 request %x", opkt.buf[Udphdrsize]);
jitter = sleepfor / 10;
if(jitter > 1)
sleepfor += nrand(jitter);
alarm(sleepfor);
if(len < 0)
write(fd, opkt.buf, p - opkt.buf);
@ -144,9 +226,14 @@ transaction(int fd, int type, int timeout)
timeout -= sleepfor;
if(len == 0)
break;
if(len < 0){
if(--retrans == 0)
break;
continue;
}
if(len < Udphdrsize+4)
continue;
if(ipkt.buf[Udphdrsize+1] != ((tra>>16)&0xFF)
|| ipkt.buf[Udphdrsize+2] != ((tra>>8)&0xFF)
|| ipkt.buf[Udphdrsize+3] != ((tra>>0)&0xFF))
@ -158,6 +245,8 @@ transaction(int fd, int type, int timeout)
switch(type){
case ADVERTISE << 8 | SOLICIT:
case REPLY << 8 | REQUEST:
case REPLY << 8 | RENEW:
case REPLY << 8 | REBIND:
goto Response;
default:
return -1;
@ -177,74 +266,174 @@ Response:
if (x > e)
return -1;
DEBUG("got dhcpv6 option %x: [%d] %.*H", opt, len, len, p);
DEBUG("got dhcpv6 option %d: [%d] %.*H", opt, len, len, p);
switch(opt){
case 0x01: /* client identifier */
case 1: /* client identifier */
continue;
case 0x02: /* server identifier */
case 2: /* server identifier */
if(len < 1 || len > sizeof(sid))
break;
sidlen = len;
memmove(sid, p, sidlen);
continue;
case 0x03: /* IA for non-temporary address */
case 3: /* IA for non-temporary address */
if(p+12 > x)
break;
t1 = (ulong)p[4] << 24 |
(ulong)p[5] << 16 |
(ulong)p[6] << 8 |
(ulong)p[7];
/* skip IAID, T1, T2 */
p += 12;
/* find IA Address option */
while(p + 4 <= x) {
opt = (int)p[0] << 8 | p[1];
len = (int)p[2] << 8 | p[3];
p += 4;
if(p + len > x)
break;
if(opt == 5)
break;
p += len;
}
status = badstatus(type, opt, p, x);
if(status != 0)
return -status;
/* IA Addresss */
if(opt != 5)
if(findopt(5, &p, x) < IPaddrlen + 2*4)
break;
if(len < IPaddrlen)
break;
memmove(conf.laddr, p, IPaddrlen);
ipmove(conf.laddr, p);
memset(conf.mask, 0xFF, IPaddrlen);
p += IPaddrlen;
/* preferred lifetime of IA Address */
apflt = (ulong)p[0] << 24 |
(ulong)p[1] << 16 |
(ulong)p[2] << 8 |
(ulong)p[3];
/* adjust lease */
if(t1 != 0 && t1 < conf.lease)
conf.lease = t1;
if(apflt != 0 && apflt < conf.lease)
conf.lease = apflt;
continue;
case 0x17: /* dns servers */
case 13: /* status */
status = badstatus(type, opt, p - 4, x);
if(status != 0)
return -status;
continue;
case 23: /* dns servers */
if(len % IPaddrlen)
break;
addaddrs(conf.dns, sizeof(conf.dns), p, len);
continue;
case 25: /* IA for prefix delegation */
if(p+12 > x)
break;
t1 = (ulong)p[4] << 24 |
(ulong)p[5] << 16 |
(ulong)p[6] << 8 |
(ulong)p[7];
/* skip IAID, T1, T2 */
p += 12;
status = badstatus(type, opt, p, x);
if(status != 0){
if(type == (ADVERTISE << 8 | SOLICIT))
continue;
return -status;
}
/* IA Prefix */
if(findopt(26, &p, x) < 2*4+1+IPaddrlen)
break;
conf.preflt = (ulong)p[0] << 24 |
(ulong)p[1] << 16 |
(ulong)p[2] << 8 |
(ulong)p[3];
conf.validlt = (ulong)p[4] << 24 |
(ulong)p[5] << 16 |
(ulong)p[6] << 8 |
(ulong)p[7];
p += 8;
if(conf.preflt > conf.validlt)
break;
conf.prefixlen = *p++ & 127;
genipmask(conf.v6mask, conf.prefixlen);
maskip(p, conf.v6mask, conf.v6pref);
/* adjust lease */
if(t1 != 0 && t1 < conf.lease)
conf.lease = t1;
if(conf.preflt != 0 && conf.preflt < conf.lease)
conf.lease = conf.preflt;
continue;
default:
DEBUG("unknown dhcpv6 option %x", opt);
DEBUG("unknown dhcpv6 option: %d", opt);
continue;
}
warning("dhcpv6: malformed option %x: [%d] %.*H", opt, len, len, x-len);
warning("dhcpv6: malformed option %d: [%d] %.*H", opt, len, len, x-len);
}
return 0;
}
void
dhcpv6query(void)
int
dhcpv6query(int renew)
{
int fd;
ipmove(conf.laddr, IPnoaddr);
memset(conf.mask, 0xFF, IPaddrlen);
if(!renew){
ipmove(conf.laddr, IPnoaddr);
ipmove(conf.v6pref, IPnoaddr);
ipmove(conf.v6mask, IPnoaddr);
conf.prefixlen = 0;
conf.preflt = 0;
conf.validlt = 0;
conf.autoflag = 0;
conf.onlink = 0;
}
if(conf.duidlen <= 0)
return;
return -1;
fd = openlisten();
if(fd < 0)
return;
if(transaction(fd, SOLICIT, 5000) < 0)
goto out;
if(!validip(conf.laddr))
goto out;
if(transaction(fd, REQUEST, 10000) < 0)
goto out;
out:
return -1;
if(renew){
if(!validv6prefix(conf.laddr))
goto fail;
/*
* the standard says 600 seconds for maxtimeout,
* but this seems ridiculous. better start over.
*/
if(transaction(fd, RENEW, 10*1000, 0, 30*1000) < 0){
if(!validv6prefix(conf.laddr))
goto fail;
if(transaction(fd, REBIND, 10*1000, 0, 30*1000) < 0)
goto fail;
}
} else {
/*
* the standard says SOL_MAX_RT is 3600 seconds,
* but it is better to fail quickly here and wait
* for the next router advertisement.
*/
if(transaction(fd, SOLICIT, 1000, 0, 10*1000) < 0)
goto fail;
if(!validv6prefix(conf.laddr))
goto fail;
if(transaction(fd, REQUEST, 1000, 10, 30*1000) < 0)
goto fail;
}
if(!validv6prefix(conf.laddr))
goto fail;
close(fd);
return 0;
fail:
close(fd);
return -1;
}

View file

@ -45,6 +45,7 @@ struct Conf
uchar laddr[IPaddrlen];
uchar mask[IPaddrlen];
uchar raddr[IPaddrlen];
uchar dns[8*IPaddrlen];
uchar fs[2*IPaddrlen];
uchar auth[2*IPaddrlen];
@ -69,6 +70,7 @@ struct Conf
/*
* IPv6
*/
uchar v6router[IPaddrlen];
/* router-advertisement related */
uchar sendra;
@ -86,6 +88,7 @@ struct Conf
/* prefix related */
uchar lladdr[IPaddrlen];
uchar v6pref[IPaddrlen];
uchar v6mask[IPaddrlen];
int prefixlen;
uchar onlink; /* flag: address is `on-link' */
uchar autoflag; /* flag: autonomous */
@ -118,13 +121,14 @@ extern int dupl_disc;
extern int nodhcpwatch;
extern int sendhostname;
extern char *ndboptions;
extern char *ipnet; /* put ipnet= tuple in ndb for raddr */
void usage(void);
int ip4cfg(void);
void ipunconfig(void);
void adddefroute(uchar*, uchar*, uchar*, uchar*);
void deldefroute(uchar*, uchar*, uchar*, uchar*);
void adddefroute(uchar*, uchar*, uchar*, uchar*, uchar*);
void deldefroute(uchar*, uchar*, uchar*, uchar*, uchar*);
int myip(Ipifc*, uchar*);
int isether(void);
@ -163,9 +167,10 @@ void doipv6(int);
void ea2lla(uchar *lla, uchar *ea);
int findllip(uchar *ip, Ipifc *ifc);
int ip6cfg(void);
int validv6prefix(uchar *ip);
void genipmask(uchar *mask, int len);
/*
* DHCPv6
*/
void dhcpv6init(void);
void dhcpv6query(void);
int dhcpv6query(int);

View file

@ -198,6 +198,7 @@ parse6pref(int argc, char **argv)
sysfatal("bad address %s", argv[0]);
break;
}
genipmask(conf.v6mask, conf.prefixlen);
DEBUG("parse6pref: pref %I len %d", conf.v6pref, conf.prefixlen);
}
@ -391,7 +392,7 @@ Again:
if(validip(conf.gaddr) && !isv4(conf.gaddr)
&& ipcmp(conf.gaddr, conf.laddr) != 0
&& ipcmp(conf.gaddr, conf.lladdr) != 0)
adddefroute(conf.gaddr, conf.laddr, conf.laddr, conf.mask);
adddefroute(conf.gaddr, conf.laddr, conf.laddr, conf.raddr, conf.mask);
return 0;
}
@ -530,8 +531,8 @@ masklen(uchar *mask)
return len;
}
static void
genipmkask(uchar *mask, int len)
void
genipmask(uchar *mask, int len)
{
memset(mask, 0, IPaddrlen);
if(len < 0)
@ -563,11 +564,27 @@ struct Route
static Route *routelist;
int
validv6prefix(uchar *ip)
{
if(!validip(ip))
return 0;
if(isv4(ip))
return 0;
if(ipcmp(ip, v6loopback) == 0)
return 0;
if(ISIPV6MCAST(ip))
return 0;
if(ISIPV6LINKLOCAL(ip))
return 0;
return 1;
}
/*
* host receiving a router advertisement calls this
*/
static int
recvrahost(uchar buf[], int pktlen)
recvrahost(uchar buf[], int pktlen, ulong now)
{
char dnsdomain[sizeof(conf.dnsdomain)];
int m, n, optype, seen;
@ -576,9 +593,9 @@ recvrahost(uchar buf[], int pktlen)
Prefixopt *prfo;
Ipaddrsopt *addrso;
Routeradv *ra;
uchar raddr[IPaddrlen];
uchar hash[SHA1dlen];
Route *r, **rr;
ulong now;
m = sizeof *ra;
ra = (Routeradv*)buf;
@ -590,6 +607,13 @@ recvrahost(uchar buf[], int pktlen)
DEBUG("got RA from %I on %s; flags %x", ra->src, conf.dev, ra->mor);
/*
* ignore all non-managed-flag router-advertisements
* when dhcpv6 is active so we do not trash conf data.
*/
if(dodhcp && conf.state && (MFMASK & ra->mor) == 0)
return -1;
conf.ttl = ra->cttl;
conf.mflag = (MFMASK & ra->mor) != 0;
conf.oflag = (OCMASK & ra->mor) != 0;
@ -598,6 +622,8 @@ recvrahost(uchar buf[], int pktlen)
conf.rxmitra = nhgetl(ra->rxmtimer);
conf.linkmtu = DEFMTU;
ipmove(conf.v6router, conf.routerlt? ra->src: IPnoaddr);
memset(conf.dns, 0, sizeof(conf.dns));
memset(conf.fs, 0, sizeof(conf.fs));
memset(conf.auth, 0, sizeof(conf.auth));
@ -657,8 +683,6 @@ recvrahost(uchar buf[], int pktlen)
issuebasera6(&conf);
now = time(nil);
/* remove expired default routes */
m = 0;
for(rr = &routelist; (r = *rr) != nil;){
@ -667,8 +691,10 @@ recvrahost(uchar buf[], int pktlen)
|| r->routerlt != ~0UL && r->routerlt < now-r->time){
DEBUG("purging RA from %I on %s; pfx %I %M",
r->src, conf.dev, r->laddr, r->mask);
if(!noconfig && validip(r->gaddr))
deldefroute(r->gaddr, conf.lladdr, r->laddr, r->mask);
if(validip(r->gaddr)){
maskip(r->laddr, r->mask, raddr);
deldefroute(r->gaddr, conf.lladdr, r->laddr, raddr, r->mask);
}
*rr = r->next;
free(r);
continue;
@ -680,6 +706,10 @@ recvrahost(uchar buf[], int pktlen)
/* remove expired prefixes */
issuedel6(&conf);
/* managed netork: prefixes are acquired with dhcpv6 */
if(dodhcp && conf.mflag)
return 0;
/* process new prefixes */
m = sizeof *ra;
while(pktlen - m >= 8) {
@ -704,18 +734,15 @@ recvrahost(uchar buf[], int pktlen)
|| conf.prefixlen > 64)
continue;
genipmkask(conf.mask, conf.prefixlen);
maskip(prfo->pref, conf.mask, conf.v6pref);
if(!validip(conf.v6pref)
|| isv4(conf.v6pref)
|| ipcmp(conf.v6pref, v6loopback) == 0
|| ISIPV6MCAST(conf.v6pref)
|| ISIPV6LINKLOCAL(conf.v6pref))
genipmask(conf.v6mask, conf.prefixlen);
maskip(prfo->pref, conf.v6mask, conf.v6pref);
if(!validv6prefix(conf.v6pref))
continue;
ipmove(conf.raddr, conf.v6pref);
ipmove(conf.mask, conf.v6mask);
memmove(conf.laddr, conf.v6pref, 8);
memmove(conf.laddr+8, conf.lladdr+8, 8);
ipmove(conf.gaddr, (prfo->lar & RFMASK) != 0? prfo->pref: ra->src);
ipmove(conf.gaddr, (prfo->lar & RFMASK) != 0? prfo->pref: conf.v6router);
conf.onlink = (prfo->lar & OLMASK) != 0;
conf.autoflag = (prfo->lar & AFMASK) != 0;
conf.validlt = nhgetl(prfo->validlt);
@ -723,8 +750,7 @@ recvrahost(uchar buf[], int pktlen)
if(conf.preflt > conf.validlt)
continue;
if(conf.routerlt == 0
|| conf.preflt == 0
if(conf.preflt == 0
|| isula(conf.laddr)
|| ipcmp(conf.gaddr, conf.laddr) == 0
|| ipcmp(conf.gaddr, conf.lladdr) == 0)
@ -754,8 +780,8 @@ recvrahost(uchar buf[], int pktlen)
seen = memcmp(r->hash, hash, SHA1dlen) == 0;
if(!seen && validip(r->gaddr) && ipcmp(r->gaddr, conf.gaddr) != 0){
DEBUG("changing router %I->%I", r->gaddr, conf.gaddr);
if(!noconfig)
deldefroute(r->gaddr, conf.lladdr, r->laddr, r->mask);
maskip(r->laddr, r->mask, raddr);
deldefroute(r->gaddr, conf.lladdr, r->laddr, raddr, r->mask);
}
} else {
seen = 0;
@ -790,19 +816,13 @@ recvrahost(uchar buf[], int pktlen)
DEBUG("got prefix %I %M via %I on %s",
conf.v6pref, conf.mask, conf.gaddr, conf.dev);
if(noconfig)
continue;
if(validip(conf.gaddr))
adddefroute(conf.gaddr, conf.lladdr, conf.laddr, conf.mask);
adddefroute(conf.gaddr, conf.lladdr, conf.laddr, conf.raddr, conf.mask);
putndb(1);
refresh();
}
/* pass gateway to dhcpv6 if it is managed network */
ipmove(conf.gaddr, conf.mflag? ra->src: IPnoaddr);
return 0;
}
@ -814,6 +834,7 @@ recvra6(void)
{
int fd, n, sendrscnt, gotra, pktcnt, sleepfor;
uchar buf[4096];
ulong now;
fd = dialicmpv6(v6allnodesL, ICMP6_RA);
if(fd < 0)
@ -834,15 +855,16 @@ recvra6(void)
procsetname("recvra6 on %s %I", conf.dev, conf.lladdr);
notify(catch);
gotra = 0;
restart:
sendrscnt = 0;
if(recvra6on(myifc) == IsHostRecv){
sendrs(fd, v6allroutersL);
sendrscnt = Maxv6rss;
}
gotra = 0;
pktcnt = 0;
sleepfor = Minv6interradelay;
conf.state = 0; /* dhcpv6 off */
for (;;) {
alarm(sleepfor);
@ -858,6 +880,7 @@ recvra6(void)
pktcnt = 1;
}
sleepfor = Maxv6radelay;
now = time(nil);
myifc = readipifc(conf.mpoint, myifc, myifc->index);
if(myifc == nil) {
@ -884,34 +907,104 @@ recvra6(void)
sendrs(fd, v6allroutersL);
sleepfor = V6rsintvl + nrand(100);
} else if(!gotra) {
gotra = 1;
warning("recvra6: no router advs after %d sols on %s",
Maxv6rss, conf.dev);
rendezvous(recvra6, (void*)0);
}
} else if(dodhcp && conf.state)
goto renewdhcp;
continue;
}
if(recvrahost(buf, n) < 0)
if(recvrahost(buf, n, now) < 0)
continue;
/* got at least initial ra; no whining */
if(!gotra){
gotra = 1;
if(dodhcp && conf.mflag){
dhcpv6query();
if(noconfig || !validip(conf.laddr))
if(dodhcp && conf.mflag){
if(conf.state){
if(validip(conf.gaddr) && ipcmp(conf.gaddr, conf.v6router) != 0){
warning("dhcpv6: default router changed %I -> %I on %s",
conf.gaddr, conf.v6router, conf.dev);
} else {
renewdhcp:
/* when renewing, wait for lease to time-out */
if(now < conf.timeout)
continue;
}
}
if(dhcpv6query(conf.state) < 0){
if(conf.state == 0)
continue;
DEBUG("dhcpv6 failed renew for %I with prefix %I %M via %I on %s",
conf.laddr, conf.v6pref, conf.v6mask, conf.gaddr, conf.dev);
if(!noconfig){
fprint(conf.rfd, "tag dhcp");
if(validip(conf.gaddr))
if(conf.preflt && validv6prefix(conf.v6pref) && !isula(conf.v6pref))
deldefroute(conf.gaddr, conf.lladdr, IPnoaddr, conf.v6pref, conf.v6mask);
ipunconfig();
fprint(conf.rfd, "tag ra6");
}
/* restart sending router solitications */
conf.state = 0;
goto restart;
}
now = time(nil);
conf.state = 1; /* dhcpv6 active */
sendrscnt = 0; /* stop sending router solicitations */
DEBUG("dhcpv6 got %I with prefix %I %M via %I on %s for lease %lud",
conf.laddr, conf.v6pref, conf.v6mask, conf.v6router, conf.dev, conf.lease);
if(!noconfig){
fprint(conf.rfd, "tag dhcp");
if(validip(conf.gaddr) && ipcmp(conf.gaddr, conf.v6router) != 0){
deldefroute(conf.gaddr, conf.lladdr, conf.laddr, conf.raddr, conf.mask);
if(conf.preflt && validv6prefix(conf.v6pref) && !isula(conf.v6pref))
deldefroute(conf.gaddr, conf.lladdr, IPnoaddr, conf.v6pref, conf.v6mask);
}
ipmove(conf.gaddr, conf.v6router);
if(ip6cfg() < 0){
fprint(conf.rfd, "tag ra6");
continue;
}
putndb(1);
if(conf.preflt && validv6prefix(conf.v6pref)){
uchar save[IPaddrlen];
/* if we got a delegated prefix, add the default route */
if(validip(conf.gaddr) && !isula(conf.v6pref))
adddefroute(conf.gaddr, conf.lladdr, IPnoaddr, conf.v6pref, conf.v6mask);
/* store prefix info in /net/ndb */
ipmove(save, conf.laddr);
ipmove(conf.laddr, IPnoaddr); /* we don't have an address there */
ipmove(conf.raddr, conf.v6pref);
ipmove(conf.mask, conf.v6mask);
putndb(1);
ipmove(conf.laddr, save);
ipmove(conf.raddr, save); /* was same as laddr as /128 */
memset(conf.mask, 0xFF, IPaddrlen);
}
refresh();
rendezvous(recvra6, (void*)1);
fprint(conf.rfd, "tag ra6");
}
/* infinity */
if(conf.lease == ~0UL){
if(!gotra)
rendezvous(recvra6, (void*)1);
exits(nil);
}
if(conf.lease < 60)
conf.lease = 60;
conf.timeout = now + conf.lease;
}
/* got at least initial ra; no whining */
if(!gotra){
gotra = 1;
rendezvous(recvra6, (void*)1);
}
}
@ -1004,11 +1097,7 @@ sendra(int fd, uchar *dst, int rlt, Ipifc *ifc, Ndb *db)
if(pktlen > sizeof buf - 4*8)
break;
if(!validip(lifc->ip)
|| isv4(lifc->ip)
|| ipcmp(lifc->ip, v6loopback) == 0
|| ISIPV6MCAST(lifc->ip)
|| ISIPV6LINKLOCAL(lifc->ip))
if(!validv6prefix(lifc->ip))
continue;
prfo = (Prefixopt*)&buf[pktlen];

View file

@ -30,6 +30,7 @@ int dodhcp;
int nodhcpwatch;
int sendhostname;
char *ndboptions;
char *ipnet;
int ipv6auto;
int dupl_disc = 1; /* flag: V6 duplicate neighbor discovery */
@ -58,7 +59,7 @@ void
usage(void)
{
fprint(2, "usage: %s [-6dDGnNOpPrtuXy][-b baud][-c ctl]* [-U duid] [-g gw]"
"[-h host][-m mtu][-s dns]...\n"
"[-h host][-i ipnet][-m mtu][-s dns]...\n"
"\t[-f dbfile][-x mtpt][-o dhcpopt] type dev [verb] [laddr [mask "
"[raddr [fs [auth]]]]]\n", argv0);
exits("usage");
@ -400,6 +401,9 @@ main(int argc, char **argv)
sysfatal("bad hostname");
sendhostname = 1;
break;
case 'i':
ipnet = EARGF(usage());
break;
case 'm':
conf.mtu = atoi(EARGF(usage()));
break;
@ -569,7 +573,7 @@ dodel(void)
return;
if(validip(conf.gaddr))
deldefroute(conf.gaddr, conf.laddr, conf.laddr, conf.mask);
deldefroute(conf.gaddr, conf.laddr, conf.laddr, conf.raddr, conf.mask);
/* use "remove" verb instead of "del" for older kernels */
if(conf.cfd >= 0 && fprint(conf.cfd, "remove %I %M", conf.laddr, conf.mask) < 0)
@ -595,6 +599,8 @@ openiproute(void)
{
char buf[127];
if(noconfig)
return;
snprint(buf, sizeof buf, "%s/iproute", conf.mpoint);
conf.rfd = open(buf, OWRITE);
}
@ -684,7 +690,7 @@ ip4cfg(void)
if(validip(conf.gaddr) && isv4(conf.gaddr)
&& ipcmp(conf.gaddr, conf.laddr) != 0)
adddefroute(conf.gaddr, conf.laddr, conf.laddr, conf.mask);
adddefroute(conf.gaddr, conf.laddr, conf.laddr, conf.laddr, conf.mask);
if(tflag)
fprint(conf.cfd, "iprouting 1");
@ -754,37 +760,43 @@ putndb(int doadd)
Ndb *db;
int fd;
if(beprimary == 0)
if(beprimary == 0 || noconfig)
return;
p = buf;
e = buf + sizeof buf;
if(doadd){
p = seprint(p, e, "ip=%I ipmask=%M ipgw=%I\n",
conf.laddr, conf.mask, conf.gaddr);
if(np = strchr(conf.hostname, '.')){
if(*conf.domainname == 0)
strcpy(conf.domainname, np+1);
*np = 0;
if(ipnet != nil && validip(conf.raddr)){
p = seprint(p, e, "ipnet=%s ip=%I ipmask=%M ipgw=%I\n",
ipnet, conf.raddr, conf.mask, conf.gaddr);
}
if(validip(conf.laddr)){
p = seprint(p, e, "ip=%I ipmask=%M ipgw=%I\n",
conf.laddr, conf.mask, conf.gaddr);
if(np = strchr(conf.hostname, '.')){
if(*conf.domainname == 0)
strcpy(conf.domainname, np+1);
*np = 0;
}
if(*conf.hostname)
p = seprint(p, e, "\tsys=%U\n", conf.hostname);
if(*conf.domainname)
p = seprint(p, e, "\tdom=%U.%U\n",
conf.hostname, conf.domainname);
if(*conf.dnsdomain)
p = putnames(p, e, "\tdnsdomain", conf.dnsdomain);
if(validip(conf.dns))
p = putaddrs(p, e, "\tdns", conf.dns, sizeof conf.dns);
if(validip(conf.fs))
p = putaddrs(p, e, "\tfs", conf.fs, sizeof conf.fs);
if(validip(conf.auth))
p = putaddrs(p, e, "\tauth", conf.auth, sizeof conf.auth);
if(validip(conf.ntp))
p = putaddrs(p, e, "\tntp", conf.ntp, sizeof conf.ntp);
if(ndboptions)
p = seprint(p, e, "%s\n", ndboptions);
}
if(*conf.hostname)
p = seprint(p, e, "\tsys=%U\n", conf.hostname);
if(*conf.domainname)
p = seprint(p, e, "\tdom=%U.%U\n",
conf.hostname, conf.domainname);
if(*conf.dnsdomain)
p = putnames(p, e, "\tdnsdomain", conf.dnsdomain);
if(validip(conf.dns))
p = putaddrs(p, e, "\tdns", conf.dns, sizeof conf.dns);
if(validip(conf.fs))
p = putaddrs(p, e, "\tfs", conf.fs, sizeof conf.fs);
if(validip(conf.auth))
p = putaddrs(p, e, "\tauth", conf.auth, sizeof conf.auth);
if(validip(conf.ntp))
p = putaddrs(p, e, "\tntp", conf.ntp, sizeof conf.ntp);
if(ndboptions)
p = seprint(p, e, "%s\n", ndboptions);
}
/* for myip() */
@ -797,10 +809,11 @@ putndb(int doadd)
while((t = ndbparse(db)) != nil){
uchar ip[IPaddrlen];
if(ndbfindattr(t, t, "ipnet") != nil
|| (nt = ndbfindattr(t, t, "ip")) == nil
|| parseip(ip, nt->val) == -1
|| ipcmp(ip, conf.laddr) != 0 && myip(allifcs, ip)){
nt = ndbfindattr(t, t, "ipnet");
if(nt != nil && (ipnet == nil || strcmp(nt->val, ipnet) != 0)
|| nt == nil && ((nt = ndbfindattr(t, t, "ip")) == nil
|| parseip(ip, nt->val) == -1
|| ipcmp(ip, conf.laddr) != 0 && myip(allifcs, ip))){
if(p > buf)
p = seprint(p, e, "\n");
for(nt = t; nt != nil; nt = nt->entry)
@ -843,7 +856,7 @@ routectl(char *cmd, uchar *dst, uchar *mask, uchar *gate, char *flags, uchar *ia
}
static void
defroutectl(char *cmd, uchar *gaddr, uchar *ia, uchar *src, uchar *smask)
defroutectl(char *cmd, uchar *gaddr, uchar *ia, uchar *laddr, uchar *src, uchar *smask)
{
uchar dst[IPaddrlen], mask[IPaddrlen];
@ -870,21 +883,21 @@ defroutectl(char *cmd, uchar *gaddr, uchar *ia, uchar *src, uchar *smask)
}
/* add source specific route for us */
if(validip(src))
routectl(cmd, dst, mask, gaddr, "", ia, src, IPallbits);
if(validip(laddr))
routectl(cmd, dst, mask, gaddr, "", ia, laddr, IPallbits);
}
void
adddefroute(uchar *gaddr, uchar *ia, uchar *src, uchar *smask)
adddefroute(uchar *gaddr, uchar *ia, uchar *laddr, uchar *src, uchar *smask)
{
defroutectl("add", gaddr, ia, src, smask);
defroutectl("add", gaddr, ia, laddr, src, smask);
}
void
deldefroute(uchar *gaddr, uchar *ia, uchar *src, uchar *smask)
deldefroute(uchar *gaddr, uchar *ia, uchar *laddr, uchar *src, uchar *smask)
{
/* use "remove" verb instead of "del" for older kernels */
defroutectl("remove", gaddr, ia, src, smask);
defroutectl("remove", gaddr, ia, laddr, src, smask);
}
void
@ -893,6 +906,9 @@ refresh(void)
char file[64];
int fd;
if(noconfig)
return;
snprint(file, sizeof file, "%s/cs", conf.mpoint);
if((fd = open(file, OWRITE)) >= 0){
write(fd, "refresh", 7);