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.
