/* Simple client for doing bootp requests. It assumes eth0 needs to be configured and uses 255.255.255.255 as the server. Only the first reply is heeded, the rest are ignored. The reply must be received within 4 seconds */ #include #include #include #include #include #include #include #include #include typedef int int32; typedef short int16; struct netInterface { int useBootp; int32 ip, netmask, broadcast; char * gateway; /* could be resolved via /etc/hosts */ int32 ns[3]; } ; struct bootp_request { char opcode; char hw; char hwlength; char hopcount; int32 id; int16 secs; int16 flags; int32 client_ip, my_ip, server_ip, gw_ip; char hwaddr[16]; char servername[64]; char bootfile[128]; char vendor[64]; } ; char * perrorstr(char * msg) { static char * err = NULL; static int errsize = 0; static int newsize; newsize = strlen(msg) + strlen(strerror(errno)) + 3; if (!errsize) { errsize = newsize; err = malloc(errsize); } else if (errsize < newsize) { free(err); errsize = newsize; err = malloc(errsize); } sprintf(err, "%s: %s", msg, strerror(errno)); return err; } char * netConfigBootp(int timeout) { struct ifreq req; struct rtentry route; int s; struct sockaddr_in * addrp = (struct sockaddr_in *) &req.ifr_addr; struct sockaddr_in addr; struct bootp_request breq; struct timeval tv; fd_set readfs; int reqid; char hwaddr[16]; int true = 1; char vendCookie[] = { 99, 130, 83, 99, 255 }; int i; strcpy(req.ifr_name, "eth0"); addrp->sin_family = AF_INET; addrp->sin_port = 0; memset(&addrp->sin_addr, 0, sizeof(addrp->sin_addr)); memset(&route, 0, sizeof(route)); memcpy(&route.rt_dst, addrp, sizeof(route.rt_dst)); s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { return perrorstr("socket"); } if (ioctl(s, SIOCSIFADDR, &req)) return perrorstr("SIOCSIFADDR"); req.ifr_flags = IFF_UP | IFF_BROADCAST | IFF_RUNNING; if (ioctl(s, SIOCSIFFLAGS, &req)) return perrorstr("SIOCSIFFLAGS"); if (ioctl(s, SIOCGIFHWADDR, &req)) return perrorstr("SIOCSIFHWADDR"); route.rt_dev = "eth0"; route.rt_flags = RTF_UP; if (ioctl(s, SIOCADDRT, &route)) return perrorstr("SIOCSADDRT"); memset(&breq, 0, sizeof(breq)); breq.opcode = 1; /* request */ breq.hw = 1; /* ethernet */ breq.hwlength = 6; breq.hopcount = 0; srandom(time(NULL)); reqid = breq.id = random(); breq.secs = 0; memcpy(breq.hwaddr, req.ifr_hwaddr.sa_data, 6); memcpy(breq.vendor, vendCookie, sizeof(vendCookie)); memset(&addr.sin_addr, 0, sizeof(&addr.sin_addr)); addr.sin_family = AF_INET; addr.sin_port = htons(68); /* bootp client */ if (bind(s, (struct sockaddr *) &addr, sizeof(addr))) { return perrorstr("bind"); } addr.sin_port = htons(67); /* bootp server */ memset(&addr.sin_addr, 0xff, sizeof(addr.sin_addr)); /* broadcast */ if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &true, 1)) { return perrorstr("setsockopt"); } if (sendto(s, &breq, sizeof(breq), 0, (struct sockaddr *) &addr, sizeof(addr)) != sizeof(breq)) return perrorstr("sendto"); memset(&addr.sin_addr, 0, sizeof(&addr.sin_addr)); addr.sin_family = AF_INET; addr.sin_port = htons(68); /* bootp client */ FD_ZERO(&readfs); FD_SET(s, &readfs); tv.tv_sec = timeout; tv.tv_usec = 0; while (1) { switch ((select(s + 1, &readfs, NULL, NULL, &tv))) { case 0: return "no reply receieved"; case 1: break; default: return perrorstr("select"); } if (recvfrom(s, &breq, sizeof(breq), 0, (struct sockaddr *) &addr, &i) != sizeof(breq)) return perrorstr("recvfrom"); /* sanity checks */ if (breq.id != reqid) continue; if (breq.opcode != 2) continue; if (memcmp(breq.hwaddr, req.ifr_hwaddr.sa_data, 6)) continue; break; } close(s); return NULL; } void main (void) { char * msg; msg = netConfigBootp(3); if (msg) puts(msg); }