morpheus-base

morpheus base system
git clone git://git.2f30.org/morpheus-base
Log | Files | Refs

sdhcp.c (11053B)


      1 #include <sys/socket.h>
      2 #include <sys/ioctl.h>
      3 #include <netinet/in.h>
      4 #include <net/if.h>
      5 #include <net/route.h>
      6 #include <signal.h>
      7 #include <poll.h>
      8 #include <errno.h>
      9 #include <fcntl.h>
     10 #include <stdio.h>
     11 #include <stdlib.h>
     12 #include <string.h>
     13 #include <time.h>
     14 #include <unistd.h>
     15 
     16 #include "util.h"
     17 #include "arg.h"
     18 
     19 typedef struct bootp {
     20 	unsigned char op      [1];
     21 	unsigned char htype   [1];
     22 	unsigned char hlen    [1];
     23 	unsigned char hops    [1];
     24 	unsigned char xid     [4];
     25 	unsigned char secs    [2];
     26 	unsigned char flags   [2];
     27 	unsigned char ciaddr  [4];
     28 	unsigned char yiaddr  [4];
     29 	unsigned char siaddr  [4];
     30 	unsigned char giaddr  [4];
     31 	unsigned char chaddr  [16];
     32 	unsigned char sname   [64];
     33 	unsigned char file    [128];
     34 	unsigned char magic   [4];
     35 	unsigned char optdata [312-4];
     36 } Bootp;
     37 
     38 enum {
     39 	DHCPdiscover =       1,
     40 	DHCPoffer,
     41 	DHCPrequest,
     42 	DHCPdecline,
     43 	DHCPack,
     44 	DHCPnak,
     45 	DHCPrelease,
     46 	DHCPinform,
     47 	Timeout =          200,
     48 
     49 	Bootrequest =        1,
     50 	Bootreply =          2,
     51 	/* bootp flags */
     52 	Fbroadcast =   1 << 15,
     53 
     54 	OBpad =              0,
     55 	OBmask =             1,
     56 	OBrouter =           3,
     57 	OBnameserver =       5,
     58 	OBdnsserver =        6,
     59 	OBbaddr =           28,
     60 	ODipaddr =          50, /* 0x32 */
     61 	ODlease =           51,
     62 	ODoverload =        52,
     63 	ODtype =            53, /* 0x35 */
     64 	ODserverid =        54, /* 0x36 */
     65 	ODparams =          55, /* 0x37 */
     66 	ODmessage =         56,
     67 	ODmaxmsg =          57,
     68 	ODrenewaltime =     58,
     69 	ODrebindingtime =   59,
     70 	ODvendorclass =     60,
     71 	ODclientid =        61, /* 0x3d */
     72 	ODtftpserver =      66,
     73 	ODbootfile =        67,
     74 	OBend =            255,
     75 };
     76 
     77 enum { Broadcast, Unicast};
     78 
     79 Bootp bp;
     80 unsigned char magic[] = {99, 130, 83, 99};
     81 
     82 /* conf */
     83 static unsigned char xid[sizeof bp.xid];
     84 static unsigned char hwaddr[16];
     85 static time_t starttime;
     86 static char *ifname = "eth0";
     87 static char *cid = "";
     88 static char *program = "";
     89 static int sock;
     90 /* sav */
     91 static unsigned char server[4];
     92 static unsigned char client[4];
     93 static unsigned char mask[4];
     94 static unsigned char router[4];
     95 static unsigned char dns[4];
     96 static unsigned long t1;
     97 
     98 static int dflag = 1; /* change DNS in /etc/resolv.conf ? */
     99 static int iflag = 1; /* set IP ? */
    100 
    101 #define IP(a,b,c,d) (unsigned char[4]){a,b,c,d}
    102 
    103 static void
    104 hnput(unsigned char *dst, unsigned long long src, size_t n)
    105 {
    106 	unsigned int i;
    107 
    108 	for(i = 0; n--; i++)
    109 		dst[i] = (src >> (n * 8)) & 0xff;
    110 }
    111 
    112 static struct sockaddr *
    113 iptoaddr(struct sockaddr *ifaddr, unsigned char ip[4], int port)
    114 {
    115 	struct sockaddr_in *in = (struct sockaddr_in *)ifaddr;
    116 
    117 	in->sin_family = AF_INET;
    118 	in->sin_port = htons(port);
    119 	memcpy(&(in->sin_addr), ip, sizeof in->sin_addr);
    120 	return ifaddr;
    121 }
    122 
    123 /* sendto UDP wrapper */
    124 static ssize_t
    125 udpsend(unsigned char ip[4], int fd, void *data, size_t n)
    126 {
    127 	struct sockaddr addr;
    128 	socklen_t addrlen = sizeof addr;
    129 	ssize_t sent;
    130 
    131 	iptoaddr(&addr, ip, 67); /* bootp server */
    132 	if((sent = sendto(fd, data, n, 0, &addr, addrlen)) == -1)
    133 		eprintf("sendto:");
    134 	return sent;
    135 }
    136 
    137 /* recvfrom UDP wrapper */
    138 static ssize_t
    139 udprecv(unsigned char ip[4], int fd, void *data, size_t n)
    140 {
    141 	struct sockaddr addr;
    142 	socklen_t addrlen = sizeof addr;
    143 	ssize_t r;
    144 
    145 	iptoaddr(&addr, ip, 68); /* bootp client */
    146 	if((r = recvfrom(fd, data, n, 0, &addr, &addrlen)) == -1)
    147 		eprintf("recvfrom:");
    148 	return r;
    149 }
    150 
    151 static void
    152 setip(unsigned char ip[4], unsigned char mask[4], unsigned char gateway[4])
    153 {
    154 	struct ifreq ifreq;
    155 	struct rtentry rtreq;
    156 	int fd;
    157 
    158 	memset(&ifreq, 0, sizeof(ifreq));
    159 	memset(&rtreq, 0, sizeof(rtreq));
    160 
    161 	strlcpy(ifreq.ifr_name, ifname, IF_NAMESIZE);
    162 	iptoaddr(&(ifreq.ifr_addr), ip, 0);
    163 	if((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP)) == -1)
    164 		eprintf("can't set ip, socket:");
    165 	ioctl(fd, SIOCSIFADDR, &ifreq);
    166 	iptoaddr(&(ifreq.ifr_netmask), mask, 0);
    167 	ioctl(fd, SIOCSIFNETMASK, &ifreq);
    168 	ifreq.ifr_flags = IFF_UP | IFF_RUNNING | IFF_BROADCAST | IFF_MULTICAST;
    169 	ioctl(fd, SIOCSIFFLAGS, &ifreq);
    170 	/* gw */
    171 	rtreq.rt_flags = (RTF_UP | RTF_GATEWAY);
    172 	iptoaddr(&(rtreq.rt_gateway), gateway, 0);
    173 	iptoaddr(&(rtreq.rt_genmask), IP(0, 0, 0, 0), 0);
    174 	iptoaddr(&(rtreq.rt_dst), IP(0, 0, 0, 0), 0);
    175 	ioctl(fd, SIOCADDRT, &rtreq);
    176 
    177 	close(fd);
    178 }
    179 
    180 static void
    181 cat(int dfd, char *src)
    182 {
    183 	char buf[BUFSIZ];
    184 	int n, fd;
    185 
    186 	if((fd = open(src, O_RDONLY)) == -1)
    187 		return; /* can't read, but don't error out */
    188 	while((n = read(fd, buf, sizeof buf)) > 0)
    189 		write(dfd, buf, n);
    190 	close(fd);
    191 }
    192 
    193 static void
    194 setdns(unsigned char dns[4])
    195 {
    196 	char buf[128];
    197 	int fd;
    198 
    199 	if((fd = creat("/etc/resolv.conf", 0644)) == -1) {
    200 		weprintf("can't change /etc/resolv.conf:");
    201 		return;
    202 	}
    203 	cat(fd, "/etc/resolv.conf.head");
    204 	if(snprintf(buf, sizeof(buf) - 1, "\nnameserver %d.%d.%d.%d\n",
    205 	         dns[0], dns[1], dns[2], dns[3]) > 0)
    206 		write(fd, buf, strlen(buf));
    207 	cat(fd, "/etc/resolv.conf.tail");
    208 	close(fd);
    209 }
    210 
    211 static void
    212 optget(Bootp *bp, void *data, int opt, int n)
    213 {
    214 	unsigned char *p = bp->optdata;
    215 	unsigned char *top = ((unsigned char *)bp) + sizeof *bp;
    216 	int code, len;
    217 
    218 	while(p < top) {
    219 		code = *p++;
    220 		if(code == OBpad)
    221 			continue;
    222 		if(code == OBend || p == top)
    223 			break;
    224 		len = *p++;
    225 		if(len > top - p)
    226 			break;
    227 		if(code == opt) {
    228 			memcpy(data, p, MIN(len, n));
    229 			return;
    230 		}
    231 		p += len;
    232 	}
    233 }
    234 
    235 static unsigned char *
    236 optput(unsigned char *p, int opt, unsigned char *data, size_t len)
    237 {
    238 	*p++ = opt;
    239 	*p++ = (unsigned char)len;
    240 	memcpy(p, data, len);
    241 	return p + len;
    242 }
    243 
    244 static unsigned char *
    245 hnoptput(unsigned char *p, int opt, long long data, size_t len)
    246 {
    247 	*p++ = opt;
    248 	*p++ = (unsigned char)len;
    249 	hnput(p, data, len);
    250 	return p + len;
    251 }
    252 
    253 static void
    254 dhcpsend(int type, int how)
    255 {
    256 	unsigned char *ip, *p;
    257 
    258 	memset(&bp, 0, sizeof bp);
    259 	hnput(bp.op, Bootrequest, 1);
    260 	hnput(bp.htype, 1, 1);
    261 	hnput(bp.hlen, 6, 1);
    262 	memcpy(bp.xid, xid, sizeof xid);
    263 	hnput(bp.flags, Fbroadcast, sizeof bp.flags);
    264 	hnput(bp.secs, time(NULL) - starttime, sizeof bp.secs);
    265 	memcpy(bp.magic, magic, sizeof bp.magic);
    266 	memcpy(bp.chaddr, hwaddr, sizeof bp.chaddr);
    267 	p = bp.optdata;
    268 	p = hnoptput(p, ODtype, type, 1);
    269 	p = optput(p, ODclientid, (unsigned char*)cid, strlen(cid));
    270 
    271 	switch(type) {
    272 	case DHCPdiscover:
    273 		break;
    274 	case DHCPrequest:
    275 		/* memcpy(bp.ciaddr, client, sizeof bp.ciaddr); */
    276 		p = hnoptput(p, ODlease, t1, sizeof t1);
    277 		p = optput(p, ODipaddr, client, sizeof client);
    278 		p = optput(p, ODserverid, server, sizeof server);
    279 		break;
    280 	case DHCPrelease:
    281 		memcpy(bp.ciaddr, client, sizeof client);
    282 		p = optput(p, ODipaddr, client, sizeof client);
    283 		p = optput(p, ODserverid, server, sizeof server);
    284 		break;
    285 	}
    286 	*p++ = OBend;
    287 
    288 	ip = (how == Broadcast) ? IP(255, 255, 255, 255) : server;
    289 	udpsend(ip, sock, &bp, p - (unsigned char *)&bp);
    290 }
    291 
    292 static int
    293 dhcprecv(void)
    294 {
    295 	unsigned char type;
    296 	struct pollfd pfd;
    297 
    298 	memset(&pfd, 0, sizeof(pfd));
    299 	pfd.fd = sock;
    300 	pfd.events = POLLIN;
    301 
    302 	memset(&bp, 0, sizeof bp);
    303 	if(poll(&pfd, 1, -1) == -1) {
    304 		if(errno != EINTR)
    305 			eprintf("poll:");
    306 		else
    307 			return Timeout;
    308 	}
    309 	udprecv(IP(255, 255, 255, 255), sock, &bp, sizeof bp);
    310 	optget(&bp, &type, ODtype, sizeof type);
    311 	return type;
    312 }
    313 
    314 static void
    315 acceptlease(void)
    316 {
    317 	char buf[128];
    318 
    319 	if(iflag == 1)
    320 		setip(client, mask, router);
    321 	if(dflag == 1)
    322 		setdns(dns);
    323 	if(*program) {
    324 		snprintf(buf, sizeof(buf), "%d.%d.%d.%d", server[0], server[1], server[2], server[3]);
    325 		setenv("SERVER", buf, 1);
    326 		snprintf(buf, sizeof(buf), "%d.%d.%d.%d", client[0], client[1], client[2], client[3]);
    327 		setenv("CLIENT", buf, 1);
    328 		snprintf(buf, sizeof(buf), "%d.%d.%d.%d", mask[0], mask[1], mask[2], mask[3]);
    329 		setenv("MASK", buf, 1);
    330 		snprintf(buf, sizeof(buf), "%d.%d.%d.%d", router[0], router[1], router[2], router[3]);
    331 		setenv("ROUTER", buf, 1);
    332 		snprintf(buf, sizeof(buf), "%d.%d.%d.%d", dns[0], dns[1], dns[2], dns[3]);
    333 		setenv("DNS", buf, 1);
    334 		system(program);
    335 	}
    336 	alarm(t1);
    337 }
    338 
    339 static void
    340 run(void)
    341 {
    342 #if 0
    343 InitReboot:
    344 	/* send DHCPrequest to old server */
    345 	dhcpsend(DHCPrequest, Broadcast);
    346 	goto Rebooting;
    347 Rebooting:
    348 	switch (dhcprecv()) {
    349 	case DHCPnak:
    350 		goto Init;
    351 	case DHCPack:
    352 		acceptoffer();
    353 		goto Bound;
    354 	}
    355 #endif
    356 Init:
    357 	dhcpsend(DHCPdiscover, Broadcast);
    358 	alarm(1);
    359 	goto Selecting;
    360 Selecting:
    361 	switch(dhcprecv()) {
    362 	case DHCPoffer:
    363 		alarm(0);
    364 		memcpy(client, bp.yiaddr, sizeof client);
    365 		optget(&bp, server, ODserverid, sizeof server);
    366 		optget(&bp, mask, OBmask, sizeof mask);
    367 		optget(&bp, router, OBrouter, sizeof router);
    368 		optget(&bp, dns, OBdnsserver, sizeof dns);
    369 		optget(&bp, &t1, ODlease, sizeof t1);
    370 		t1 = ntohl(t1);
    371 		dhcpsend(DHCPrequest, Broadcast);
    372 		goto Requesting;
    373 	case Timeout:
    374 		goto Init;
    375 	default:
    376 		goto Selecting;
    377 	}
    378 Requesting:
    379 	switch(dhcprecv()) {
    380 	case DHCPoffer:
    381 		goto Requesting; /* ignore other offers. */
    382 #if 0
    383 	case DHCPack: /* (and you don't want it) ? */
    384 		dhcpsend(DHCPdecline, Unicast);
    385 		goto Init;
    386 #endif
    387 	case DHCPack:
    388 		acceptlease();
    389 		goto Bound;
    390 	}
    391 Bound:
    392 	fputs("Congrats! You should be on the 'net.\n", stdout);
    393 	if(fork())
    394 		exit(EXIT_SUCCESS);
    395 	switch (dhcprecv()) {
    396 	case DHCPoffer:
    397 	case DHCPack:
    398 	case DHCPnak:
    399 		goto Bound; /* discard offer, ACK or NAK */
    400 	case Timeout:
    401 		dhcpsend(DHCPrequest, Unicast);
    402 		goto Renewing;
    403 	}
    404 Renewing:
    405 	switch(dhcprecv()) {
    406 	case DHCPack:
    407 		acceptlease();
    408 		goto Bound;
    409 	case DHCPnak:
    410 		goto Init;
    411 	case Timeout:
    412 		dhcpsend(DHCPrequest, Broadcast);
    413 		goto Rebinding;
    414 	}
    415 Rebinding:
    416 	switch(dhcprecv()) {
    417 	case DHCPnak: /* lease expired */
    418 		goto Init;
    419 	case DHCPack:
    420 		acceptlease();
    421 		goto Bound;
    422 	}
    423 }
    424 
    425 static void nop(int unused) {
    426 	(void) unused;
    427 }
    428 
    429 static void cleanexit(int unused) {
    430 	(void) unused;
    431 	dhcpsend(DHCPrelease, Unicast);
    432 	exit(EXIT_SUCCESS);
    433 }
    434 
    435 static void
    436 usage(void) {
    437 	eprintf("usage: sdhcp [-i] [-d] [-e program] [ifname] [clientid]\n");
    438 }
    439 
    440 int
    441 main(int argc, char *argv[])
    442 {
    443 	int bcast = 1;
    444 	struct ifreq ifreq;
    445 	struct sockaddr addr;
    446 	int rnd;
    447 
    448 	ARGBEGIN {
    449 	case 'e': /* run program */
    450 		program = EARGF(usage());
    451 		break;
    452 	case 'i': /* don't set ip */
    453 		iflag = 0;
    454 		break;
    455 	case 'd': /* don't update DNS in/etc/resolv.conf */
    456 		dflag = 0;
    457 		break;
    458 	default:
    459 		usage();
    460 		break;
    461 	} ARGEND;
    462 
    463 	if(argc >= 1)
    464 		ifname = argv[0]; /* interface name */
    465 	if(argc >= 2)
    466 		cid = argv[1]; /* client-id */
    467 
    468 	memset(&ifreq, 0, sizeof(ifreq));
    469 	signal(SIGALRM, nop);
    470 	signal(SIGTERM, cleanexit);
    471 
    472 	if((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    473 		eprintf("socket:");
    474 	if(setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof bcast) == -1)
    475 		eprintf("setsockopt:");
    476 
    477 	strlcpy(ifreq.ifr_name, ifname, IF_NAMESIZE);
    478 	ioctl(sock, SIOCGIFINDEX, &ifreq);
    479 	if(setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, &ifreq, sizeof ifreq) == -1)
    480 		eprintf("setsockopt:");
    481 	iptoaddr(&addr, IP(255, 255, 255, 255), 68);
    482 	if(bind(sock, (void*)&addr, sizeof addr) != 0)
    483 		eprintf("bind:");
    484 	ioctl(sock, SIOCGIFHWADDR, &ifreq);
    485 	memcpy(hwaddr, ifreq.ifr_hwaddr.sa_data, sizeof ifreq.ifr_hwaddr.sa_data);
    486 
    487 	if((rnd = open("/dev/urandom", O_RDONLY)) == -1)
    488 		eprintf("can't open /dev/urandom to generate unique transaction identifier:");
    489 	read(rnd, xid, sizeof xid);
    490 	close(rnd);
    491 
    492 	starttime = time(NULL);
    493 	run();
    494 	return EXIT_SUCCESS;
    495 }