diff options
author | cinap_lenrek <cinap_lenrek@felloff.net> | 2023-05-21 17:48:42 +0000 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@felloff.net> | 2023-05-21 17:48:42 +0000 |
commit | cbf5d3dc340da5b96cd1282e622a3f862c5b103e (patch) | |
tree | 9f8fbafdf6de49df2b503c335a2ce1e264bfbde7 /sys/src/cmd/ip/ipconfig/dhcpv6.c | |
parent | 169aa63ec5a670d0c72b7e9fb747e16d76b718c3 (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.c | 233 |
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); +} |