Merged to master at d04efcdab130..ed1e23e5b5f9 (from, to]

You can see the entire diff with 'git diff' or at
https://github.com/brho/akaros/compare/d04efcdab130...ed1e23e5b5f9



On 2016-05-23 at 14:09 Dan Cross <[email protected]> wrote:
> ipconfig is an IP stack configuration tool.  It acts
> as both a frontend to the filesystem-based configuration
> dance one does when configuring a Plan 9-derived IP
> stack, as well as a DHCP client and IPv6 autoconfiguration
> agent.
> 
> This change is a port of `ipconfig` to Akaros.  Changes
> include reformatting the code to match Akaros kernel style,
> replacing `alarm` with Akaros system calls, removing
> Plan 9- and GNU-specific code and replacing with more
> portable idioms, and generally cleaning up.  This has been
> tested on hardware for static and DHCP configuration of IPv4
> and static configuration of IPv6.  It will be called by
> `/ifconfig` if present in the KFS.  Fixes #4.
> 
> Change-Id: I233969a8d8efca429a773c7a8667f0bb7e88e552
> Signed-off-by: Dan Cross <[email protected]>
> Issue: 4
> Bug: 28668810
> ---
>  Makefile                       |    2 +
>  tools/apps/ipconfig/Makefile   |   34 +
>  tools/apps/ipconfig/README.md  |   17 +
>  tools/apps/ipconfig/dhcp.h     |  172 ++++
>  tools/apps/ipconfig/icmp.h     |   50 ++
>  tools/apps/ipconfig/ipconfig.h |  256 ++++++
>  tools/apps/ipconfig/ipv6.c     |  954 ++++++++++++++++++++
>  tools/apps/ipconfig/main.c     | 1929 
> ++++++++++++++++++++++++++++++++++++++++
>  8 files changed, 3414 insertions(+)
>  create mode 100644 tools/apps/ipconfig/Makefile
>  create mode 100644 tools/apps/ipconfig/README.md
>  create mode 100644 tools/apps/ipconfig/dhcp.h
>  create mode 100644 tools/apps/ipconfig/icmp.h
>  create mode 100644 tools/apps/ipconfig/ipconfig.h
>  create mode 100644 tools/apps/ipconfig/ipv6.c
>  create mode 100644 tools/apps/ipconfig/main.c
> 
> diff --git a/Makefile b/Makefile
> index f47ac4a..3e00fd1 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -686,11 +686,13 @@ $(app-dirs-clean):
>  PHONY +=  apps-install
>  apps-install: $(app-dirs-install)
>       @$(call make_as_parent, -C tools/apps/busybox)
> +     @$(call make_as_parent, -C tools/apps/ipconfig install)
>       @$(call make_as_parent, -C tools/profile/perf install)
>  
>  PHONY += apps-clean
>  apps-clean: $(app-dirs-clean)
>       @$(call make_as_parent, -C tools/apps/busybox clean)
> +     @$(call make_as_parent, -C tools/apps/ipconfig clean)
>       @$(call make_as_parent, -C tools/profile/perf clean)
>  
>  # Cross Compiler
> diff --git a/tools/apps/ipconfig/Makefile b/tools/apps/ipconfig/Makefile
> new file mode 100644
> index 0000000..65f3c82
> --- /dev/null
> +++ b/tools/apps/ipconfig/Makefile
> @@ -0,0 +1,34 @@
> +include              ../../Makefrag
> +
> +SOURCES:=    main.c ipv6.c
> +
> +XCC:=                $(CROSS_COMPILE)gcc
> +
> +LIBS:=               -lndblib -liplib -lbenchutil
> +
> +PHONY:=              all
> +all:         ipconfig
> +
> +
> +PHONY+=              ipconfig
> +ipconfig:    $(SOURCES)
> +             @echo "  CC      ipconfig"
> +             $(Q)$(XCC) -O2 -std=gnu99 -o ipconfig $(SOURCES) $(LIBS)
> +
> +
> +PHONY+=              install
> +install:     ipconfig
> +             @echo "  IN      ipconfig"
> +             $(Q)cp ipconfig $(KFS_ROOT)/bin/ipconfig
> +
> +
> +PHONY+=              clean
> +clean:
> +             @echo "  RM      ipconfig"
> +             $(Q)rm -f ipconfig
> +
> +
> +PHONY+=              mrproper
> +mrproper:    clean
> +
> +.PHONY:              $(PHONY)
> diff --git a/tools/apps/ipconfig/README.md b/tools/apps/ipconfig/README.md
> new file mode 100644
> index 0000000..43e3ffc
> --- /dev/null
> +++ b/tools/apps/ipconfig/README.md
> @@ -0,0 +1,17 @@
> +# ipconfig for Akaros
> +
> +This is Plan 9 ipconfig(8), ported to Akaros.
> +
> +ipconfig is an IP stack configuration tool.  It acts as
> +both a frontend to the filesystem-based configuration dance
> +one does when configuring a Plan 9-derived IP stack, as well
> +as a implementing a DHCP client and IPv6 autoconfiguration
> +agent.  It will be called from `/ifconfig` if present in the
> +KFS.
> +
> +Changes to Plan 9 `ipconfig` include reformatting the code
> +to match Akaros kernel style, replacing `alarm` with Akaros
> +system calls, removing Plan 9- and GNU-specific code and
> +replacing with more portable idioms and general cleaning up.
> +
> +Man page: http://plan9.bell-labs.com/magic/man2html/8/ipconfig
> diff --git a/tools/apps/ipconfig/dhcp.h b/tools/apps/ipconfig/dhcp.h
> new file mode 100644
> index 0000000..3dfa5be
> --- /dev/null
> +++ b/tools/apps/ipconfig/dhcp.h
> @@ -0,0 +1,172 @@
> +/*
> + * This file is part of the UCB release of Plan 9. It is subject to the 
> license
> + * terms in the LICENSE file found in the top-level directory of this
> + * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
> + * part of the UCB release of Plan 9, including this file, may be copied,
> + * modified, propagated, or distributed except according to the terms 
> contained
> + * in the LICENSE file.
> + */
> +
> +// Dynamic Host Configuration Protocol / BOOTP
> +enum {
> +     OfferTimeout = 60,              // when an offer times out
> +     MaxLease = 60 * 60,             // longest lease for dynamic binding
> +     MinLease = 15 * 60,             // shortest lease for dynamic binding
> +     StaticLease = 30 * 60,  // lease for static binding
> +
> +     IPUDPHDRSIZE = 28,              // size of an IP plus UDP header
> +     MINSUPPORTED = 576,             // biggest IP message the client must 
> support
> +
> +     // lengths of some bootp fields
> +     Maxhwlen = 16,
> +     Maxfilelen = 128,
> +     Maxoptlen = 312 - 4,
> +
> +     // bootp types
> +     Bootrequest = 1,
> +     Bootreply = 2,
> +
> +     // bootp flags
> +     Fbroadcast = 1 << 15,
> +
> +     // dhcp v4 types
> +     Discover = 1,
> +     Offer = 2,
> +     Request = 3,
> +     Decline = 4,
> +     Ack = 5,
> +     Nak = 6,
> +     Release = 7,
> +     Inform = 8,
> +
> +     // bootp option types
> +     OBend = 255,
> +     OBpad = 0,
> +     OBmask = 1,
> +     OBtimeoff = 2,
> +     OBrouter = 3,
> +     OBtimeserver = 4,
> +     OBnameserver = 5,
> +     OBdnserver = 6,
> +     OBlogserver = 7,
> +     OBcookieserver = 8,
> +     OBlprserver = 9,
> +     OBimpressserver = 10,
> +     OBrlserver = 11,
> +     OBhostname = 12,                // 0x0c
> +     OBbflen = 13,
> +     OBdumpfile = 14,
> +     OBdomainname = 15,
> +     OBswapserver = 16,              // 0x10
> +     OBrootpath = 17,
> +     OBextpath = 18,
> +     OBipforward = 19,
> +     OBnonlocal = 20,
> +     OBpolicyfilter = 21,
> +     OBmaxdatagram = 22,
> +     OBttl = 23,
> +     OBpathtimeout = 24,
> +     OBpathplateau = 25,
> +     OBmtu = 26,
> +     OBsubnetslocal = 27,
> +     OBbaddr = 28,
> +     OBdiscovermask = 29,
> +     OBsupplymask = 30,
> +     OBdiscoverrouter = 31,
> +     OBrsserver = 32,                // 0x20
> +     OBstaticroutes = 33,
> +     OBtrailerencap = 34,
> +     OBarptimeout = 35,
> +     OBetherencap = 36,
> +     OBtcpttl = 37,
> +     OBtcpka = 38,
> +     OBtcpkag = 39,
> +     OBnisdomain = 40,
> +     OBniserver = 41,
> +     OBntpserver = 42,
> +     OBvendorinfo = 43,              // 0x2b
> +     OBnetbiosns = 44,
> +     OBnetbiosdds = 45,
> +     OBnetbiostype = 46,
> +     OBnetbiosscope = 47,
> +     OBxfontserver = 48,             // 0x30
> +     OBxdispmanager = 49,
> +     OBnisplusdomain = 64,   // 0x40
> +     OBnisplusserver = 65,
> +     OBhomeagent = 68,
> +     OBsmtpserver = 69,
> +     OBpop3server = 70,
> +     OBnntpserver = 71,
> +     OBwwwserver = 72,
> +     OBfingerserver = 73,
> +     OBircserver = 74,
> +     OBstserver = 75,
> +     OBstdaserver = 76,
> +
> +     // dhcp v4 options
> +     ODipaddr = 50,                  // 0x32
> +     ODlease = 51,
> +     ODoverload = 52,
> +     ODtype = 53,                    // 0x35
> +     ODserverid = 54,                // 0x36
> +     ODparams = 55,                  // 0x37
> +     ODmessage = 56,
> +     ODmaxmsg = 57,
> +     ODrenewaltime = 58,
> +     ODrebindingtime = 59,
> +     ODvendorclass = 60,
> +     ODclientid = 61,                // 0x3d
> +     ODtftpserver = 66,
> +     ODbootfile = 67,
> +
> +     ODpxearch = 93,                 // see rfc 4578
> +     ODpxeni = 94,
> +     ODpxeguid = 97,
> +
> +     // plan9 vendor info options, v4 addresses only (deprecated)
> +     OP9fsv4 = 128,                  // plan9 file servers
> +     OP9authv4 = 129,                // plan9 auth servers
> +
> +     // plan9 vendor info options, textual addresses, thus v4 or v6
> +     OP9fs = 130,                    // plan9 file servers
> +     OP9auth = 131,                  // plan9 auth servers
> +     OP9ipaddr = 132,                // client's address
> +     OP9ipmask = 133,                // client's subnet mask
> +     OP9ipgw = 134,                  // client's gateway
> +     // OP9dns = 135,                // dns servers
> +};
> +
> +// a lease that never expires
> +#define Lforever ~0UL
> +
> +// dhcp states
> +enum {
> +     Sinit,
> +     Sselecting,
> +     Srequesting,
> +     Sbound,
> +     Srenewing,
> +     Srebinding,
> +};
> +
> +struct bootp {
> +     // Udphdr (included because of structure alignment on the alpha)
> +     uint8_t udphdr[Udphdrsize];
> +
> +     uint8_t op;                                             // opcode
> +     uint8_t htype;                                  // hardware type
> +     uint8_t hlen;                                   // hardware address len
> +     uint8_t hops;                                   // hops
> +     uint8_t xid[4];                                 // a random number
> +     uint8_t secs[2];                                // elapsed since client 
> started booting
> +     uint8_t flags[2];
> +     uint8_t ciaddr[IPv4addrlen];    // client IP address (client tells 
> server)
> +     uint8_t yiaddr[IPv4addrlen];    // client IP address (server tells 
> client)
> +     uint8_t siaddr[IPv4addrlen];    // server IP address
> +     uint8_t giaddr[IPv4addrlen];    // gateway IP address
> +     uint8_t chaddr[Maxhwlen];               // client hardware address
> +     char sname[64];                                 // server host name 
> (optional)
> +     char file[Maxfilelen];                  // boot file name
> +     uint8_t optmagic[4];
> +     uint8_t optdata[Maxoptlen];
> +};
> diff --git a/tools/apps/ipconfig/icmp.h b/tools/apps/ipconfig/icmp.h
> new file mode 100644
> index 0000000..68f8f2f
> --- /dev/null
> +++ b/tools/apps/ipconfig/icmp.h
> @@ -0,0 +1,50 @@
> +/*
> + * This file is part of the UCB release of Plan 9. It is subject to the 
> license
> + * terms in the LICENSE file found in the top-level directory of this
> + * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
> + * part of the UCB release of Plan 9, including this file, may be copied,
> + * modified, propagated, or distributed except according to the terms 
> contained
> + * in the LICENSE file.
> + */
> +
> +// ICMP for IP v4 and v6
> +enum {
> +     // Packet Types, icmp v4 (rfc 792)
> +     EchoReply = 0,
> +     Unreachable = 3,
> +     SrcQuench = 4,
> +     Redirect = 5,
> +     EchoRequest = 8,
> +     TimeExceed = 11,
> +     InParmProblem = 12,
> +     Timestamp = 13,
> +     TimestampReply = 14,
> +     InfoRequest = 15,
> +     InfoReply = 16,
> +     AddrMaskRequest = 17,
> +     AddrMaskReply = 18,
> +     Traceroute = 30,
> +     IPv6WhereAreYou = 33,
> +     IPv6IAmHere = 34,
> +
> +     // packet types, icmp v6 (rfc 2463)
> +
> +     // error messages
> +     UnreachableV6 = 1,
> +     PacketTooBigV6 = 2,
> +     TimeExceedV6 = 3,
> +     ParamProblemV6 = 4,
> +
> +     // informational messages (rfc 2461 also)
> +     EchoRequestV6 = 128,
> +     EchoReplyV6 = 129,
> +     RouterSolicit = 133,
> +     RouterAdvert = 134,
> +     NbrSolicit = 135,
> +     NbrAdvert = 136,
> +     RedirectV6 = 137,
> +
> +     Maxtype6 = 137,
> +
> +     ICMP_HDRSIZE = 8,
> +};
> diff --git a/tools/apps/ipconfig/ipconfig.h b/tools/apps/ipconfig/ipconfig.h
> new file mode 100644
> index 0000000..12c3a75
> --- /dev/null
> +++ b/tools/apps/ipconfig/ipconfig.h
> @@ -0,0 +1,256 @@
> +/*
> + * This file is part of the UCB release of Plan 9. It is subject to the 
> license
> + * terms in the LICENSE file found in the top-level directory of this
> + * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
> + * part of the UCB release of Plan 9, including this file, may be copied,
> + * modified, propagated, or distributed except according to the terms 
> contained
> + * in the LICENSE file.
> + */
> +
> +struct conf {
> +     // locally generated
> +     char *type;
> +     char *dev;
> +     char mpoint[32];
> +     int cfd;                                        // ifc control channel
> +     int dfd;                                        // ifc data channel 
> (for ppp)
> +     char *cputype;
> +     uint8_t hwa[32];                        // hardware address
> +     int hwatype;
> +     int hwalen;
> +     uint8_t cid[32];
> +     int cidlen;
> +     char *baud;
> +
> +     // learned info
> +     uint8_t gaddr[IPaddrlen];
> +     uint8_t laddr[IPaddrlen];
> +     uint8_t mask[IPaddrlen];
> +     uint8_t raddr[IPaddrlen];
> +     uint8_t dns[2 * IPaddrlen];
> +     uint8_t fs[2 * IPaddrlen];
> +     uint8_t auth[2 * IPaddrlen];
> +     uint8_t ntp[IPaddrlen];
> +     int mtu;
> +
> +     // dhcp specific
> +     int state;
> +     int fd;
> +     uint32_t xid;
> +     uint32_t starttime;
> +     char sname[64];
> +     char hostname[32];
> +     char domainname[64];
> +     uint8_t server[IPaddrlen];      // server IP address
> +     uint32_t offered;                       // offered lease time
> +     uint32_t lease;                         // lease time
> +     uint32_t resend;                        // # of resends for current 
> state
> +     uint32_t timeout;                       // time to timeout - seconds
> +
> +     //
> +     // IPv6
> +     //
> +
> +     // solicitation specific - XXX add support for IPv6 leases
> +     // ulong        solicit_retries;
> +
> +     // router-advertisement related
> +     uint8_t sendra;
> +     uint8_t recvra;
> +     uint8_t mflag;
> +     uint8_t oflag;
> +     int maxraint;                           // rfc2461, p.39:
> +                                                             // 4sec ≤ 
> maxraint ≤ 1800sec, def 600
> +     int minraint;                           // 3sec ≤ minraint ≤ 
> 0.75*maxraint
> +     int linkmtu;
> +     int reachtime;                          // 3,600,000 msec, default 0
> +     int rxmitra;                            // default 0
> +     int ttl;                                        // default 0 
> (unspecified)
> +
> +     // default gateway params
> +     uint8_t v6gaddr[IPaddrlen];
> +     int routerlt;                           // router life time
> +
> +     // prefix related
> +     uint8_t v6pref[IPaddrlen];
> +     int prefixlen;
> +     uint8_t onlink;                         // flag: address is `on-link'
> +     uint8_t autoflag;                       // flag: autonomous
> +     uint32_t validlt;                       // valid lifetime (seconds)
> +     uint32_t preflt;                        // preferred lifetime (seconds)
> +};
> +
> +struct ctl {
> +     struct ctl *next;
> +     char *ctl;
> +};
> +
> +extern struct ctl *firstctl, **ctll;
> +extern struct conf conf;
> +extern int noconfig;
> +extern int ipv6auto;
> +extern int debug;
> +extern int dodhcp;
> +extern int dolog;
> +extern int nip;
> +extern int plan9;
> +extern int dupl_disc;
> +extern int myifc;
> +extern char *vs;
> +
> +void adddefroute(char *, uint8_t *);
> +int addoption(char *opt);
> +void binddevice(void);
> +void bootprequest(void);
> +void controldevice(void);
> +void dhcpquery(int, int);
> +void dhcprecv(void);
> +void dhcpsend(int);
> +int dhcptimer(void);
> +void dhcpwatch(int);
> +void doadd(int);
> +void doremove(void);
> +void dounbind(void);
> +void ea2lla(uint8_t *lla, uint8_t *ea);
> +int getndb(void);
> +void getoptions(uint8_t *p);
> +int ip4cfg(void);
> +int ip6cfg(int autoconf);
> +int ipconfig4(void);
> +int ipconfig6(int);
> +void ipv62smcast(uint8_t *smcast, uint8_t *a);
> +long jitter(void);
> +void lookforip(char *);
> +void mkclientid(void);
> +void ndbconfig(void);
> +int nipifcs(char *);
> +int openlisten(void);
> +uint8_t *optadd(uint8_t *, int, void *, int);
> +uint8_t *optaddaddr(uint8_t *, int, uint8_t *);
> +uint8_t *optaddbyte(uint8_t *, int, int);
> +uint8_t *optaddstr(uint8_t *, int, char *);
> +uint8_t *optaddulong(uint8_t *, int, uint32_t);
> +uint8_t *optaddvec(uint8_t *, int, uint8_t *, int);
> +uint8_t *optget(uint8_t *, int, int *);
> +int optgetaddr(uint8_t *, int, uint8_t *);
> +int optgetaddrs(uint8_t *, int, uint8_t *, int);
> +int optgetbyte(uint8_t *, int);
> +int optgetp9addrs(uint8_t *ap, int op, uint8_t *ip, int n);
> +int optgetstr(uint8_t *, int, char *, int);
> +uint32_t optgetulong(uint8_t *, int);
> +int optgetvec(uint8_t *, int, uint8_t *, int);
> +struct bootp *parsebootp(uint8_t *p, int n);
> +int parseoptions(uint8_t *p, int n);
> +int parseverb(char *);
> +void procsetname(char *fmt, ...);
> +void putndb(void);
> +uint32_t randint(uint32_t low, uint32_t hi);
> +void tweakservers(void);
> +void usage(void);
> +int validip(uint8_t *);
> +void warning(char *fmt, ...);
> +void writendb(char *, int, int);
> +
> +/*
> + * IPv6
> + */
> +void doipv6(int);
> +int ipconfig6(int);
> +void recvra6(void);
> +void sendra6(void);
> +void v6paraminit(struct conf *);
> +
> +enum {
> +     IsRouter = 1,
> +     IsHostRecv = 2,
> +     IsHostNoRecv = 3,
> +
> +     MAClen = 6,
> +
> +     IPv4 = 4,
> +     IPv6 = 6,
> +     Defmtu = 1400,
> +
> +     IP_HOPBYHOP = 0,
> +     ICMPv4 = 1,
> +     IP_IGMPPROTO = 2,
> +     IP_TCPPROTO = 6,
> +     IP_UDPPROTO = 17,
> +     IP_ILPROTO = 40,
> +     IP_v6ROUTE = 43,
> +     IP_v6FRAG = 44,
> +     IP_IPsecESP = 50,
> +     IP_IPsecAH = 51,
> +     IP_v6NOMORE = 59,
> +     ICMP6_RS = 133,
> +     ICMP6_RA = 134,
> +
> +     IP_IN_IP = 41,
> +};
> +
> +enum {
> +     MFMASK = 1 << 7,
> +     OCMASK = 1 << 6,
> +     OLMASK = 1 << 7,
> +     AFMASK = 1 << 6,
> +};
> +
> +enum {
> +     MAXTTL = 255,
> +     D64HLEN = IPV6HDR_LEN - IPV4HDR_LEN,
> +     IP_MAX = 32 * 1024,
> +};
> +
> +struct routersol {
> +     uint8_t vcf[4];                         // version:4, traffic class:8, 
> flow label:20
> +     uint8_t ploadlen[2];            // payload length: packet length - 40
> +     uint8_t proto;                          // next header  type
> +     uint8_t ttl;                            // hop limit
> +     uint8_t src[IPaddrlen];
> +     uint8_t dst[IPaddrlen];
> +     uint8_t type;
> +     uint8_t code;
> +     uint8_t cksum[2];
> +     uint8_t res[4];
> +};
> +
> +struct routeradv {
> +     uint8_t vcf[4];                         // version:4, traffic class:8, 
> flow label:20
> +     uint8_t ploadlen[2];            // payload length: packet length - 40
> +     uint8_t proto;                          // next header type
> +     uint8_t ttl;                            // hop limit
> +     uint8_t src[IPaddrlen];
> +     uint8_t dst[IPaddrlen];
> +     uint8_t type;
> +     uint8_t code;
> +     uint8_t cksum[2];
> +     uint8_t cttl;
> +     uint8_t mor;
> +     uint8_t routerlt[2];
> +     uint8_t rchbltime[4];
> +     uint8_t rxmtimer[4];
> +};
> +
> +struct lladdropt {
> +     uint8_t type;
> +     uint8_t len;
> +     uint8_t lladdr[MAClen];
> +};
> +
> +struct prefixopt {
> +     uint8_t type;
> +     uint8_t len;
> +     uint8_t plen;
> +     uint8_t lar;
> +     uint8_t validlt[4];
> +     uint8_t preflt[4];
> +     uint8_t reserv[4];
> +     uint8_t pref[IPaddrlen];
> +};
> +
> +struct mtuopt {
> +     uint8_t type;
> +     uint8_t len;
> +     uint8_t reserv[2];
> +     uint8_t mtu[4];
> +};
> diff --git a/tools/apps/ipconfig/ipv6.c b/tools/apps/ipconfig/ipv6.c
> new file mode 100644
> index 0000000..95b633a
> --- /dev/null
> +++ b/tools/apps/ipconfig/ipv6.c
> @@ -0,0 +1,954 @@
> +/*
> + * This file is part of the UCB release of Plan 9. It is subject to the 
> license
> + * terms in the LICENSE file found in the top-level directory of this
> + * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
> + * part of the UCB release of Plan 9, including this file, may be copied,
> + * modified, propagated, or distributed except according to the terms 
> contained
> + * in the LICENSE file.
> + */
> +
> +//
> +// ipconfig for IPv6
> +//   RS means Router Solicitation
> +//   RA means Router Advertisement
> +//
> +
> +#include <benchutil/alarm.h>
> +#include <iplib/iplib.h>
> +#include <parlib/common.h>
> +#include <parlib/parlib.h>
> +#include <parlib/uthread.h>
> +
> +#include <ctype.h>
> +#include <fcntl.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <time.h>
> +#include <unistd.h>
> +
> +#include "icmp.h"
> +#include "ipconfig.h"
> +
> +#define RALOG "v6routeradv"
> +
> +#define NetS(x) (((uint8_t *)x)[0] << 8 | ((uint8_t *)x)[1])
> +#define NetL(x) \
> +     (((uint8_t *)x)[0] << 24 | ((uint8_t *)x)[1] << 16 | \
> +      ((uint8_t *)x)[2] << 8 | ((uint8_t *)x)[3])
> +
> +enum {
> +     ICMP6LEN = 4,
> +};
> +
> +// ICMP v4 & v6 header
> +struct icmphdr {
> +     uint8_t type;
> +     uint8_t code;
> +     uint8_t cksum[2];    // Checksum
> +     uint8_t data[];
> +};
> +
> +char *icmpmsg6[Maxtype6 + 1] = {
> +        [EchoReply] "EchoReply",
> +        [UnreachableV6] "UnreachableV6",
> +        [PacketTooBigV6] "PacketTooBigV6",
> +        [TimeExceedV6] "TimeExceedV6",
> +        [Redirect] "Redirect",
> +        [EchoRequest] "EchoRequest",
> +        [TimeExceed] "TimeExceed",
> +        [InParmProblem] "InParmProblem",
> +        [Timestamp] "Timestamp",
> +        [TimestampReply] "TimestampReply",
> +        [InfoRequest] "InfoRequest",
> +        [InfoReply] "InfoReply",
> +        [AddrMaskRequest] "AddrMaskRequest",
> +        [AddrMaskReply] "AddrMaskReply",
> +        [EchoRequestV6] "EchoRequestV6",
> +        [EchoReplyV6] "EchoReplyV6",
> +        [RouterSolicit] "RouterSolicit",
> +        [RouterAdvert] "RouterAdvert",
> +        [NbrSolicit] "NbrSolicit",
> +        [NbrAdvert] "NbrAdvert",
> +        [RedirectV6] "RedirectV6",
> +};
> +
> +static char *icmp6opts[] = {
> +        [0] "unknown option",
> +        [V6nd_srclladdr] "srcll_addr",
> +        [V6nd_targlladdr] "targll_addr",
> +        [V6nd_pfxinfo] "prefix",
> +        [V6nd_redirhdr] "redirect",
> +        [V6nd_mtu] "mtu",
> +        [V6nd_home] "home",
> +        [V6nd_srcaddrs] "src_addrs",
> +        [V6nd_ip] "ip",
> +        [V6nd_rdns] "rdns",
> +        [V6nd_9fs] "9fs",
> +        [V6nd_9auth] "9auth",
> +};
> +
> +uint8_t v6allroutersL[IPaddrlen] = {
> +    0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02
> +};
> +
> +uint8_t v6allnodesL[IPaddrlen] = {
> +    0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01
> +};
> +
> +uint8_t v6Unspecified[IPaddrlen] = {
> +    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
> +};
> +
> +uint8_t v6loopback[IPaddrlen] = {
> +    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
> +};
> +
> +uint8_t v6glunicast[IPaddrlen] = {
> +    0x08, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
> +};
> +
> +uint8_t v6linklocal[IPaddrlen] = {
> +    0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
> +};
> +
> +uint8_t v6solpfx[IPaddrlen] = {
> +    0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
> +    // last 3 bytes filled with low-order bytes of addr being solicited
> +    0xff, 0, 0, 0,
> +};
> +
> +uint8_t v6defmask[IPaddrlen] = {
> +    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0
> +};
> +
> +enum {
> +     Vadd,
> +     Vremove,
> +     Vunbind,
> +     Vaddpref6,
> +     Vra6,
> +};
> +
> +static void ralog(char *fmt, ...)
> +{
> +     char msg[512];
> +     va_list arg;
> +
> +     va_start(arg, fmt);
> +     vsnprintf(msg, sizeof(msg), fmt, arg);
> +     va_end(arg);
> +     fprintf(stderr, RALOG ": %s\n", msg);
> +}
> +
> +void ea2lla(uint8_t *lla, uint8_t *ea)
> +{
> +     assert(IPaddrlen == 16);
> +     memset(lla, 0, IPaddrlen);
> +     lla[0] = 0xFE;
> +     lla[1] = 0x80;
> +     lla[8] = ea[0] | 0x2;
> +     lla[9] = ea[1];
> +     lla[10] = ea[2];
> +     lla[11] = 0xFF;
> +     lla[12] = 0xFE;
> +     lla[13] = ea[3];
> +     lla[14] = ea[4];
> +     lla[15] = ea[5];
> +}
> +
> +void ipv62smcast(uint8_t *smcast, uint8_t *a)
> +{
> +     assert(IPaddrlen == 16);
> +     memset(smcast, 0, IPaddrlen);
> +     smcast[0] = 0xFF;
> +     smcast[1] = 0x02;
> +     smcast[11] = 0x1;
> +     smcast[12] = 0xFF;
> +     smcast[13] = a[13];
> +     smcast[14] = a[14];
> +     smcast[15] = a[15];
> +}
> +
> +void v6paraminit(struct conf *cf)
> +{
> +     cf->sendra = cf->recvra = 0;
> +     cf->mflag = 0;
> +     cf->oflag = 0;
> +     cf->maxraint = Maxv6initraintvl;
> +     cf->minraint = Maxv6initraintvl / 4;
> +     cf->linkmtu = 1500;
> +     cf->reachtime = V6reachabletime;
> +     cf->rxmitra = V6retranstimer;
> +     cf->ttl = MAXTTL;
> +
> +     cf->routerlt = 0;
> +
> +     cf->prefixlen = 64;
> +     cf->onlink = 0;
> +     cf->autoflag = 0;
> +     cf->validlt = cf->preflt = ~0L;
> +}
> +
> +static char *optname(unsigned opt)
> +{
> +     static char buf[32];
> +
> +     if (opt >= COUNT_OF(icmp6opts) || icmp6opts[opt] == NULL) {
> +             snprintf(buf, sizeof(buf), "unknown option %d", opt);
> +             return buf;
> +     }
> +
> +     return icmp6opts[opt];
> +}
> +
> +size_t opt_sprint(uint8_t *ps, uint8_t *pe, char *buf, size_t size)
> +{
> +     int otype, osz, pktsz;
> +     uint8_t *a;
> +     size_t n;
> +
> +     a = ps;
> +     n = 0;
> +     for (pktsz = pe - ps; pktsz > 0; pktsz -= osz) {
> +             otype = a[0];
> +             osz = a[1] * 8;
> +
> +             switch (otype) {
> +             default:
> +                     n += snprintf(buf + n, size - n, " option=%s ", 
> optname(otype));
> +             case V6nd_srclladdr:
> +             case V6nd_targlladdr:
> +                     if (pktsz < osz || osz != 8) {
> +                             n += snprintf(buf + n, size - n,
> +                                           " option=%s bad size=%d",
> +                                           optname(otype), osz);
> +                             return n;
> +                     }
> +                     n += snprintf(buf + n, size - n, " option=%s maddr=%E",
> +                                   optname(otype), a + 2);
> +                     break;
> +             case V6nd_pfxinfo:
> +                     if (pktsz < osz || osz != 32) {
> +                             n += snprintf(buf + n, size - n,
> +                                           " option=%s: bad size=%d",
> +                                           optname(otype), osz);
> +                             return n;
> +                     }
> +                     n += snprintf(buf, size - n,
> +                                   " option=%s pref=%R preflen=%3.3d 
> lflag=%1.1d aflag=%1.1d unused1=%1.1d validlt=%ud preflt=%ud unused2=%1.1d",
> +                                   optname(otype),
> +                                   a + 16,
> +                                   (int)(*(a + 2)),
> +                                   (*(a + 3) & (1 << 7)) != 0,
> +                                   (*(a + 3) & (1 << 6)) != 0,
> +                                   (*(a + 3) & 63) != 0,
> +                                   NetL(a + 4),
> +                                   NetL(a + 8),
> +                                   NetL(a + 12) != 0);
> +                     break;
> +             }
> +             a += osz;
> +     }
> +
> +     return n;
> +}
> +
> +static void pkt2str(uint8_t *ps, uint8_t *pe, char *buf, size_t size)
> +{
> +     int pktlen;
> +     char *tn;
> +     uint8_t *a;
> +     struct icmphdr *h;
> +     size_t n;
> +
> +     h = (struct icmphdr *)ps;
> +     a = ps + 4;
> +
> +     pktlen = pe - ps;
> +     if (pktlen < ICMP6LEN) {
> +             snprintf(buf, size, "short pkt");
> +             return;
> +     }
> +
> +     tn = icmpmsg6[h->type];
> +     if (tn == NULL)
> +             n = snprintf(buf, size, "t=%ud c=%d ck=%4.4ux",
> +                          h->type, h->code, (uint16_t)NetS(h->cksum));
> +     else
> +             n = snprintf(buf, size, "t=%s c=%d ck=%4.4ux",
> +                          tn, h->code, (uint16_t)NetS(h->cksum));
> +
> +     switch (h->type) {
> +     case RouterSolicit:
> +             ps += 8;
> +             n += snprintf(buf + n, size - n, " unused=%1.1d ", NetL(a) != 
> 0);
> +             opt_sprint(ps, pe, buf + n, size - n);
> +             break;
> +     case RouterAdvert:
> +             ps += 16;
> +             n += snprintf(buf + n, size - n,
> +                           " hoplim=%3.3d mflag=%1.1d oflag=%1.1d 
> unused=%1.1d routerlt=%d reachtime=%d rxmtimer=%d",
> +                           a[0],
> +                           (*(a + 1) & (1 << 7)) != 0,
> +                           (*(a + 1) & (1 << 6)) != 0,
> +                           (*(a + 1) & 63) != 0,
> +                           NetS(a + 2),
> +                           NetL(a + 4),
> +                           NetL(a + 8));
> +             opt_sprint(ps, pe, buf + n, size - n);
> +             break;
> +     default:
> +             snprintf(buf + n, size - n, " unexpected icmp6 pkt type");
> +             break;
> +     }
> +}
> +
> +int dialicmp(uint8_t *dst, int dport, int *ctlfd)
> +{
> +     int fd, cfd, n, m;
> +     char cmsg[100], name[128], connind[40];
> +     char hdrs[] = "headers";
> +
> +     snprintf(name, sizeof(name), "%s/icmpv6/clone", conf.mpoint);
> +     cfd = open(name, O_RDWR);
> +     if (cfd < 0) {
> +             fprintf(stderr, "dialicmp: can't open %s: %r", name);
> +             exit(-1);
> +     }
> +
> +     n = snprintf(cmsg, sizeof(cmsg), "connect %R!%d!r %d", dst, dport, 
> dport);
> +     m = write(cfd, cmsg, n);
> +     if (m < n) {
> +             fprintf(stderr, "dialicmp: can't write %s to %s: %r", cmsg, 
> name);
> +             exit(-1);
> +     }
> +
> +     lseek(cfd, 0, 0);
> +     n = read(cfd, connind, sizeof(connind));
> +     if (n < 0)
> +             connind[0] = 0;
> +     else if (n < sizeof(connind))
> +             connind[n] = 0;
> +     else
> +             connind[sizeof(connind) - 1] = 0;
> +
> +     snprintf(name, sizeof(name), "%s/icmpv6/%s/data", conf.mpoint, connind);
> +     fd = open(name, O_RDWR);
> +     if (fd < 0) {
> +             fprintf(stderr, "dialicmp: can't open %s: %r", name);
> +             exit(-1);
> +     }
> +
> +     n = sizeof(hdrs) - 1;
> +     if (write(cfd, hdrs, n) < n) {
> +             fprintf(stderr, "dialicmp: can't write `%s' to %s: %r", hdrs, 
> name);
> +             exit(-1);
> +     }
> +     *ctlfd = cfd;
> +     return fd;
> +}
> +
> +// Add an IPv6 address to an interface.
> +int ip6cfg(int autoconf)
> +{
> +     int dupfound = 0, n;
> +     char *p, *s;
> +     char buf[256], line[1024];
> +     uint8_t ethaddr[6];
> +     FILE *bp;
> +
> +     if (autoconf) {
> +             // create link-local addr
> +             if (myetheraddr(ethaddr, conf.dev) < 0) {
> +                     fprintf(stderr, "myetheraddr w/ %s failed: %r", 
> conf.dev);
> +                     exit(-1);
> +             }
> +             ea2lla(conf.laddr, ethaddr);
> +     }
> +
> +     if (dupl_disc)
> +             n = snprintf(buf, sizeof(buf), "try");
> +     else
> +             n = snprintf(buf, sizeof(buf), "add");
> +
> +     n += snprintf(buf + n, sizeof(buf) - n, " %R", conf.laddr);
> +     if (!validip(conf.mask))
> +             ipmove(conf.mask, v6defmask);
> +     n += snprintf(buf + n, sizeof(buf) - n, " %M", conf.mask);
> +     if (validip(conf.raddr)) {
> +             n += snprintf(buf + n, sizeof(buf) - n, " %R", conf.raddr);
> +             if (conf.mtu != 0)
> +                     n += snprintf(buf + n, sizeof(buf) - n, " %d", 
> conf.mtu);
> +     }
> +
> +     if (write(conf.cfd, buf, n) < 0) {
> +             warning("write(%s): %r", buf);
> +             return -1;
> +     }
> +
> +     if (!dupl_disc)
> +             return 0;
> +
> +     sleep(3000);
> +
> +     /* read arp table, look for addr duplication */
> +     snprintf(buf, sizeof(buf), "%s/arp", conf.mpoint);
> +     bp = fopen(buf, "r");
> +     if (bp == NULL) {
> +             warning("couldn't open %s: %r", buf);
> +             return -1;
> +     }
> +
> +     snprintf(buf, sizeof(buf), "%R", conf.laddr);
> +     for (p = buf; *p != '\0'; p++)
> +             if (isascii(*p) && isalpha(*p) && isupper(*p))
> +                     *p = tolower(*p);
> +     while ((s = fgets(line, sizeof(line), bp)) != NULL) {
> +             s[strlen(line) - 1] = 0;
> +             for (p = buf; *p != '\0'; p++)
> +                     if (isascii(*p) && isalpha(*p) && isupper(*p))
> +                             *p = tolower(*p);
> +             if (strstr(s, buf) != 0) {
> +                     warning("found dup entry in arp cache");
> +                     dupfound = 1;
> +                     break;
> +             }
> +     }
> +     fclose(bp);
> +
> +     if (dupfound)
> +             doremove();
> +     else {
> +             n = snprintf(buf, sizeof(buf), "add %R %M", conf.laddr, 
> conf.mask);
> +             if (validip(conf.raddr)) {
> +                     n += snprintf(buf + n, sizeof(buf) - n, " %R", 
> conf.raddr);
> +                     if (conf.mtu != 0)
> +                             n += snprintf(buf + n, sizeof(buf) - n, " %d", 
> conf.mtu);
> +             }
> +             write(conf.cfd, buf, n);
> +     }
> +     return 0;
> +}
> +
> +static int recvra6on(char *net, int conn)
> +{
> +     struct ipifc *ifc;
> +
> +     ifc = readipifc(net, NULL, conn);
> +     if (ifc == NULL)
> +             return 0;
> +     else if (ifc->sendra6 > 0)
> +             return IsRouter;
> +     else if (ifc->recvra6 > 0)
> +             return IsHostRecv;
> +     else
> +             return IsHostNoRecv;
> +}
> +
> +/* send icmpv6 router solicitation to multicast address for all routers */
> +static void sendrs(int fd)
> +{
> +     struct routersol *rs;
> +     uint8_t buff[sizeof(*rs)];
> +
> +     memset(buff, 0, sizeof(buff));
> +     rs = (struct routersol *)buff;
> +     memmove(rs->dst, v6allroutersL, IPaddrlen);
> +     memmove(rs->src, v6Unspecified, IPaddrlen);
> +     rs->type = ICMP6_RS;
> +
> +     if (write(fd, rs, sizeof(buff)) < sizeof(buff))
> +             ralog("sendrs: write failed, pkt size %d", sizeof(buff));
> +     else
> +             ralog("sendrs: sent solicitation to %R from %R on %s", rs->dst, 
> rs->src,
> +                   conf.dev);
> +}
> +
> +/*
> + * a router receiving a router adv from another
> + * router calls this; it is basically supposed to
> + * log the information in the ra and raise a flag
> + * if any parameter value is different from its configured values.
> + *
> + * doing nothing for now since I don't know where to log this yet.
> + */
> +static void recvrarouter(uint8_t buf[], int pktlen)
> +{
> +     ralog("i am a router and got a router advert");
> +}
> +
> +// host receiving a router advertisement calls this
> +static void ewrite(int fd, char *str)
> +{
> +     int n;
> +
> +     n = strlen(str);
> +     if (write(fd, str, n) != n)
> +             ralog("write(%s) failed: %r", str);
> +}
> +
> +static void issuebasera6(struct conf *cf)
> +{
> +     char cfg[256];
> +
> +     snprintf(cfg, sizeof(cfg),
> +              "ra6 mflag %d oflag %d reachtime %d rxmitra %d ttl %d routerlt 
> %d",
> +              cf->mflag, cf->oflag, cf->reachtime, cf->rxmitra,
> +              cf->ttl, cf->routerlt);
> +     ewrite(cf->cfd, cfg);
> +}
> +
> +static void issuerara6(struct conf *cf)
> +{
> +     char cfg[256];
> +
> +     snprintf(cfg, sizeof(cfg),
> +              "ra6 sendra %d recvra %d maxraint %d minraint %d linkmtu %d",
> +              cf->sendra, cf->recvra, cf->maxraint, cf->minraint, 
> cf->linkmtu);
> +     ewrite(cf->cfd, cfg);
> +}
> +
> +static void
> +issueadd6(struct conf *cf)
> +{
> +     char cfg[256];
> +
> +     snprintf(cfg, sizeof(cfg),
> +              "add6 %R %d %d %d %lud %lud", cf->v6pref, cf->prefixlen,
> +              cf->onlink, cf->autoflag, cf->validlt, cf->preflt);
> +     ewrite(cf->cfd, cfg);
> +}
> +
> +static void
> +recvrahost(uint8_t buf[], int pktlen)
> +{
> +     int arpfd, m, n;
> +     char abuf[100], *msg;
> +     uint8_t optype;
> +     struct lladdropt *llao;
> +     struct mtuopt *mtuo;
> +     struct prefixopt *prfo;
> +     struct routeradv *ra;
> +     static int first = 1;
> +
> +     ra = (struct routeradv *)buf;
> +     //      memmove(conf.v6gaddr, ra->src, IPaddrlen);
> +     conf.ttl = ra->cttl;
> +     conf.mflag = (MFMASK & ra->mor);
> +     conf.oflag = (OCMASK & ra->mor);
> +     conf.routerlt = nhgets(ra->routerlt);
> +     conf.reachtime = nhgetl(ra->rchbltime);
> +     conf.rxmitra = nhgetl(ra->rxmtimer);
> +
> +     //      issueadd6(&conf);               /* for conf.v6gaddr? */
> +     msg = "ra6 recvra 1";
> +     if (write(conf.cfd, msg, strlen(msg)) < 0)
> +             ralog("write(ra6 recvra 1) failed: %r");
> +     issuebasera6(&conf);
> +
> +     m = sizeof(*ra);
> +     while (pktlen - m > 0) {
> +             optype = buf[m];
> +             switch (optype) {
> +             case V6nd_srclladdr:
> +                     llao = (struct lladdropt *)&buf[m];
> +                     m += 8 * buf[m + 1];
> +                     if (llao->len != 1) {
> +                             ralog(
> +                                 "recvrahost: illegal len (%d) for source 
> link layer address option",
> +                                 llao->len);
> +                             return;
> +                     }
> +                     if (!ISIPV6LINKLOCAL(ra->src)) {
> +                             ralog("recvrahost: non-link-local src addr for 
> router adv %R",
> +                                   ra->src);
> +                             return;
> +                     }
> +
> +                     snprintf(abuf, sizeof(abuf), "%s/arp", conf.mpoint);
> +                     arpfd = open(abuf, O_WRONLY);
> +                     if (arpfd < 0) {
> +                             ralog("recvrahost: couldn't open %s to write: 
> %r", abuf);
> +                             return;
> +                     }
> +
> +                     n = snprintf(abuf, sizeof(abuf), "add ether %R %E", 
> ra->src,
> +                                  llao->lladdr);
> +                     if (write(arpfd, abuf, n) < n)
> +                             ralog("recvrahost: couldn't write to %s/arp", 
> conf.mpoint);
> +                     close(arpfd);
> +                     break;
> +             case V6nd_targlladdr:
> +             case V6nd_redirhdr:
> +                     m += 8 * buf[m + 1];
> +                     ralog("ignoring unexpected option type `%s' in 
> Routeradv",
> +                           optname(optype));
> +                     break;
> +             case V6nd_mtu:
> +                     mtuo = (struct mtuopt *)&buf[m];
> +                     m += 8 * mtuo->len;
> +                     conf.linkmtu = nhgetl(mtuo->mtu);
> +                     break;
> +             case V6nd_pfxinfo:
> +                     prfo = (struct prefixopt *)&buf[m];
> +                     m += 8 * prfo->len;
> +                     if (prfo->len != 4) {
> +                             ralog("illegal len (%d) for prefix option", 
> prfo->len);
> +                             return;
> +                     }
> +                     memmove(conf.v6pref, prfo->pref, IPaddrlen);
> +                     conf.prefixlen = prfo->plen;
> +                     conf.onlink = ((prfo->lar & OLMASK) != 0);
> +                     conf.autoflag = ((prfo->lar & AFMASK) != 0);
> +                     conf.validlt = nhgetl(prfo->validlt);
> +                     conf.preflt = nhgetl(prfo->preflt);
> +                     issueadd6(&conf);
> +                     if (first) {
> +                             first = 0;
> +                             ralog("got initial RA from %R on %s; pfx %R", 
> ra->src, conf.dev,
> +                                   prfo->pref);
> +                     }
> +                     break;
> +             default:
> +                     if (debug)
> +                             ralog("ignoring optype %d in Routeradv from %R",
> +                                   optype, ra->src);
> +             /* fall through */
> +             case V6nd_srcaddrs:
> +                     /* netsbd sends this, so quietly ignore it for now */
> +                     m += 8 * buf[m + 1];
> +                     break;
> +             }
> +     }
> +}
> +
> +/*
> + * daemon to receive router advertisements from routers
> + */
> +void recvra6(void)
> +{
> +     int fd, cfd, n, sendrscnt, sleepfor;
> +     uint8_t buf[4096];
> +
> +     /* TODO: why not v6allroutersL? */
> +     fd = dialicmp(v6allnodesL, ICMP6_RA, &cfd);
> +     if (fd < 0) {
> +             fprintf(stderr, "can't open icmp_ra connection: %r");
> +             exit(-1);
> +     }
> +
> +     sendrscnt = Maxv6rss;
> +
> +     switch (fork()) {
> +     case -1:
> +             fprintf(stderr, "can't fork: %r");
> +             exit(-1);
> +     default:
> +             return;
> +     case 0:
> +             break;
> +     }
> +
> +     ralog("recvra6 on %s", conf.dev);
> +     sleepfor = jitter();
> +     for (;;) {
> +             //
> +             // We only get 3 (Maxv6rss) tries, so make sure we
> +             // wait long enough to be certain that at least one RA
> +             // will be transmitted.
> +             //
> +             struct alarm_waiter waiter;
> +
> +             init_awaiter(&waiter, alarm_abort_sysc);
> +             waiter.data = current_uthread;
> +             set_awaiter_rel(&waiter, 1000 * MAX(sleepfor, 7000));
> +             set_alarm(&waiter);
> +             n = read(fd, buf, sizeof(buf));
> +             unset_alarm(&waiter);
> +
> +             if (n <= 0) {
> +                     if (sendrscnt > 0) {
> +                             sendrscnt--;
> +                             if (recvra6on(conf.mpoint, myifc) == IsHostRecv)
> +                                     sendrs(fd);
> +                             sleepfor = V6rsintvl + (lrand48() % 100);
> +                     }
> +                     if (sendrscnt == 0) {
> +                             sendrscnt--;
> +                             sleepfor = 0;
> +                             ralog("recvra6: no router advs after %d sols on 
> %s", Maxv6rss,
> +                                   conf.dev);
> +                     }
> +                     continue;
> +             }
> +
> +             sleepfor = 0;
> +             sendrscnt = -1; /* got at least initial ra; no whining */
> +             switch (recvra6on(conf.mpoint, myifc)) {
> +             case IsRouter:
> +                     recvrarouter(buf, n);
> +                     break;
> +             case IsHostRecv:
> +                     recvrahost(buf, n);
> +                     break;
> +             case IsHostNoRecv:
> +                     ralog("recvra6: recvra off, quitting on %s", conf.dev);
> +                     close(fd);
> +                     exit(0);
> +             default:
> +                     ralog("recvra6: unable to read router status on %s", 
> conf.dev);
> +                     break;
> +             }
> +     }
> +}
> +
> +/*
> + * return -1 -- error, reading/writing some file,
> + *         0 -- no arp table updates
> + *         1 -- successful arp table update
> + */
> +int recvrs(uint8_t *buf, int pktlen, uint8_t *sol)
> +{
> +     int n, optsz, arpfd;
> +     char abuf[256];
> +     struct routersol *rs;
> +     struct lladdropt *llao;
> +
> +     rs = (struct routersol *)buf;
> +     n = sizeof(*rs);
> +     optsz = pktlen - n;
> +     pkt2str(buf, buf + pktlen, abuf, sizeof(abuf));
> +
> +     if (optsz != sizeof(*llao))
> +             return 0;
> +     if (buf[n] != V6nd_srclladdr || 8 * buf[n + 1] != sizeof(*llao)) {
> +             ralog("rs opt err %s", abuf);
> +             return -1;
> +     }
> +
> +     ralog("rs recv %s", abuf);
> +
> +     if (memcmp(rs->src, v6Unspecified, IPaddrlen) == 0)
> +             return 0;
> +
> +     snprintf(abuf, sizeof(abuf), "%s/arp", conf.mpoint);
> +     arpfd = open(abuf, O_WRONLY);
> +     if (arpfd < 0) {
> +             ralog("recvrs: can't open %s/arp to write: %r", conf.mpoint);
> +             return -1;
> +     }
> +
> +     llao = (struct lladdropt *)&buf[n];
> +     n = snprintf(abuf, sizeof(abuf), "add ether %R %E", rs->src, 
> llao->lladdr);
> +     if (write(arpfd, abuf, n) < n) {
> +             ralog("recvrs: can't write to %s/arp: %r", conf.mpoint);
> +             close(arpfd);
> +             return -1;
> +     }
> +
> +     memmove(sol, rs->src, IPaddrlen);
> +     close(arpfd);
> +     return 1;
> +}
> +
> +void sendra(int fd, uint8_t *dst, int rlt)
> +{
> +     int pktsz, preflen;
> +     char abuf[1024], tmp[64];
> +     uint8_t buf[1024], macaddr[6], src[IPaddrlen];
> +     struct ipifc *ifc = NULL;
> +     struct iplifc *lifc, *nlifc;
> +     struct lladdropt *llao;
> +     struct prefixopt *prfo;
> +     struct routeradv *ra;
> +
> +     memset(buf, 0, sizeof(buf));
> +     ra = (struct routeradv *)buf;
> +
> +     myetheraddr(macaddr, conf.dev);
> +     ea2lla(src, macaddr);
> +     memmove(ra->src, src, IPaddrlen);
> +     memmove(ra->dst, dst, IPaddrlen);
> +     ra->type = ICMP6_RA;
> +     ra->cttl = conf.ttl;
> +
> +     if (conf.mflag > 0)
> +             ra->mor |= MFMASK;
> +     if (conf.oflag > 0)
> +             ra->mor |= OCMASK;
> +     if (rlt > 0)
> +             hnputs(ra->routerlt, conf.routerlt);
> +     else
> +             hnputs(ra->routerlt, 0);
> +     hnputl(ra->rchbltime, conf.reachtime);
> +     hnputl(ra->rxmtimer, conf.rxmitra);
> +
> +     pktsz = sizeof(*ra);
> +
> +     /* include all global unicast prefixes on interface in prefix options */
> +     ifc = readipifc(conf.mpoint, ifc, myifc);
> +     for (lifc = (ifc ? ifc->lifc : NULL); lifc; lifc = nlifc) {
> +             nlifc = lifc->next;
> +             prfo = (struct prefixopt *)(buf + pktsz);
> +             /* global unicast address? */
> +             if (!ISIPV6LINKLOCAL(lifc->ip) && !ISIPV6MCAST(lifc->ip) &&
> +                 memcmp(lifc->ip, IPnoaddr, IPaddrlen) != 0 &&
> +                 memcmp(lifc->ip, v6loopback, IPaddrlen) != 0 && 
> !isv4(lifc->ip)) {
> +                     memmove(prfo->pref, lifc->net, IPaddrlen);
> +
> +                     /* hack to find prefix length */
> +                     snprintf(tmp, sizeof(tmp), "%M", lifc->mask);
> +                     preflen = atoi(&tmp[1]);
> +                     prfo->plen = preflen & 0xff;
> +                     if (prfo->plen == 0)
> +                             continue;
> +
> +                     prfo->type = V6nd_pfxinfo;
> +                     prfo->len = 4;
> +                     prfo->lar = AFMASK;
> +                     hnputl(prfo->validlt, lifc->validlt);
> +                     hnputl(prfo->preflt, lifc->preflt);
> +                     pktsz += sizeof(*prfo);
> +             }
> +     }
> +     /*
> +      * include link layer address (mac address for now) in
> +      * link layer address option
> +      */
> +     llao = (struct lladdropt *)(buf + pktsz);
> +     llao->type = V6nd_srclladdr;
> +     llao->len = 1;
> +     memmove(llao->lladdr, macaddr, sizeof(macaddr));
> +     pktsz += sizeof(*llao);
> +     pkt2str(buf + 40, buf + pktsz, abuf, sizeof(abuf));
> +     if (write(fd, buf, pktsz) < pktsz)
> +             ralog("sendra fail %s: %r", abuf);
> +     else if (debug)
> +             ralog("sendra succ %s", abuf);
> +}
> +
> +/*
> + * daemon to send router advertisements to hosts
> + */
> +void sendra6(void)
> +{
> +     int fd, cfd, n, dstknown = 0, sendracnt, sleepfor, nquitmsgs;
> +     long lastra, now;
> +     uint8_t buf[4096], dst[IPaddrlen];
> +     struct ipifc *ifc = NULL;
> +
> +     fd = dialicmp(v6allnodesL, ICMP6_RS, &cfd);
> +     if (fd < 0) {
> +             fprintf(stderr, "can't open icmp_rs connection: %r");
> +             exit(-1);
> +     }
> +
> +     sendracnt = Maxv6initras;
> +     nquitmsgs = Maxv6finalras;
> +
> +     switch (fork()) {
> +     case -1:
> +             fprintf(stderr, "can't fork: %r");
> +             exit(-1);
> +     default:
> +             return;
> +     case 0:
> +             break;
> +     }
> +
> +     ralog("sendra6 on %s", conf.dev);
> +     sleepfor = jitter();
> +     for (;;) {
> +             struct alarm_waiter waiter;
> +
> +             init_awaiter(&waiter, alarm_abort_sysc);
> +             set_awaiter_rel(&waiter, 1000 * MAX(sleepfor, 0));
> +             lastra = time(0);
> +             set_alarm(&waiter);
> +             n = read(fd, buf, sizeof(buf));
> +             unset_alarm(&waiter);
> +
> +             ifc = readipifc(conf.mpoint, ifc, myifc);
> +             if (ifc == NULL) {
> +                     ralog("sendra6: can't read router params on %s", 
> conf.mpoint);
> +                     continue;
> +             }
> +
> +             if (ifc->sendra6 <= 0) {
> +                     if (nquitmsgs > 0) {
> +                             sendra(fd, v6allnodesL, 0);
> +                             nquitmsgs--;
> +                             sleepfor = Minv6interradelay + jitter();
> +                             continue;
> +                     } else {
> +                             ralog("sendra6: sendra off, quitting on %s", 
> conf.dev);
> +                             exit(0);
> +                     }
> +             }
> +
> +             nquitmsgs = Maxv6finalras;
> +
> +             if (n <= 0) { /* no RS */
> +                     if (sendracnt > 0)
> +                             sendracnt--;
> +             } else { /* respond to RS */
> +                     dstknown = recvrs(buf, n, dst);
> +                     now = time(0);
> +
> +                     if (now - lastra < Minv6interradelay) {
> +                             /* too close, skip */
> +                             sleepfor = lastra + Minv6interradelay + 
> jitter() - now;
> +                             continue;
> +                     }
> +                     sleep(jitter());
> +             }
> +             sleepfor = randint(ifc->rp.minraint, ifc->rp.maxraint);
> +             if (dstknown > 0)
> +                     sendra(fd, dst, 1);
> +             else
> +                     sendra(fd, v6allnodesL, 1);
> +     }
> +}
> +
> +void startra6(void)
> +{
> +     static const char routeon[] = "iprouting 1";
> +
> +     if (conf.recvra > 0)
> +             recvra6();
> +
> +     if (conf.sendra > 0) {
> +             if (write(conf.cfd, routeon, sizeof(routeon) - 1) < 0) {
> +                     warning("write (iprouting 1) failed: %r");
> +                     return;
> +             }
> +             sendra6();
> +             if (conf.recvra <= 0)
> +                     recvra6();
> +     }
> +}
> +
> +void doipv6(int what)
> +{
> +     nip = nipifcs(conf.mpoint);
> +     if (!noconfig) {
> +             lookforip(conf.mpoint);
> +             controldevice();
> +             binddevice();
> +     }
> +
> +     switch (what) {
> +     default:
> +             fprintf(stderr, "unknown IPv6 verb\n");
> +             exit(-1);
> +     case Vaddpref6:
> +             issueadd6(&conf);
> +             break;
> +     case Vra6:
> +             issuebasera6(&conf);
> +             issuerara6(&conf);
> +             startra6();
> +             break;
> +     }
> +}
> diff --git a/tools/apps/ipconfig/main.c b/tools/apps/ipconfig/main.c
> new file mode 100644
> index 0000000..9a3075d
> --- /dev/null
> +++ b/tools/apps/ipconfig/main.c
> @@ -0,0 +1,1929 @@
> +/*
> + * This file is part of the UCB release of Plan 9. It is subject to the 
> license
> + * terms in the LICENSE file found in the top-level directory of this
> + * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
> + * part of the UCB release of Plan 9, including this file, may be copied,
> + * modified, propagated, or distributed except according to the terms 
> contained
> + * in the LICENSE file.
> + */
> +
> +/*
> + * ipconfig - configure parameters of an ip stack
> + */
> +
> +#include <benchutil/alarm.h>
> +#include <iplib/iplib.h>
> +#include <ndblib/ndb.h>
> +#include <parlib/common.h>
> +#include <parlib/printf-ext.h>
> +#include <parlib/uthread.h>
> +#include <stdio.h>
> +#include <sys/stat.h>
> +#include <sys/types.h>
> +
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <time.h>
> +#include <unistd.h>
> +
> +#include "dhcp.h"
> +#include "ipconfig.h"
> +
> +#define DEBUG(...) do { if (debug) warning(__VA_ARGS__); } while (0)
> +
> +/* possible verbs */
> +enum {
> +     /* commands */
> +     Vadd,
> +     Vremove,
> +     Vunbind,
> +     Vaddpref6,
> +     Vra6,
> +     /* media */
> +     Vether,
> +     Vgbe,
> +     Vloopback,
> +     Vtorus,
> +     Vtree,
> +     Vpkt,
> +};
> +
> +enum {
> +     Taddr,
> +     Taddrs,
> +     Tstr,
> +     Tbyte,
> +     Tulong,
> +     Tvec,
> +};
> +
> +struct option {
> +     char *name;
> +     int type;
> +};
> +
> +/*
> + * I was too lazy to look up the types for each of these
> + * options.  If someone feels like it, please mail me a
> + * corrected array -- presotto
> + */
> +struct option option[256] = {
> +    [OBmask] { "ipmask", Taddr },
> +    [OBtimeoff] { "timeoff", Tulong },
> +    [OBrouter] { "ipgw", Taddrs },
> +    [OBtimeserver] { "time", Taddrs },
> +    [OBnameserver] { "name", Taddrs },
> +    [OBdnserver] { "dns", Taddrs },
> +    [OBlogserver] { "log", Taddrs },
> +    [OBcookieserver] { "cookie", Taddrs },
> +    [OBlprserver] { "lpr", Taddrs },
> +    [OBimpressserver] { "impress", Taddrs },
> +    [OBrlserver] { "rl", Taddrs },
> +    [OBhostname] { "sys", Tstr },
> +    [OBbflen] { "bflen", Tulong },
> +    [OBdumpfile] { "dumpfile", Tstr },
> +    [OBdomainname] { "dom", Tstr },
> +    [OBswapserver] { "swap", Taddrs },
> +    [OBrootpath] { "rootpath", Tstr },
> +    [OBextpath] { "extpath", Tstr },
> +    [OBipforward] { "ipforward", Taddrs },
> +    [OBnonlocal] { "nonlocal", Taddrs },
> +    [OBpolicyfilter] { "policyfilter", Taddrs },
> +    [OBmaxdatagram] { "maxdatagram", Tulong },
> +    [OBttl] { "ttl", Tulong },
> +    [OBpathtimeout] { "pathtimeout", Taddrs },
> +    [OBpathplateau] { "pathplateau", Taddrs },
> +    [OBmtu] { "mtu", Tulong },
> +    [OBsubnetslocal] { "subnetslocal", Taddrs },
> +    [OBbaddr] { "baddr", Taddrs },
> +    [OBdiscovermask] { "discovermask", Taddrs },
> +    [OBsupplymask] { "supplymask", Taddrs },
> +    [OBdiscoverrouter] { "discoverrouter", Taddrs },
> +    [OBrsserver] { "rs", Taddrs },
> +    [OBstaticroutes] { "staticroutes", Taddrs },
> +    [OBtrailerencap] { "trailerencap", Taddrs },
> +    [OBarptimeout] { "arptimeout", Tulong },
> +    [OBetherencap] { "etherencap", Taddrs },
> +    [OBtcpttl] { "tcpttl", Tulong },
> +    [OBtcpka] { "tcpka", Tulong },
> +    [OBtcpkag] { "tcpkag", Tulong },
> +    [OBnisdomain] { "nisdomain", Tstr },
> +    [OBniserver] { "ni", Taddrs },
> +    [OBntpserver] { "ntp", Taddrs },
> +    [OBnetbiosns] { "netbiosns", Taddrs },
> +    [OBnetbiosdds] { "netbiosdds", Taddrs },
> +    [OBnetbiostype] { "netbiostype", Taddrs },
> +    [OBnetbiosscope] { "netbiosscope", Taddrs },
> +    [OBxfontserver] { "xfont", Taddrs },
> +    [OBxdispmanager] { "xdispmanager", Taddrs },
> +    [OBnisplusdomain] { "nisplusdomain", Tstr },
> +    [OBnisplusserver] { "nisplus", Taddrs },
> +    [OBhomeagent] { "homeagent", Taddrs },
> +    [OBsmtpserver] { "smtp", Taddrs },
> +    [OBpop3server] { "pop3", Taddrs },
> +    [OBnntpserver] { "nntp", Taddrs },
> +    [OBwwwserver] { "www", Taddrs },
> +    [OBfingerserver] { "finger", Taddrs },
> +    [OBircserver] { "irc", Taddrs },
> +    [OBstserver] { "st", Taddrs },
> +    [OBstdaserver] { "stdar", Taddrs },
> +    [ODipaddr] { "ipaddr", Taddr },
> +    [ODlease] { "lease", Tulong },
> +    [ODoverload] { "overload", Taddr },
> +    [ODtype] { "type", Tbyte },
> +    [ODserverid] { "serverid", Taddr },
> +    [ODparams] { "params", Tvec },
> +    [ODmessage] { "message", Tstr },
> +    [ODmaxmsg] { "maxmsg", Tulong },
> +    [ODrenewaltime] { "renewaltime", Tulong },
> +    [ODrebindingtime] { "rebindingtime", Tulong },
> +    [ODvendorclass] { "vendorclass", Tvec },
> +    [ODclientid] { "clientid", Tvec },
> +    [ODtftpserver] { "tftp", Taddr },
> +    [ODbootfile] { "bootfile", Tstr },
> +};
> +
> +uint8_t defrequested[] = {
> +    OBmask, OBrouter, OBdnserver, OBhostname, OBdomainname, OBntpserver,
> +};
> +
> +uint8_t requested[256];
> +int nrequested;
> +
> +char *argv0;
> +int Oflag;
> +int beprimary = -1;
> +struct conf conf;
> +int debug;
> +int dodhcp;
> +int dondbconfig;
> +int dupl_disc = 1; /* flag: V6 duplicate neighbor discovery */
> +struct ctl *firstctl, **ctll;
> +struct ipifc *ifc;
> +int ipv6auto;
> +int myifc = -1;
> +char *ndboptions;
> +int nip;
> +int noconfig;
> +int nodhcpwatch;
> +char optmagic[4] = {0x63, 0x82, 0x53, 0x63};
> +int plan9 = 1;
> +int sendhostname;
> +
> +char *verbs[] = {
> +    [Vadd] "add",
> +    [Vremove] "remove",
> +    [Vunbind] "unbind",
> +    [Vether] "ether",
> +    [Vgbe] "gbe",
> +    [Vloopback] "loopback",
> +    [Vaddpref6] "add6",
> +    [Vra6] "ra6",
> +    [Vtorus] "torus",
> +    [Vtree] "tree",
> +    [Vpkt] "pkt",
> +};
> +
> +void usage(void)
> +{
> +     fprintf(stderr,
> +             "usage: %s %s\n\t%s\n",
> +             argv0,
> +             "[-6dDGnNOpPruX][-b baud][-c ctl]* [-g gw] [-h host][-m mtu]",
> +             "[-x mtpt][-o dhcpopt] type dev [verb] [laddr [mask [raddr [fs 
> [auth]]]]]");
> +     exit(1);
> +}
> +
> +void warning(char *fmt, ...)
> +{
> +     char buf[1024];
> +     va_list arg;
> +
> +     va_start(arg, fmt);
> +     vsnprintf(buf, sizeof(buf), fmt, arg);
> +     va_end(arg);
> +     fprintf(stderr, "%s: %s\n", argv0, buf);
> +}
> +
> +char *sysname(void)
> +{
> +     static char sname[256];
> +
> +     gethostname(sname, sizeof(sname));
> +
> +     return sname;
> +}
> +
> +void parsenorm(int argc, char **argv)
> +{
> +     switch (argc) {
> +     case 5:
> +             if (parseip(conf.auth, argv[4]) == -1)
> +                     usage();
> +             /* fall through */
> +     case 4:
> +             if (parseip(conf.fs, argv[3]) == -1)
> +                     usage();
> +             /* fall through */
> +     case 3:
> +             if (parseip(conf.raddr, argv[2]) == -1)
> +                     usage();
> +             /* fall through */
> +     case 2:
> +             /*
> +              * can't test for parseipmask()==-1 cuz 255.255.255.255
> +              * looks like that.
> +              */
> +             if (strcmp(argv[1], "0") != 0)
> +                     parseipmask(conf.mask, argv[1]);
> +             /* fall through */
> +     case 1:
> +             if (parseip(conf.laddr, argv[0]) == -1)
> +                     usage();
> +             /* fall through */
> +     case 0:
> +             break;
> +     default:
> +             usage();
> +     }
> +}
> +
> +void parse6pref(int argc, char **argv)
> +{
> +     switch (argc) {
> +     case 6:
> +             conf.preflt = strtoul(argv[5], 0, 10);
> +             /* fall through */
> +     case 5:
> +             conf.validlt = strtoul(argv[4], 0, 10);
> +             /* fall through */
> +     case 4:
> +             conf.autoflag = (atoi(argv[3]) != 0);
> +             /* fall through */
> +     case 3:
> +             conf.onlink = (atoi(argv[2]) != 0);
> +             /* fall through */
> +     case 2:
> +             conf.prefixlen = atoi(argv[1]);
> +             /* fall through */
> +     case 1:
> +             if (parseip(conf.v6pref, argv[0]) == -1) {
> +                     fprintf(stderr, "bad address %s", argv[0]);
> +                     exit(-1);
> +             }
> +             break;
> +     }
> +     DEBUG("parse6pref: pref %R len %d", conf.v6pref, conf.prefixlen);
> +}
> +
> +/* parse router advertisement (keyword, value) pairs */
> +void parse6ra(int argc, char *argv[])
> +{
> +     int i, argsleft;
> +     char *kw, *val;
> +
> +     if (argc % 2 != 0)
> +             usage();
> +
> +     i = 0;
> +     for (argsleft = argc; argsleft > 1; argsleft -= 2) {
> +             kw = argv[i];
> +             val = argv[i + 1];
> +             if (strcmp(kw, "recvra") == 0)
> +                     conf.recvra = (atoi(val) != 0);
> +             else if (strcmp(kw, "sendra") == 0)
> +                     conf.sendra = (atoi(val) != 0);
> +             else if (strcmp(kw, "mflag") == 0)
> +                     conf.mflag = (atoi(val) != 0);
> +             else if (strcmp(kw, "oflag") == 0)
> +                     conf.oflag = (atoi(val) != 0);
> +             else if (strcmp(kw, "maxraint") == 0)
> +                     conf.maxraint = atoi(val);
> +             else if (strcmp(kw, "minraint") == 0)
> +                     conf.minraint = atoi(val);
> +             else if (strcmp(kw, "linkmtu") == 0)
> +                     conf.linkmtu = atoi(val);
> +             else if (strcmp(kw, "reachtime") == 0)
> +                     conf.reachtime = atoi(val);
> +             else if (strcmp(kw, "rxmitra") == 0)
> +                     conf.rxmitra = atoi(val);
> +             else if (strcmp(kw, "ttl") == 0)
> +                     conf.ttl = atoi(val);
> +             else if (strcmp(kw, "routerlt") == 0)
> +                     conf.routerlt = atoi(val);
> +             else {
> +                     warning("bad ra6 keyword %s", kw);
> +                     usage();
> +             }
> +             i += 2;
> +     }
> +
> +     /* consistency check */
> +     if (conf.maxraint < conf.minraint) {
> +             fprintf(stderr, "maxraint %d < minraint %d",
> +                     conf.maxraint, conf.minraint);
> +             exit(-1);
> +     }
> +}
> +
> +void init(void)
> +{
> +     srand(lrand48());
> +     if (register_printf_specifier('E', printf_ethaddr,
> +                                   printf_ethaddr_info) != 0)
> +             fprintf(stderr, "Installing 'E' failed\n");
> +     if (register_printf_specifier('R', printf_ipaddr, printf_ipaddr_info) 
> != 0)
> +             fprintf(stderr, "Installing 'i' failed\n");
> +     if (register_printf_specifier('M', printf_ipmask, printf_ipmask_info) 
> != 0)
> +             fprintf(stderr, "Installing 'M' failed\n");
> +
> +     setnetmtpt(conf.mpoint, sizeof(conf).mpoint, NULL);
> +     conf.cputype = getenv("cputype");
> +     if (conf.cputype == NULL)
> +             conf.cputype = "386";
> +
> +     ctll = &firstctl;
> +     v6paraminit(&conf);
> +
> +     /* init set of requested dhcp parameters with the default */
> +     nrequested = sizeof(defrequested);
> +     memcpy(requested, defrequested, nrequested);
> +}
> +
> +int parseargs(int argc, char *argv[])
> +{
> +     char *p;
> +     int action, verb;
> +
> +     /* default to any host name we already have */
> +     if (*conf.hostname == 0) {
> +             p = getenv("sysname");
> +             if (p == NULL || *p == 0)
> +                     p = sysname();
> +             if (p != NULL)
> +                     strncpy(conf.hostname, p, sizeof(conf).hostname - 1);
> +     }
> +
> +     /* defaults */
> +     conf.type = "ether";
> +     conf.dev = "/net/ether0";
> +     action = Vadd;
> +
> +     /* get optional medium and device */
> +     if (argc > 0) {
> +             verb = parseverb(*argv);
> +             switch (verb) {
> +             case Vether:
> +             case Vgbe:
> +             case Vloopback:
> +             case Vtorus:
> +             case Vtree:
> +             case Vpkt:
> +                     conf.type = *argv++;
> +                     argc--;
> +                     if (argc > 0) {
> +                             conf.dev = *argv++;
> +                             argc--;
> +                     }
> +                     break;
> +             }
> +     }
> +
> +     /* get optional verb */
> +     if (argc > 0) {
> +             verb = parseverb(*argv);
> +             switch (verb) {
> +             case Vether:
> +             case Vgbe:
> +             case Vloopback:
> +             case Vtorus:
> +             case Vtree:
> +             case Vpkt:
> +                     fprintf(stderr, "medium %s already specified", 
> conf.type);
> +                     exit(-1);
> +             case Vadd:
> +             case Vremove:
> +             case Vunbind:
> +             case Vaddpref6:
> +             case Vra6:
> +                     argv++;
> +                     argc--;
> +                     action = verb;
> +                     break;
> +             }
> +     }
> +
> +     /* get verb-dependent arguments */
> +     switch (action) {
> +     case Vadd:
> +     case Vremove:
> +     case Vunbind:
> +             parsenorm(argc, argv);
> +             break;
> +     case Vaddpref6:
> +             parse6pref(argc, argv);
> +             break;
> +     case Vra6:
> +             parse6ra(argc, argv);
> +             break;
> +     }
> +     return action;
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +     int retry, action, ch;
> +     struct ctl *cp;
> +
> +     init();
> +     retry = 0;
> +     while ((ch = getopt(argc, argv, "6b:c:dDg:h:m:nNo:OpPrux:X")) != -1) {
> +             switch (ch) {
> +             case '6': /* IPv6 auto config */
> +                     ipv6auto = 1;
> +                     break;
> +             case 'b':
> +                     conf.baud = optarg;
> +                     break;
> +             case 'c':
> +                     cp = malloc(sizeof(*cp));
> +                     if (cp == NULL) {
> +                             fprintf(stderr, "%r");
> +                             exit(1);
> +                     }
> +                     *ctll = cp;
> +                     ctll = &cp->next;
> +                     cp->next = NULL;
> +                     cp->ctl = optarg;
> +                     break;
> +             case 'd':
> +                     dodhcp = 1;
> +                     break;
> +             case 'D':
> +                     debug = 1;
> +                     break;
> +             case 'g':
> +                     if (parseip(conf.gaddr, optarg) == -1)
> +                             usage();
> +                     break;
> +             case 'G':
> +                     plan9 = 0;
> +                     break;
> +             case 'h':
> +                     snprintf(conf.hostname, sizeof(conf).hostname,
> +                              "%s", optarg);
> +                     sendhostname = 1;
> +                     break;
> +             case 'm':
> +                     conf.mtu = atoi(optarg);
> +                     break;
> +             case 'n':
> +                     noconfig = 1;
> +                     break;
> +             case 'N':
> +                     dondbconfig = 1;
> +                     break;
> +             case 'o':
> +                     if (addoption(optarg) < 0)
> +                             usage();
> +                     break;
> +             case 'O':
> +                     Oflag = 1;
> +                     break;
> +             case 'p':
> +                     beprimary = 1;
> +                     break;
> +             case 'P':
> +                     beprimary = 0;
> +                     break;
> +             case 'r':
> +                     retry = 1;
> +                     break;
> +             case 'u': /* IPv6: duplicate neighbour disc. off */
> +                     dupl_disc = 0;
> +                     break;
> +             case 'x':
> +                     setnetmtpt(conf.mpoint, sizeof(conf).mpoint, optarg);
> +                     break;
> +             case 'X':
> +                     nodhcpwatch = 1;
> +                     break;
> +             default:
> +                     usage();
> +             }
> +     }
> +     argv0 = "ipconfig"; /* boot invokes us as tcp? */
> +     argc -= optind;
> +     argv += optind;
> +
> +     action = parseargs(argc, argv);
> +     switch (action) {
> +     case Vadd:
> +             doadd(retry);
> +             break;
> +     case Vremove:
> +             doremove();
> +             break;
> +     case Vunbind:
> +             dounbind();
> +             break;
> +     case Vaddpref6:
> +     case Vra6:
> +             doipv6(action);
> +             break;
> +     }
> +
> +     return 0;
> +}
> +
> +int havendb(char *net)
> +{
> +     struct stat s;
> +     char buf[128];
> +
> +     snprintf(buf, sizeof(buf), "%s/ndb", net);
> +     if (stat(buf, &s) < 0)
> +             return 0;
> +     if (s.st_size == 0)
> +             return 0;
> +     return 1;
> +}
> +
> +void doadd(int retry)
> +{
> +     int tries;
> +
> +     /* get number of preexisting interfaces */
> +     nip = nipifcs(conf.mpoint);
> +     if (beprimary == -1 && (nip == 0 || !havendb(conf.mpoint)))
> +             beprimary = 1;
> +
> +     /* get ipifc into name space and condition device for ip */
> +     if (!noconfig) {
> +             lookforip(conf.mpoint);
> +             controldevice();
> +             binddevice();
> +     }
> +
> +     if (ipv6auto) {
> +             if (ip6cfg(ipv6auto) < 0) {
> +                     fprintf(stderr, "can't automatically start IPv6 on %s",
> +                             conf.dev);
> +                     exit(-1);
> +             }
> +     } else if (validip(conf.laddr) && !isv4(conf.laddr)) {
> +             if (ip6cfg(0) < 0)
> +                     fprintf(stderr, "can't start IPv6 on %s, address %R",
> +                             conf.dev, conf.laddr);
> +                     exit(-1);
> +     }
> +
> +     if (!validip(conf.laddr)) {
> +             if (dondbconfig)
> +                     ndbconfig();
> +             else
> +                     dodhcp = 1;
> +     }
> +
> +     /* run dhcp if we need something */
> +     if (dodhcp) {
> +             mkclientid();
> +             for (tries = 0; tries < 30; tries++) {
> +                     dhcpquery(!noconfig, Sselecting);
> +                     if (conf.state == Sbound)
> +                             break;
> +                     usleep(1000 * 1000);
> +             }
> +     }
> +
> +     if (!validip(conf.laddr)) {
> +             if (retry && dodhcp && !noconfig) {
> +                     warning("couldn't determine ip address, retrying");
> +                     dhcpwatch(1);
> +                     return;
> +             }
> +             fprintf(stderr, "no success with DHCP");
> +             exit(-1);
> +     }
> +
> +
> +     if (!noconfig) {
> +             if (ip4cfg() < 0) {
> +                     fprintf(stderr, "can't start ip");
> +                     exit(-1);
> +             }
> +             if (dodhcp && conf.lease != Lforever)
> +                     dhcpwatch(0);
> +     }
> +
> +     /* leave everything we've learned somewhere other procs can find it */
> +     if (beprimary == 1) {
> +             putndb();
> +             tweakservers();
> +     }
> +}
> +
> +void doremove(void)
> +{
> +     char file[128];
> +     char buf[256];
> +     int cfd;
> +     struct ipifc *nifc;
> +     struct iplifc *lifc;
> +
> +     if (!validip(conf.laddr)) {
> +             fprintf(stderr, "remove requires an address");
> +             exit(-1);
> +     }
> +     ifc = readipifc(conf.mpoint, ifc, -1);
> +     for (nifc = ifc; nifc != NULL; nifc = nifc->next) {
> +             if (strcmp(nifc->dev, conf.dev) != 0)
> +                     continue;
> +             for (lifc = nifc->lifc; lifc != NULL; lifc = lifc->next) {
> +                     if (ipcmp(conf.laddr, lifc->ip) != 0)
> +                             continue;
> +                     if (validip(conf.mask) && ipcmp(conf.mask, lifc->mask) 
> != 0)
> +                             continue;
> +                     if (validip(conf.raddr) && ipcmp(conf.raddr, lifc->net) 
> != 0)
> +                             continue;
> +
> +                     snprintf(file, sizeof(file), "%s/ipifc/%d/ctl",
> +                              conf.mpoint, nifc->index);
> +                     cfd = open(file, O_RDWR);
> +                     if (cfd < 0) {
> +                             warning("can't open %s: %r", conf.mpoint);
> +                             continue;
> +                     }
> +                     snprintf(buf, sizeof(buf), "remove %R %M", lifc->ip, 
> lifc->mask);
> +                     if (write(cfd, buf, strlen(buf)) != strlen(buf))
> +                             warning("can't remove %R %M from %s: %r",
> +                                     lifc->ip, lifc->mask, file);
> +                     close(cfd);
> +             }
> +     }
> +}
> +
> +void dounbind(void)
> +{
> +     struct ipifc *nifc;
> +     char file[128];
> +     int cfd;
> +
> +     ifc = readipifc(conf.mpoint, ifc, -1);
> +     for (nifc = ifc; nifc != NULL; nifc = nifc->next) {
> +             if (strcmp(nifc->dev, conf.dev) == 0) {
> +                     snprintf(file, sizeof(file), "%s/ipifc/%d/ctl",
> +                              conf.mpoint, nifc->index);
> +                     cfd = open(file, O_RDWR);
> +                     if (cfd < 0) {
> +                             warning("can't open %s: %r", conf.mpoint);
> +                             break;
> +                     }
> +                     if (write(cfd, "unbind", strlen("unbind")) < 0)
> +                             warning("can't unbind from %s: %r", file);
> +                     close(cfd);
> +                     break;
> +             }
> +     }
> +}
> +
> +/* set the default route */
> +void adddefroute(char *mpoint, uint8_t *gaddr)
> +{
> +     char buf[256];
> +     int cfd;
> +
> +     sprintf(buf, "%s/iproute", mpoint);
> +     cfd = open(buf, O_RDWR);
> +     if (cfd < 0)
> +             return;
> +
> +     if (isv4(gaddr))
> +             snprintf(buf, sizeof(buf), "add 0 0 %R", gaddr);
> +     else
> +             snprintf(buf, sizeof(buf), "add :: /0 %R", gaddr);
> +     write(cfd, buf, strlen(buf));
> +     close(cfd);
> +}
> +
> +/* create a client id */
> +void mkclientid(void)
> +{
> +     if ((strcmp(conf.type, "ether") == 0) || (strcmp(conf.type, "gbe") == 
> 0)) {
> +             if (myetheraddr(conf.hwa, conf.dev) == 0) {
> +                     conf.hwalen = 6;
> +                     conf.hwatype = 1;
> +                     conf.cid[0] = conf.hwatype;
> +                     memmove(&conf.cid[1], conf.hwa, conf.hwalen);
> +                     conf.cidlen = conf.hwalen + 1;
> +             } else {
> +                     conf.hwatype = -1;
> +                     snprintf((char *)conf.cid, sizeof(conf).cid, 
> "plan9_%ld.%d",
> +                              lrand48(), getpid());
> +                     conf.cidlen = strlen((char *)conf.cid);
> +             }
> +     }
> +}
> +
> +/* bind ip into the namespace */
> +void lookforip(char *net)
> +{
> +     struct stat s;
> +     char proto[64];
> +
> +     snprintf(proto, sizeof(proto), "%s/ipifc", net);
> +     if (stat(proto, &s) < 0) {
> +             fprintf(stderr, "no ip stack bound onto %s", net);
> +             exit(-1);
> +     }
> +}
> +
> +/* send some ctls to a device */
> +void controldevice(void)
> +{
> +     char ctlfile[256];
> +     int fd;
> +     struct ctl *cp;
> +
> +     if (firstctl == NULL ||
> +         (strcmp(conf.type, "ether") != 0 && strcmp(conf.type, "gbe") != 0))
> +             return;
> +
> +     snprintf(ctlfile, sizeof(ctlfile), "%s/clone", conf.dev);
> +     fd = open(ctlfile, O_RDWR);
> +     if (fd < 0) {
> +             fprintf(stderr, "can't open %s", ctlfile);
> +             exit(-1);
> +     }
> +
> +     for (cp = firstctl; cp != NULL; cp = cp->next) {
> +             if (write(fd, cp->ctl, strlen(cp->ctl)) < 0) {
> +                     fprintf(stderr, "ctl message %s: %r", cp->ctl);
> +                     exit(-1);
> +             }
> +             lseek(fd, 0, 0);
> +     }
> +}
> +
> +/* bind an ip stack to a device, leave the control channel open */
> +void binddevice(void)
> +{
> +     char buf[256];
> +
> +     if (myifc < 0) {
> +             /* get a new ip interface */
> +             snprintf(buf, sizeof(buf), "%s/ipifc/clone", conf.mpoint);
> +             conf.cfd = open(buf, O_RDWR);
> +             if (conf.cfd < 0) {
> +                     fprintf(stderr, "opening %s/ipifc/clone: %r", 
> conf.mpoint);
> +                     exit(-1);
> +             }
> +
> +             /* specify medium as ethernet, bind the interface to it */
> +             snprintf(buf, sizeof(buf), "bind %s %s", conf.type, conf.dev);
> +             if (write(conf.cfd, buf, strlen(buf)) != strlen(buf)) {
> +                     fprintf(stderr, "%s: bind %s %s: %r", buf, conf.type, 
> conf.dev);
> +                     exit(-1);
> +             }
> +     } else {
> +             /* open the old interface */
> +             snprintf(buf, sizeof(buf), "%s/ipifc/%d/ctl", conf.mpoint, 
> myifc);
> +             conf.cfd = open(buf, O_RDWR);
> +             if (conf.cfd < 0) {
> +                     fprintf(stderr, "open %s: %r", buf);
> +                     exit(-1);
> +             }
> +     }
> +}
> +
> +/* add a logical interface to the ip stack */
> +int ip4cfg(void)
> +{
> +     char buf[256];
> +     int n;
> +
> +     if (!validip(conf.laddr))
> +             return -1;
> +
> +     n = snprintf(buf, sizeof(buf), "add");
> +     n += snprintf(buf + n, sizeof(buf) - n, " %R", conf.laddr);
> +
> +     if (!validip(conf.mask))
> +             ipmove(conf.mask, defmask(conf.laddr));
> +     n += snprintf(buf + n, sizeof(buf) - n, " %R", conf.mask);
> +
> +     if (validip(conf.raddr)) {
> +             n += snprintf(buf + n, sizeof(buf) - n, " %R", conf.raddr);
> +             if (conf.mtu != 0)
> +                     n += snprintf(buf + n, sizeof(buf) - n, " %d", 
> conf.mtu);
> +     }
> +
> +     if (write(conf.cfd, buf, n) < 0) {
> +             warning("write(%s): %r", buf);
> +             return -1;
> +     }
> +
> +     if (beprimary == 1 && validip(conf.gaddr))
> +             adddefroute(conf.mpoint, conf.gaddr);
> +
> +     return 0;
> +}
> +
> +/* remove a logical interface to the ip stack */
> +void ipunconfig(void)
> +{
> +     char buf[256];
> +     int n;
> +
> +     if (!validip(conf.laddr))
> +             return;
> +     DEBUG("couldn't renew IP lease, releasing %R", conf.laddr);
> +     n = sprintf(buf, "remove");
> +     n += snprintf(buf + n, sizeof(buf) - n, " %R", conf.laddr);
> +
> +     if (!validip(conf.mask))
> +             ipmove(conf.mask, defmask(conf.laddr));
> +     n += snprintf(buf + n, sizeof(buf) - n, " %R", conf.mask);
> +
> +     write(conf.cfd, buf, n);
> +
> +     ipmove(conf.laddr, IPnoaddr);
> +     ipmove(conf.raddr, IPnoaddr);
> +     ipmove(conf.mask, IPnoaddr);
> +
> +     /* forget configuration info */
> +     if (beprimary == 1)
> +             writendb("", 0, 0);
> +}
> +
> +void dhcpquery(int needconfig, int startstate)
> +{
> +     char buf[256];
> +
> +     if (needconfig) {
> +             snprintf(buf, sizeof(buf), "add %R %R", IPnoaddr, IPnoaddr);
> +             write(conf.cfd, buf, strlen(buf));
> +     }
> +
> +     conf.fd = openlisten();
> +     if (conf.fd < 0) {
> +             conf.state = Sinit;
> +             return;
> +     }
> +
> +     /* try dhcp for 10 seconds */
> +     conf.xid = lrand48();
> +     conf.starttime = time(0);
> +     conf.state = startstate;
> +     switch (startstate) {
> +     case Sselecting:
> +             conf.offered = 0;
> +             dhcpsend(Discover);
> +             break;
> +     case Srenewing:
> +             dhcpsend(Request);
> +             break;
> +     default:
> +             fprintf(stderr, "internal error 0");
> +             exit(-1);
> +     }
> +     conf.resend = 0;
> +     conf.timeout = time(0) + 4;
> +
> +     while (conf.state != Sbound) {
> +             dhcprecv();
> +             if (dhcptimer() < 0)
> +                     break;
> +             if (time(0) - conf.starttime > 10)
> +                     break;
> +     }
> +     close(conf.fd);
> +
> +     if (needconfig) {
> +             snprintf(buf, sizeof(buf), "remove %R %R", IPnoaddr, IPnoaddr);
> +             write(conf.cfd, buf, strlen(buf));
> +     }
> +}
> +
> +enum {
> +     // This was an hour, but needs to be less for the ARM/GS1 until the 
> timer
> +     // code has been cleaned up (pb).
> +     Maxsleep = 450,
> +};
> +
> +void dhcpwatch(int needconfig)
> +{
> +     int secs, s;
> +     uint32_t t;
> +
> +     if (nodhcpwatch)
> +             return;
> +
> +     switch (fork()) {
> +     default:
> +             return;
> +     case 0:
> +             break;
> +     }
> +
> +     // procsetname("dhcpwatch");
> +     /* keep trying to renew the lease */
> +     for (;;) {
> +             if (conf.lease == 0)
> +                     secs = 5;
> +             else
> +                     secs = conf.lease >> 1;
> +
> +             /* avoid overflows */
> +             for (s = secs; s > 0; s -= t) {
> +                     if (s > Maxsleep)
> +                             t = Maxsleep;
> +                     else
> +                             t = s;
> +                     usleep(t * 1000 * 1000);
> +             }
> +
> +             if (conf.lease > 0) {
> +                     /*
> +                      * during boot, the starttime can be bogus so avoid
> +                      * spurious ipunconfig's
> +                      */
> +                     t = time(0) - conf.starttime;
> +                     if (t > (3 * secs) / 2)
> +                             t = secs;
> +                     if (t >= conf.lease) {
> +                             conf.lease = 0;
> +                             if (!noconfig) {
> +                                     ipunconfig();
> +                                     needconfig = 1;
> +                             }
> +                     } else
> +                             conf.lease -= t;
> +             }
> +             dhcpquery(needconfig, needconfig ? Sselecting : Srenewing);
> +
> +             if (needconfig && conf.state == Sbound) {
> +                     if (ip4cfg() < 0) {
> +                             fprintf(stderr, "can't start ip: %r");
> +                             exit(-1);
> +                     }
> +                     needconfig = 0;
> +                     /*
> +                      * leave everything we've learned somewhere that
> +                      * other procs can find it.
> +                      */
> +                     if (beprimary == 1) {
> +                             putndb();
> +                             tweakservers();
> +                     }
> +             }
> +     }
> +}
> +
> +int dhcptimer(void)
> +{
> +     uint32_t now;
> +
> +     now = time(0);
> +     if (now < conf.timeout)
> +             return 0;
> +
> +     switch (conf.state) {
> +     default:
> +             fprintf(stderr, "dhcptimer: unknown state %d", conf.state);
> +             exit(-1);
> +     case Sinit:
> +     case Sbound:
> +             break;
> +     case Sselecting:
> +     case Srequesting:
> +     case Srebinding:
> +             dhcpsend(conf.state == Sselecting ? Discover : Request);
> +             conf.timeout = now + 4;
> +             if (++conf.resend > 5) {
> +                     conf.state = Sinit;
> +                     return -1;
> +             }
> +             break;
> +     case Srenewing:
> +             dhcpsend(Request);
> +             conf.timeout = now + 1;
> +             if (++conf.resend > 3) {
> +                     conf.state = Srebinding;
> +                     conf.resend = 0;
> +             }
> +             break;
> +     }
> +     return 0;
> +}
> +
> +void dhcpsend(int type)
> +{
> +     struct bootp bp;
> +     uint8_t *p;
> +     int n;
> +     uint8_t vendor[64];
> +     struct udphdr *up = (struct udphdr *)bp.udphdr;
> +
> +     memset(&bp, 0, sizeof(bp));
> +
> +     hnputs(up->rport, 67);
> +     bp.op = Bootrequest;
> +     hnputl(bp.xid, conf.xid);
> +     hnputs(bp.secs, time(0) - conf.starttime);
> +     hnputs(bp.flags, 0);
> +     memmove(bp.optmagic, optmagic, 4);
> +     if (conf.hwatype >= 0 && conf.hwalen < sizeof(bp).chaddr) {
> +             memmove(bp.chaddr, conf.hwa, conf.hwalen);
> +             bp.hlen = conf.hwalen;
> +             bp.htype = conf.hwatype;
> +     }
> +     p = bp.optdata;
> +     p = optaddbyte(p, ODtype, type);
> +     p = optadd(p, ODclientid, conf.cid, conf.cidlen);
> +     switch (type) {
> +     default:
> +             fprintf(stderr, "dhcpsend: unknown message type: %d", type);
> +             exit(-1);
> +     case Discover:
> +             ipmove(up->raddr, IPv4bcast); /* broadcast */
> +             if (*conf.hostname && sendhostname)
> +                     p = optaddstr(p, OBhostname, conf.hostname);
> +             if (plan9) {
> +                     n = snprintf((char *)vendor, sizeof(vendor), "plan9_%s",
> +                                  conf.cputype);
> +                     p = optaddvec(p, ODvendorclass, vendor, n);
> +             }
> +             p = optaddvec(p, ODparams, requested, nrequested);
> +             if (validip(conf.laddr))
> +                     p = optaddaddr(p, ODipaddr, conf.laddr);
> +             break;
> +     case Request:
> +             switch (conf.state) {
> +             case Srenewing:
> +                     ipmove(up->raddr, conf.server);
> +                     v6tov4(bp.ciaddr, conf.laddr);
> +                     break;
> +             case Srebinding:
> +                     ipmove(up->raddr, IPv4bcast); /* broadcast */
> +                     v6tov4(bp.ciaddr, conf.laddr);
> +                     break;
> +             case Srequesting:
> +                     ipmove(up->raddr, IPv4bcast); /* broadcast */
> +                     p = optaddaddr(p, ODipaddr, conf.laddr);
> +                     p = optaddaddr(p, ODserverid, conf.server);
> +                     break;
> +             }
> +             p = optaddulong(p, ODlease, conf.offered);
> +             if (plan9) {
> +                     n = snprintf((char *)vendor, sizeof(vendor), "plan9_%s",
> +                                  conf.cputype);
> +                     p = optaddvec(p, ODvendorclass, vendor, n);
> +             }
> +             p = optaddvec(p, ODparams, requested, nrequested);
> +             if (*conf.hostname && sendhostname)
> +                     p = optaddstr(p, OBhostname, conf.hostname);
> +             break;
> +     case Release:
> +             ipmove(up->raddr, conf.server);
> +             v6tov4(bp.ciaddr, conf.laddr);
> +             p = optaddaddr(p, ODipaddr, conf.laddr);
> +             p = optaddaddr(p, ODserverid, conf.server);
> +             break;
> +     }
> +
> +     *p++ = OBend;
> +
> +     n = p - (uint8_t *)&bp;
> +
> +     /*
> +      * We use a maximum size DHCP packet to survive the
> +      * All_Aboard NAT package from Internet Share.  It
> +      * always replies to DHCP requests with a packet of the
> +      * same size, so if the request is too short the reply
> +      * is truncated.
> +      */
> +     if (write(conf.fd, &bp, sizeof(bp)) != sizeof(bp))
> +             warning("dhcpsend: write failed: %r");
> +}
> +
> +void rerrstr(char *buf, size_t buflen)
> +{
> +     snprintf(buf, buflen, "%s", errstr());
> +}
> +
> +void dhcprecv(void)
> +{
> +     int i, n, type;
> +     uint32_t lease;
> +     char err[256];
> +     uint8_t buf[8000], vopts[256], taddr[IPaddrlen];
> +     struct bootp *bp;
> +     struct alarm_waiter waiter;
> +
> +     init_awaiter(&waiter, alarm_abort_sysc);
> +     waiter.data = current_uthread;
> +
> +     memset(buf, 0, sizeof(buf));
> +     set_awaiter_rel(&waiter, 1000 * 1000);
> +     set_alarm(&waiter);
> +     n = read(conf.fd, buf, sizeof(buf));
> +     unset_alarm(&waiter);
> +
> +     if (n < 0) {
> +             rerrstr(err, sizeof(err));
> +             if (strstr(err, "interrupt") == NULL)
> +                     warning("dhcprecv: bad read: %s", err);
> +             else
> +                     DEBUG("dhcprecv: read timed out");
> +             return;
> +     }
> +     if (n == 0) {
> +             warning("dhcprecv: zero-length packet read");
> +             return;
> +     }
> +
> +     bp = parsebootp(buf, n);
> +     if (bp == 0) {
> +             DEBUG("parsebootp failed: dropping packet");
> +             return;
> +     }
> +
> +     type = optgetbyte(bp->optdata, ODtype);
> +     switch (type) {
> +     default:
> +             warning("dhcprecv: unknown type: %d", type);
> +             break;
> +     case Offer:
> +             DEBUG("got offer from %R ", bp->siaddr);
> +             if (conf.state != Sselecting) {
> +                     DEBUG("");
> +                     break;
> +             }
> +             lease = optgetulong(bp->optdata, ODlease);
> +             if (lease == 0) {
> +                     /*
> +                      * The All_Aboard NAT package from Internet Share
> +                      * doesn't give a lease time, so we have to assume one.
> +                      */
> +                     warning("Offer with %lud lease, using %d", lease, 
> MinLease);
> +                     lease = MinLease;
> +             }
> +             DEBUG("lease=%lud ", lease);
> +             if (!optgetaddr(bp->optdata, ODserverid, conf.server)) {
> +                     warning("Offer from server with invalid serverid");
> +                     break;
> +             }
> +
> +             v4tov6(conf.laddr, bp->yiaddr);
> +             memmove(conf.sname, bp->sname, sizeof(conf).sname);
> +             conf.sname[sizeof(conf).sname - 1] = 0;
> +             DEBUG("server=%R sname=%s", conf.server, conf.sname);
> +             conf.offered = lease;
> +             conf.state = Srequesting;
> +             dhcpsend(Request);
> +             conf.resend = 0;
> +             conf.timeout = time(0) + 4;
> +             break;
> +     case Ack:
> +             DEBUG("got ack from %R ", bp->siaddr);
> +             if (conf.state != Srequesting && conf.state != Srenewing &&
> +                 conf.state != Srebinding)
> +                     break;
> +
> +             /* ignore a bad lease */
> +             lease = optgetulong(bp->optdata, ODlease);
> +             if (lease == 0) {
> +                     /*
> +                      * The All_Aboard NAT package from Internet Share
> +                      * doesn't give a lease time, so we have to assume one.
> +                      */
> +                     warning("Ack with %lud lease, using %d", lease, 
> MinLease);
> +                     lease = MinLease;
> +             }
> +             DEBUG("lease=%lud ", lease);
> +
> +             /* address and mask */
> +             if (!validip(conf.laddr) || !Oflag)
> +                     v4tov6(conf.laddr, bp->yiaddr);
> +             if (!validip(conf.mask) || !Oflag) {
> +                     if (!optgetaddr(bp->optdata, OBmask, conf.mask))
> +                             ipmove(conf.mask, IPnoaddr);
> +             }
> +             DEBUG("ipaddr=%R ipmask=%M ", conf.laddr, conf.mask);
> +
> +             /*
> +              * get a router address either from the router option
> +              * or from the router that forwarded the dhcp packet
> +              */
> +             if (validip(conf.gaddr) && Oflag) {
> +                     DEBUG("ipgw=%R ", conf.gaddr);
> +             } else if (optgetaddr(bp->optdata, OBrouter, conf.gaddr)) {
> +                     DEBUG("ipgw=%R ", conf.gaddr);
> +             } else if (memcmp(bp->giaddr, IPnoaddr + IPv4off, IPv4addrlen) 
> != 0) {
> +                     v4tov6(conf.gaddr, bp->giaddr);
> +                     DEBUG("giaddr=%R ", conf.gaddr);
> +             }
> +
> +             /* get dns servers */
> +             memset(conf.dns, 0, sizeof(conf).dns);
> +             n = optgetaddrs(bp->optdata, OBdnserver, conf.dns,
> +                             sizeof(conf).dns / IPaddrlen);
> +             for (i = 0; i < n; i++)
> +                     DEBUG("dns=%R ", conf.dns + i * IPaddrlen);
> +
> +             /* get ntp servers */
> +             memset(conf.ntp, 0, sizeof(conf).ntp);
> +             n = optgetaddrs(bp->optdata, OBntpserver, conf.ntp,
> +                             sizeof(conf).ntp / IPaddrlen);
> +             for (i = 0; i < n; i++)
> +                     DEBUG("ntp=%R ", conf.ntp + i * IPaddrlen);
> +
> +             /* get names */
> +             optgetstr(bp->optdata, OBhostname,
> +                       conf.hostname, sizeof(conf).hostname);
> +             optgetstr(bp->optdata, OBdomainname,
> +                       conf.domainname, sizeof(conf).domainname);
> +
> +             /* get anything else we asked for */
> +             getoptions(bp->optdata);
> +
> +             /* get plan9-specific options */
> +             n = optgetvec(bp->optdata, OBvendorinfo, vopts, sizeof(vopts) - 
> 1);
> +             if (n > 0 && parseoptions(vopts, n) == 0) {
> +                     if (validip(conf.fs) && Oflag)
> +                             n = 1;
> +                     else {
> +                             n = optgetp9addrs(vopts, OP9fs, conf.fs, 2);
> +                             if (n == 0)
> +                                     n = optgetaddrs(vopts, OP9fsv4, 
> conf.fs, 2);
> +                     }
> +                     for (i = 0; i < n; i++)
> +                             DEBUG("fs=%R ", conf.fs + i * IPaddrlen);
> +
> +                     if (validip(conf.auth) && Oflag)
> +                             n = 1;
> +                     else {
> +                             n = optgetp9addrs(vopts, OP9auth, conf.auth, 2);
> +                             if (n == 0)
> +                                     n = optgetaddrs(vopts, OP9authv4, 
> conf.auth, 2);
> +                     }
> +                     for (i = 0; i < n; i++)
> +                             DEBUG("auth=%R ", conf.auth + i * IPaddrlen);
> +
> +                     n = optgetp9addrs(vopts, OP9ipaddr, taddr, 1);
> +                     if (n > 0)
> +                             memmove(conf.laddr, taddr, IPaddrlen);
> +                     n = optgetp9addrs(vopts, OP9ipmask, taddr, 1);
> +                     if (n > 0)
> +                             memmove(conf.mask, taddr, IPaddrlen);
> +                     n = optgetp9addrs(vopts, OP9ipgw, taddr, 1);
> +                     if (n > 0)
> +                             memmove(conf.gaddr, taddr, IPaddrlen);
> +                     DEBUG("new ipaddr=%R new ipmask=%M new ipgw=%R",
> +                           conf.laddr, conf.mask, conf.gaddr);
> +             }
> +             conf.lease = lease;
> +             conf.state = Sbound;
> +             DEBUG("server=%R sname=%s", conf.server, conf.sname);
> +             break;
> +     case Nak:
> +             conf.state = Sinit;
> +             warning("recved dhcpnak on %s", conf.mpoint);
> +             break;
> +     }
> +}
> +
> +/* return pseudo-random integer in range low...(hi-1) */
> +uint32_t randint(uint32_t low, uint32_t hi)
> +{
> +     if (hi < low)
> +             return low;
> +     return low + (lrand48() % hi);
> +}
> +
> +// compute small pseudo-random delay in ms
> +long jitter(void)
> +{
> +     return randint(0, 10 * 1000);
> +}
> +
> +int openlisten(void)
> +{
> +     int n, fd, cfd;
> +     char data[128], devdir[40];
> +
> +     if (validip(conf.laddr) &&
> +         (conf.state == Srenewing || conf.state == Srebinding))
> +             sprintf(data, "%s/udp!%R!68", conf.mpoint, conf.laddr);
> +     else
> +             sprintf(data, "%s/udp!*!68", conf.mpoint);
> +     for (n = 0; (cfd = announce9(data, devdir, 0)) < 0; n++) {
> +             if (!noconfig) {
> +                     fprintf(stderr, "can't announce for dhcp: %r");
> +                     exit(-1);
> +             }
> +
> +             /* might be another client - wait and try again */
> +             warning("can't announce %s: %r", data);
> +             usleep(jitter() * 1000);
> +             if (n > 10)
> +                     return -1;
> +     }
> +
> +     if (write(cfd, "headers", strlen("headers")) < 0) {
> +             fprintf(stderr, "can't set header mode: %r");
> +             exit(-1);
> +     }
> +
> +     sprintf(data, "%s/data", devdir);
> +     fd = open(data, O_RDWR);
> +     if (fd < 0) {
> +             fprintf(stderr, "open %s: %r", data);
> +             exit(-1);
> +     }
> +     close(cfd);
> +     return fd;
> +}
> +
> +uint8_t *optadd(uint8_t *p, int op, void *d, int n)
> +{
> +     p[0] = op;
> +     p[1] = n;
> +     memmove(p + 2, d, n);
> +     return p + n + 2;
> +}
> +
> +uint8_t *optaddbyte(uint8_t *p, int op, int b)
> +{
> +     p[0] = op;
> +     p[1] = 1;
> +     p[2] = b;
> +     return p + 3;
> +}
> +
> +uint8_t *optaddulong(uint8_t *p, int op, uint32_t x)
> +{
> +     p[0] = op;
> +     p[1] = 4;
> +     hnputl(p + 2, x);
> +     return p + 6;
> +}
> +
> +uint8_t *optaddaddr(uint8_t *p, int op, uint8_t *ip)
> +{
> +     p[0] = op;
> +     p[1] = 4;
> +     v6tov4(p + 2, ip);
> +     return p + 6;
> +}
> +
> +/* add dhcp option op with value v of length n to dhcp option array p */
> +uint8_t *optaddvec(uint8_t *p, int op, uint8_t *v, int n)
> +{
> +     p[0] = op;
> +     p[1] = n;
> +     memmove(p + 2, v, n);
> +     return p + 2 + n;
> +}
> +
> +uint8_t *optaddstr(uint8_t *p, int op, char *v)
> +{
> +     int n;
> +
> +     n = strlen(v) + 1; /* microsoft leaves on the NUL, so we do too */
> +     p[0] = op;
> +     p[1] = n;
> +     memmove(p + 2, v, n);
> +     return p + 2 + n;
> +}
> +
> +/*
> + * parse p, looking for option `op'.  if non-nil, np points to minimum 
> length.
> + * return NULL if option is too small, else ptr to opt, and
> + * store actual length via np if non-nil.
> + */
> +uint8_t *optget(uint8_t *p, int op, int *np)
> +{
> +     int len, code;
> +
> +     while ((code = *p++) != OBend) {
> +             if (code == OBpad)
> +                     continue;
> +             len = *p++;
> +             if (code != op) {
> +                     p += len;
> +                     continue;
> +             }
> +             if (np != NULL) {
> +                     if (*np > len)
> +                             return 0;
> +                     *np = len;
> +             }
> +             return p;
> +     }
> +     return 0;
> +}
> +
> +int optgetbyte(uint8_t *p, int op)
> +{
> +     int len;
> +
> +     len = 1;
> +     p = optget(p, op, &len);
> +     if (p == NULL)
> +             return 0;
> +     return *p;
> +}
> +
> +uint32_t optgetulong(uint8_t *p, int op)
> +{
> +     int len;
> +
> +     len = 4;
> +     p = optget(p, op, &len);
> +     if (p == NULL)
> +             return 0;
> +     return nhgetl(p);
> +}
> +
> +int optgetaddr(uint8_t *p, int op, uint8_t *ip)
> +{
> +     int len;
> +
> +     len = 4;
> +     p = optget(p, op, &len);
> +     if (p == NULL)
> +             return 0;
> +     v4tov6(ip, p);
> +     return 1;
> +}
> +
> +/* expect at most n addresses; ip[] only has room for that many */
> +int optgetaddrs(uint8_t *p, int op, uint8_t *ip, int n)
> +{
> +     int len, i;
> +
> +     len = 4;
> +     p = optget(p, op, &len);
> +     if (p == NULL)
> +             return 0;
> +     len /= IPv4addrlen;
> +     if (len > n)
> +             len = n;
> +     for (i = 0; i < len; i++)
> +             v4tov6(&ip[i * IPaddrlen], &p[i * IPv4addrlen]);
> +     return i;
> +}
> +
> +/* expect at most n addresses; ip[] only has room for that many */
> +int optgetp9addrs(uint8_t *ap, int op, uint8_t *ip, int n)
> +{
> +     int len, i, slen, addrs;
> +     char *p;
> +
> +     len = 1; /* minimum bytes needed */
> +     p = (char *)optget(ap, op, &len);
> +     if (p == NULL)
> +             return 0;
> +     addrs = *p++; /* first byte is address count */
> +     for (i = 0; i < n && i < addrs && len > 0; i++) {
> +             slen = strlen(p) + 1;
> +             if (parseip(&ip[i * IPaddrlen], p) == -1)
> +                     fprintf(stderr, "%s: bad address %s\n", argv0, p);
> +             DEBUG("got plan 9 option %d addr %R (%s)", op, &ip[i * 
> IPaddrlen], p);
> +             p += slen;
> +             len -= slen;
> +     }
> +     return addrs;
> +}
> +
> +int optgetvec(uint8_t *p, int op, uint8_t *v, int n)
> +{
> +     int len;
> +
> +     len = 1;
> +     p = optget(p, op, &len);
> +     if (p == NULL)
> +             return 0;
> +     if (len > n)
> +             len = n;
> +     memmove(v, p, len);
> +     return len;
> +}
> +
> +int optgetstr(uint8_t *p, int op, char *s, int n)
> +{
> +     int len;
> +
> +     len = 1;
> +     p = optget(p, op, &len);
> +     if (p == NULL)
> +             return 0;
> +     if (len >= n)
> +             len = n - 1;
> +     memmove(s, p, len);
> +     s[len] = 0;
> +     return len;
> +}
> +
> +/*
> + * sanity check options area
> + *   - options don't overflow packet
> + *   - options end with an OBend
> + */
> +int parseoptions(uint8_t *p, int n)
> +{
> +     int code, len, nin = n;
> +
> +     while (n > 0) {
> +             code = *p++;
> +             n--;
> +             if (code == OBend)
> +                     return 0;
> +             if (code == OBpad)
> +                     continue;
> +             if (n == 0) {
> +                     warning(
> +                         "parseoptions: bad option: 0x%x: truncated: opt 
> length = %d",
> +                         code, nin);
> +                     return -1;
> +             }
> +
> +             len = *p++;
> +             n--;
> +             DEBUG("parseoptions: %s(%d) len %d, bytes left %d",
> +                   option[code].name, code, len, n);
> +             if (len > n) {
> +                     warning(
> +                         "parseoptions: bad option: 0x%x: %d > %d: opt 
> length = %d",
> +                         code, len, n, nin);
> +                     return -1;
> +             }
> +             p += len;
> +             n -= len;
> +     }
> +
> +     /* make sure packet ends with an OBend after all the optget code */
> +     *p = OBend;
> +     return 0;
> +}
> +
> +/*
> + * sanity check received packet:
> + *   - magic is dhcp magic
> + *   - options don't overflow packet
> + */
> +struct bootp *parsebootp(uint8_t *p, int n)
> +{
> +     struct bootp *bp;
> +
> +     bp = (struct bootp *)p;
> +     if (n < bp->optmagic - p) {
> +             warning(
> +                 "parsebootp: short bootp packet; with options, need %d 
> bytes, got %d",
> +                 bp->optmagic - p, n);
> +             return NULL;
> +     }
> +
> +     if (conf.xid != nhgetl(bp->xid)) /* not meant for us */
> +             return NULL;
> +
> +     if (bp->op != Bootreply) {
> +             warning("parsebootp: bad op %d", bp->op);
> +             return NULL;
> +     }
> +
> +     n -= bp->optmagic - p;
> +     p = bp->optmagic;
> +
> +     if (n < 4) {
> +             warning("parsebootp: no option data");
> +             return NULL;
> +     }
> +     if (memcmp(optmagic, p, 4) != 0) {
> +             warning("parsebootp: bad opt magic %x %x %x %x",
> +                     p[0], p[1], p[2], p[3]);
> +             return NULL;
> +     }
> +     p += 4;
> +     n -= 4;
> +     DEBUG("parsebootp: new packet");
> +     if (parseoptions(p, n) < 0)
> +             return NULL;
> +     return bp;
> +}
> +
> +/* write out an ndb entry */
> +void writendb(char *s, int n, int append)
> +{
> +     char file[64];
> +     int fd;
> +
> +     snprintf(file, sizeof(file), "%s/ndb", conf.mpoint);
> +     if (append) {
> +             fd = open(file, O_WRITE);
> +             lseek(fd, 0, 2);
> +     } else
> +             fd = open(file, O_WRITE | O_TRUNC);
> +     write(fd, s, n);
> +     close(fd);
> +}
> +
> +/* put server addresses into the ndb entry */
> +size_t putaddrs(char *buf, size_t size, char *attr, uint8_t *a, int len)
> +{
> +     int i;
> +     size_t n;
> +     char *p;
> +
> +     n = 0;
> +     p = "";
> +     for (i = 0; i < len && validip(a); i += IPaddrlen, a += IPaddrlen) {
> +             n += snprintf(buf + n, size - n, "%s%s=%R\n", p, attr, a);
> +             p = " ";
> +     }
> +
> +     return n;
> +}
> +
> +/* make an ndb entry and put it into /net/ndb for the servers to see */
> +void putndb(void)
> +{
> +     int append;
> +     char buf[1024];
> +     char *np;
> +     size_t n;
> +
> +     buf[0] = '\0';
> +     n = 0;
> +     if (getndb() == 0)
> +             append = 1;
> +     else {
> +             append = 0;
> +             n += snprintf(buf + n, sizeof(buf) - n, "ip=%R ipmask=%M 
> ipgw=%R\n",
> +                           conf.laddr, conf.mask, conf.gaddr);
> +     }
> +     np = strchr(conf.hostname, '.');
> +     if (np != NULL) {
> +             if (*conf.domainname == 0)
> +                     snprintf(conf.domainname, sizeof(conf).domainname, 
> "%s", np + 1);
> +             *np = 0;
> +     }
> +     if (*conf.hostname)
> +             n += snprintf(buf + n, sizeof(buf) - n, "\tsys=%s\n", 
> conf.hostname);
> +     if (*conf.domainname)
> +             n += snprintf(buf + n, sizeof(buf) - n, "\tdom=%s.%s\n",
> +                           conf.hostname, conf.domainname);
> +     if (validip(conf.fs))
> +             n += putaddrs(buf + n, sizeof(buf) - n, "\tfs",
> +                           conf.fs, sizeof(conf).fs);
> +     if (validip(conf.auth))
> +             n += putaddrs(buf + n, sizeof(buf) - n, "\tauth",
> +                           conf.auth, sizeof(conf).auth);
> +     if (validip(conf.dns))
> +             n += putaddrs(buf + n, sizeof(buf) - n, "\tdns",
> +                           conf.dns, sizeof(conf).dns);
> +     if (validip(conf.ntp))
> +             n += putaddrs(buf + n, sizeof(buf) - n, "\tntp",
> +                           conf.ntp, sizeof(conf).ntp);
> +     if (ndboptions)
> +             n += snprintf(buf + n, sizeof(buf) - n, "%s\n", ndboptions);
> +     if (n > 0)
> +             writendb(buf, n, append);
> +}
> +
> +/* get an ndb entry someone else wrote */
> +int getndb(void)
> +{
> +     char buf[1024];
> +     int fd, n;
> +     char *p;
> +
> +     snprintf(buf, sizeof(buf), "%s/ndb", conf.mpoint);
> +     fd = open(buf, O_RDONLY);
> +     n = read(fd, buf, sizeof(buf) - 1);
> +     close(fd);
> +     if (n <= 0)
> +             return -1;
> +     buf[n] = 0;
> +     p = strstr(buf, "ip=");
> +     if (p == NULL)
> +             return -1;
> +     if (parseip(conf.laddr, p + 3) == -1)
> +             fprintf(stderr, "%s: bad address %s\n", argv0, p + 3);
> +     return 0;
> +}
> +
> +/* tell a server to refresh */
> +void tweakserver(char *server)
> +{
> +     int fd;
> +     char file[64];
> +
> +     snprintf(file, sizeof(file), "%s/%s", conf.mpoint, server);
> +     fd = open(file, O_RDWR);
> +     if (fd < 0)
> +             return;
> +     write(fd, "refresh", strlen("refresh"));
> +     close(fd);
> +}
> +
> +/* tell all servers to refresh their information */
> +void tweakservers(void)
> +{
> +     tweakserver("dns");
> +     tweakserver("cs");
> +}
> +
> +/* return number of networks */
> +int nipifcs(char *net)
> +{
> +     int n;
> +     struct ipifc *nifc;
> +     struct iplifc *lifc;
> +
> +     n = 0;
> +     ifc = readipifc(net, ifc, -1);
> +     for (nifc = ifc; nifc != NULL; nifc = nifc->next) {
> +             /*
> +              * ignore loopback devices when trying to
> +              * figure out if we're the primary interface.
> +              */
> +             if (strcmp(nifc->dev, "/dev/null") != 0)
> +                     for (lifc = nifc->lifc; lifc != NULL; lifc = lifc->next)
> +                             if (validip(lifc->ip)) {
> +                                     n++;
> +                                     break;
> +                             }
> +             if (strcmp(nifc->dev, conf.dev) == 0)
> +                     myifc = nifc->index;
> +     }
> +     return n;
> +}
> +
> +/* return true if this is a valid v4 address */
> +int validip(uint8_t *addr)
> +{
> +     return ipcmp(addr, IPnoaddr) != 0 && ipcmp(addr, v4prefix) != 0;
> +}
> +
> +/* look for an action */
> +int parseverb(char *name)
> +{
> +     int i;
> +
> +     for (i = 0; i < COUNT_OF(verbs); i++)
> +             if (verbs[i] != NULL && strcmp(name, verbs[i]) == 0)
> +                     return i;
> +     return -1;
> +}
> +
> +/* get everything out of ndb */
> +void ndbconfig(void)
> +{
> +     int nattr, nauth = 0, ndns = 0, nfs = 0, ok;
> +     char etheraddr[32];
> +     char *attrs[10];
> +     struct ndb *db;
> +     struct ndbtuple *t, *nt;
> +
> +     db = ndbopen(0);
> +     if (db == NULL) {
> +             fprintf(stderr, "can't open ndb: %r");
> +             exit(-1);
> +     }
> +     if ((strcmp(conf.type, "ether") != 0 && strcmp(conf.type, "gbe") != 0) 
> ||
> +         myetheraddr(conf.hwa, conf.dev) != 0) {
> +             fprintf(stderr, "can't read hardware address");
> +             exit(-1);
> +     }
> +     snprintf(etheraddr, sizeof(etheraddr), "%E", conf.hwa);
> +     nattr = 0;
> +     attrs[nattr++] = "ip";
> +     attrs[nattr++] = "ipmask";
> +     attrs[nattr++] = "ipgw";
> +     /* the @ triggers resolution to an IP address; see ndb(2) */
> +     attrs[nattr++] = "@dns";
> +     attrs[nattr++] = "@ntp";
> +     attrs[nattr++] = "@fs";
> +     attrs[nattr++] = "@auth";
> +     attrs[nattr] = NULL;
> +     t = ndbipinfo(db, "ether", etheraddr, attrs, nattr);
> +     for (nt = t; nt != NULL; nt = nt->entry) {
> +             ok = 1;
> +             if (strcmp(nt->attr, "ip") == 0)
> +                     ok = parseip(conf.laddr, nt->val);
> +             else if (strcmp(nt->attr, "ipmask") == 0)
> +                     parseipmask(conf.mask, nt->val); /* could be -1 */
> +             else if (strcmp(nt->attr, "ipgw") == 0)
> +                     ok = parseip(conf.gaddr, nt->val);
> +             else if (ndns < 2 && strcmp(nt->attr, "dns") == 0)
> +                     ok = parseip(conf.dns + IPaddrlen * ndns, nt->val);
> +             else if (strcmp(nt->attr, "ntp") == 0)
> +                     ok = parseip(conf.ntp, nt->val);
> +             else if (nfs < 2 && strcmp(nt->attr, "fs") == 0)
> +                     ok = parseip(conf.fs + IPaddrlen * nfs, nt->val);
> +             else if (nauth < 2 && strcmp(nt->attr, "auth") == 0)
> +                     ok = parseip(conf.auth + IPaddrlen * nauth, nt->val);
> +             if (!ok)
> +                     fprintf(stderr, "%s: bad %s address in ndb: %s\n",
> +                             argv0, nt->attr, nt->val);
> +     }
> +     ndbfree(t);
> +     if (!validip(conf.laddr)) {
> +             fprintf(stderr, "address not found in ndb");
> +             exit(-1);
> +     }
> +}
> +
> +int addoption(char *opt)
> +{
> +     int i;
> +     struct option *o;
> +
> +     if (opt == NULL)
> +             return -1;
> +     for (o = option; o < &option[COUNT_OF(option)]; o++)
> +             if (o->name && strcmp(opt, o->name) == 0) {
> +                     i = o - option;
> +                     if (memchr(requested, i, nrequested) == 0 &&
> +                         nrequested < COUNT_OF(requested))
> +                             requested[nrequested++] = i;
> +                     return 0;
> +             }
> +     return -1;
> +}
> +
> +char *optgetx(uint8_t *p, uint8_t opt)
> +{
> +     int i, n;
> +     uint32_t x;
> +     char str[256], buf[1024];
> +     uint8_t ip[IPaddrlen], ips[16 * IPaddrlen], vec[256];
> +     size_t l;
> +     struct option *o;
> +
> +     o = &option[opt];
> +     if (o->name == NULL)
> +             return NULL;
> +
> +     memset(buf, '\0', sizeof(buf));
> +     switch (o->type) {
> +     case Taddr:
> +             if (optgetaddr(p, opt, ip))
> +                     snprintf(buf, sizeof(buf), "%s=%R", o->name, ip);
> +             break;
> +     case Taddrs:
> +             n = optgetaddrs(p, opt, ips, 16);
> +             if (n > 0)
> +                     l = snprintf(buf, sizeof(buf), "%s=%R", o->name, ips);
> +             for (i = 1; i < n; i++) {
> +                     l += snprintf(buf + l, sizeof(buf) - l, " %s=%R",
> +                                   o->name, &ips[i * IPaddrlen]);
> +             }
> +             break;
> +     case Tulong:
> +             x = optgetulong(p, opt);
> +             if (x != 0)
> +                     snprintf(buf, sizeof(buf), "%s=%lud", o->name, x);
> +             break;
> +     case Tbyte:
> +             x = optgetbyte(p, opt);
> +             if (x != 0)
> +                     snprintf(buf, sizeof(buf), "%s=%lud", o->name, x);
> +             break;
> +     case Tstr:
> +             if (optgetstr(p, opt, str, sizeof(str)))
> +                     snprintf(buf, sizeof(buf), "%s=%s", o->name, str);
> +             break;
> +     case Tvec:
> +             n = optgetvec(p, opt, vec, sizeof(vec));
> +             if (n > 0) /* what's %H?  it's not installed */
> +                     snprintf(buf, sizeof(buf), "%s=%.*H", o->name, n, vec);
> +             break;
> +     }
> +     return strdup(buf);
> +}
> +
> +void getoptions(uint8_t *p)
> +{
> +     int i;
> +     char buf[1024];
> +     char *s;
> +
> +     for (i = COUNT_OF(defrequested); i < nrequested; i++) {
> +             s = optgetx(p, requested[i]);
> +             if (s == NULL)
> +                     continue;
> +             DEBUG("%s ", s);
> +             snprintf(buf, sizeof(buf),
> +                      (ndboptions == NULL) ? "\t%s" : "\t%s%s",
> +                      s, ndboptions);
> +             free(ndboptions);
> +             ndboptions = strdup(buf);
> +             free(s);
> +     }
> +}

-- 
You received this message because you are subscribed to the Google Groups 
"Akaros" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to