ref: c12f73cb4c31d9f6fc3f6ab6bb2ddd86531d24a8
parent: 733db65e8deda366890074444119919ac75c8ca6
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Mon Oct 28 15:06:47 EDT 2024
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...
--- a/sys/man/8/ipconfig
+++ b/sys/man/8/ipconfig
@@ -16,6 +16,8 @@
.IR gateway ]
.RB [ -h
.IR host ]
+.BR [ -i
+.IR ipnet ]
.RB [ -m
.IR mtu ]
.RB [ -o
@@ -191,6 +193,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.
--- a/sys/src/cmd/ip/ipconfig/dhcpv6.c
+++ b/sys/src/cmd/ip/ipconfig/dhcpv6.c
@@ -63,16 +63,60 @@
}
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;
ipmove(opkt.laddr, conf.lladdr);
@@ -98,8 +142,8 @@
/* 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,7 +154,7 @@
*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);
@@ -117,12 +161,46 @@
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 @@
}
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 @@
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 @@
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 @@
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;
+
}
--- a/sys/src/cmd/ip/ipconfig/ipconfig.h
+++ b/sys/src/cmd/ip/ipconfig/ipconfig.h
@@ -45,6 +45,7 @@
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 @@
/*
* IPv6
*/
+ uchar v6router[IPaddrlen];
/* router-advertisement related */
uchar sendra;
@@ -86,6 +88,7 @@
/* 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 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 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);
--- a/sys/src/cmd/ip/ipconfig/ipv6.c
+++ b/sys/src/cmd/ip/ipconfig/ipv6.c
@@ -198,6 +198,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
issuebasera6(&conf);
- now = time(nil);
-
/* remove expired default routes */
m = 0;
for(rr = &routelist; (r = *rr) != nil;){
@@ -667,8 +691,10 @@
|| 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 @@
/* 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 @@
|| 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 @@
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 @@
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 @@
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 @@
{
int fd, n, sendrscnt, gotra, pktcnt, sleepfor;
uchar buf[4096];
+ ulong now;
fd = dialicmpv6(v6allnodesL, ICMP6_RA);
if(fd < 0)
@@ -834,15 +855,16 @@
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 @@
pktcnt = 1;
}
sleepfor = Maxv6radelay;
+ now = time(nil);
myifc = readipifc(conf.mpoint, myifc, myifc->index);
if(myifc == nil) {
@@ -884,34 +907,104 @@
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 @@
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];
--- a/sys/src/cmd/ip/ipconfig/main.c
+++ b/sys/src/cmd/ip/ipconfig/main.c
@@ -30,6 +30,7 @@
int nodhcpwatch;
int sendhostname;
char *ndboptions;
+char *ipnet;
int ipv6auto;
int dupl_disc = 1; /* flag: V6 duplicate neighbor discovery */
@@ -58,7 +59,7 @@
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 @@
sysfatal("bad hostname");
sendhostname = 1;
break;
+ case 'i':
+ ipnet = EARGF(usage());
+ break;
case 'm':
conf.mtu = atoi(EARGF(usage()));
break;
@@ -569,7 +573,7 @@
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 @@
{
char buf[127];
+ if(noconfig)
+ return;
snprint(buf, sizeof buf, "%s/iproute", conf.mpoint);
conf.rfd = open(buf, OWRITE);
}
@@ -684,7 +690,7 @@
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,7 +760,7 @@
Ndb *db;
int fd;
- if(beprimary == 0)
+ if(beprimary == 0 || noconfig)
return;
p = buf;
@@ -761,30 +767,36 @@
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(*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(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);
+ }
}
/* for myip() */
@@ -797,10 +809,11 @@
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 @@
}
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 @@
}
/* 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
@@ -892,6 +905,9 @@
{
char file[64];
int fd;
+
+ if(noconfig)
+ return;
snprint(file, sizeof file, "%s/cs", conf.mpoint);
if((fd = open(file, OWRITE)) >= 0){
--
⑨