summaryrefslogtreecommitdiff
path: root/sys/src/cmd/ip/ipconfig/dhcpv6.c
diff options
context:
space:
mode:
authorcinap_lenrek <cinap_lenrek@felloff.net>2023-05-21 17:48:42 +0000
committercinap_lenrek <cinap_lenrek@felloff.net>2023-05-21 17:48:42 +0000
commitcbf5d3dc340da5b96cd1282e622a3f862c5b103e (patch)
tree9f8fbafdf6de49df2b503c335a2ce1e264bfbde7 /sys/src/cmd/ip/ipconfig/dhcpv6.c
parent169aa63ec5a670d0c72b7e9fb747e16d76b718c3 (diff)
ip/ipconfig: initial dhcpv6 support, clean default-routes and /net/ndb on remove
This adds a very basic (probably wrong) DHCPv6 client, to handle the "managed"-flag in IPv6 router solicitations. We add -U option to pass the DHCPv6 client id as well as an -s flag to manually add a dns server (because ppp is going to call ipconfig to handle all the configuration and write-back to /net/ndb in the future). Have the remove command also remove default routes and /net/ndb entries. (needed by ppp).
Diffstat (limited to 'sys/src/cmd/ip/ipconfig/dhcpv6.c')
-rw-r--r--sys/src/cmd/ip/ipconfig/dhcpv6.c233
1 files changed, 233 insertions, 0 deletions
diff --git a/sys/src/cmd/ip/ipconfig/dhcpv6.c b/sys/src/cmd/ip/ipconfig/dhcpv6.c
new file mode 100644
index 000000000..ba31fb1e3
--- /dev/null
+++ b/sys/src/cmd/ip/ipconfig/dhcpv6.c
@@ -0,0 +1,233 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ip.h>
+#include <ndb.h>
+#include "ipconfig.h"
+
+enum {
+ SOLICIT = 1,
+ ADVERTISE,
+ REQUEST,
+ CONFIRM,
+ RENEW,
+ REBIND,
+ REPLY,
+ RELEASE,
+ DECLINE,
+ RECONFIGURE,
+ INFOREQ,
+ RELAYFORW,
+ RELAYREPL,
+};
+
+static uchar v6dhcpservers[IPaddrlen] = {
+ 0xff, 0x02, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 1, 0, 2,
+};
+
+static uchar sid[256];
+static int sidlen;
+
+static int
+openlisten(void)
+{
+ int n, fd, cfd;
+ char data[128], devdir[40];
+
+ sprint(data, "%s/udp!%I!546", conf.mpoint, conf.lladdr);
+ for (n = 0; (cfd = announce(data, devdir)) < 0; n++) {
+ if(!noconfig)
+ sysfatal("can't announce for dhcp: %r");
+
+ /* might be another client - wait and try again */
+ warning("can't announce %s: %r", data);
+ sleep(jitter());
+ if(n > 10)
+ return -1;
+ }
+
+ if(fprint(cfd, "headers") < 0)
+ sysfatal("can't set header mode: %r");
+
+ fprint(cfd, "ignoreadvice");
+
+ sprint(data, "%s/data", devdir);
+ fd = open(data, ORDWR);
+ if(fd < 0)
+ sysfatal("open %s: %r", data);
+ close(cfd);
+ return fd;
+}
+
+static int
+transaction(int fd, int type, int timeout)
+{
+ union {
+ Udphdr;
+ uchar buf[4096];
+ } ipkt, opkt;
+
+ uchar *p, *e, *x;
+ int tra, opt, len, sleepfor;
+
+ tra = lrand() & 0xFFFFFF;
+
+ ipmove(opkt.laddr, conf.lladdr);
+ ipmove(opkt.raddr, v6dhcpservers);
+ ipmove(opkt.ifcaddr, conf.lladdr);
+ hnputs(opkt.lport, 546);
+ hnputs(opkt.rport, 547);
+
+ p = opkt.buf + Udphdrsize;
+
+ *p++ = type;
+ *p++ = tra >> 16;
+ *p++ = tra >> 8;
+ *p++ = tra >> 0;
+
+ /* client identifier */
+ *p++ = 0x00; *p++ = 0x01;
+ /* len */
+ *p++ = conf.duidlen >> 8;
+ *p++ = conf.duidlen;
+ memmove(p, conf.duid, conf.duidlen);
+ p += conf.duidlen;
+
+ /* IA for non-temporary address */
+ len = 12;
+ if(validip(conf.laddr))
+ len += 4 + IPaddrlen + 2*4;
+ *p++ = 0x00; *p++ = 0x03;
+ *p++ = len >> 8;
+ *p++ = len;
+ /* IAID */
+ *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x01;
+ /* T1, T2 */
+ *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;
+ *p++ = 0x00; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;
+ if(len > 12){
+ *p++ = 0x00; *p++ = 0x05;
+ *p++ = 0x00; *p++ = IPaddrlen + 2*4;
+ memmove(p, conf.laddr, IPaddrlen);
+ p += IPaddrlen;
+ memset(p, 0xFF, 2*4);
+ p += 2*4;
+ }
+
+ /* Option Request */
+ *p++ = 0x00; *p++ = 0x06;
+ *p++ = 0x00; *p++ = 0x02;
+ *p++ = 0x00; *p++ = 0x17; /* DNS servers */
+
+ if(sidlen > 0){
+ *p++ = 0x00; *p++ = 0x02;
+ /* len */
+ *p++ = sidlen >> 8;
+ *p++ = sidlen;;
+ memmove(p, sid, sidlen);
+ p += sidlen;
+ }
+
+ len = -1;
+ for(sleepfor = 500; timeout > 0; sleepfor <<= 1){
+ DEBUG("sending dhcpv6 request %x", opkt.buf[Udphdrsize]);
+
+ alarm(sleepfor);
+ if(len < 0)
+ write(fd, opkt.buf, p - opkt.buf);
+
+ len = read(fd, ipkt.buf, sizeof(ipkt.buf));
+ timeout += alarm(0);
+ timeout -= sleepfor;
+ if(len == 0)
+ break;
+
+ 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))
+ continue;
+
+ DEBUG("got dhcpv6 reply %x from %I on %I", ipkt.buf[Udphdrsize+0], ipkt.raddr, ipkt.ifcaddr);
+
+ type |= (int)ipkt.buf[Udphdrsize+0]<<8;
+ switch(type){
+ case ADVERTISE << 8 | SOLICIT:
+ case REPLY << 8 | REQUEST:
+ goto Response;
+ default:
+ return -1;
+ }
+ }
+ return -1;
+
+Response:
+ for(p = ipkt.buf + Udphdrsize + 4, e = ipkt.buf + len; p < e; p = x) {
+ if (p+4 > e)
+ return -1;
+
+ opt = (int)p[0] << 8 | p[1];
+ len = (int)p[2] << 8 | p[3];
+ p += 4;
+ x = p+len;
+ if (x > e)
+ return -1;
+
+ DEBUG("got dhcpv6 option %x: [%d] %.*H", opt, len, len, p);
+
+ switch(opt){
+ case 0x01: /* client identifier */
+ continue;
+ case 0x02: /* server identifier */
+ if(len < 1 || len > sizeof(sid))
+ break;
+ sidlen = len;
+ memmove(sid, p, sidlen);
+ continue;
+ case 0x03: /* IA for non-temporary address */
+ if(p+12+4+IPaddrlen+2*4 > x)
+ break;
+ /* skip IAID, T1, T2 */
+ p += 12;
+ /* IA Addresss */
+ if(p[0] != 0x00 || p[1] != 0x05
+ || p[2] != 0x00 || p[3] != IPaddrlen+2*4)
+ break;
+ p += 4;
+ memset(conf.mask, 0xFF, IPaddrlen);
+ memmove(conf.laddr, p, IPaddrlen);
+ continue;
+ case 0x17: /* dns servers */
+ if(len % IPaddrlen)
+ break;
+ addaddrs(conf.dns, sizeof(conf.dns), p, len);
+ continue;
+ default:
+ DEBUG("unknown dhcpv6 option %x", opt);
+ continue;
+ }
+ warning("dhcpv6: malformed option %x: [%d] %.*H", opt, len, len, x-len);
+ }
+
+ return 0;
+}
+
+void
+dhcpv6query(void)
+{
+ int fd;
+
+ fd = openlisten();
+ if(transaction(fd, SOLICIT, 5000) < 0)
+ goto out;
+ if(!validip(conf.laddr))
+ goto out;
+ if(transaction(fd, REQUEST, 10000) < 0)
+ goto out;
+out:
+ close(fd);
+}