diff --git a/raw.c b/raw.c new file mode 100644 index 0000000..27b7024 --- /dev/null +++ b/raw.c @@ -0,0 +1,158 @@ +#include +#include +#include +#include + +static struct pkt { + struct ether_header ethhdr; + struct ip iphdr; + struct udphdr udphdr; + struct bootp bootp; +} __attribute__((packed)) pkt; + +/* pseudo header for udp calc */ +static struct pseudohdr +{ + unsigned long source_ip; + unsigned long dest_ip; + unsigned char reserved; + unsigned char protocol; + unsigned short udp_length; + struct udphdr udphdr; + struct bootp bootp; +} __attribute__((packed)) pseudohdr; + +static unsigned char server_mac[ETHER_ADDR_LEN]; +static unsigned int ifindex; + +/* RFC 1071. */ +static uint16_t +chksum16(const void *buf, int count) +{ + int32_t sum = 0, shift; + const uint16_t *p = buf; + + while (count > 1) { + sum += *p++; + count -= 2; + } + + if (count > 0) + sum += *p; + + /* Fold 32-bit sum to 16 bits */ + if ((shift = sum >> 16)) + sum = (sum & 0xffff) + shift; + + return ~sum; +} + +static int +open_socket(void) +{ + struct ifreq ifreq; + int sock, bcast = 1; + + if ((sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) == -1) + eprintf("socket:"); + + if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast)) == -1) + eprintf("setsockopt broadcast:"); + + memset(&ifreq, 0, sizeof(ifreq)); + strlcpy(ifreq.ifr_name, ifname, IF_NAMESIZE); + + if (ioctl(sock, SIOCGIFINDEX, &ifreq)) + eprintf("SIOCGIFINDEX"); + ifindex = ifreq.ifr_ifindex; + + if (ioctl(sock, SIOCGIFHWADDR, &ifreq)) + eprintf("SIOCGIFHWADDR"); + memcpy(hwaddr, ifreq.ifr_hwaddr.sa_data, sizeof(ifreq.ifr_hwaddr.sa_data)); + + return sock; +} + +static ssize_t +udpsend(int sock, void *data, size_t n, int how) +{ + if (sock == -1) + sock = open_socket(); + + memset(&pkt, 0, sizeof(pkt)); + + if (how == Broadcast) { + memset(pkt.ethhdr.ether_dhost, 0xff, ETHER_ADDR_LEN); + pkt.iphdr.ip_dst.s_addr = INADDR_BROADCAST; + } else { + memcpy(&pkt.ethhdr.ether_dhost, server_mac, ETHER_ADDR_LEN); + memcpy(&pkt.iphdr.ip_dst, server, 4); + } + + memcpy(pkt.ethhdr.ether_shost, hwaddr, ETHER_ADDR_LEN); + pkt.ethhdr.ether_type = ntohs(ETHERTYPE_IP); + + pkt.iphdr.ip_v = 4; + pkt.iphdr.ip_hl = 5; + pkt.iphdr.ip_tos = IPTOS_LOWDELAY; + pkt.iphdr.ip_len = htons(sizeof(struct ip) + sizeof(struct udphdr) + n); + pkt.iphdr.ip_id = 0; + pkt.iphdr.ip_off = htons(0x4000); /* DF set */ + pkt.iphdr.ip_ttl = 16; + pkt.iphdr.ip_p = IPPROTO_UDP; + memcpy(&pkt.iphdr.ip_src, client, 4); + pkt.iphdr.ip_sum = chksum16(&pkt.iphdr, 20); + + pkt.udphdr.uh_sport = htons(68); + pkt.udphdr.uh_dport = htons(67); + pkt.udphdr.uh_ulen = htons(sizeof(struct udphdr) + n); + + memcpy(&pkt.bootp, data, n); + + memset(&pseudohdr, 0, sizeof(pseudohdr)); + pseudohdr.source_ip = pkt.iphdr.ip_src.s_addr; + pseudohdr.dest_ip = pkt.iphdr.ip_dst.s_addr; + pseudohdr.protocol = pkt.iphdr.ip_p; + pseudohdr.udp_length = htons(sizeof(struct udphdr) + n); + + memcpy(&pseudohdr.udphdr, &pkt.udphdr, sizeof(struct udphdr)); + memcpy(&pseudohdr.bootp, data, n); + int header_len = sizeof(pseudohdr) - sizeof(struct bootp) + n; + pkt.udphdr.uh_sum = chksum16(&pseudohdr, header_len); + + struct sockaddr_ll sa; + memset(&sa, 0, sizeof (sa)); + sa.sll_family = AF_PACKET; + sa.sll_protocol = htons(ETH_P_IP); + sa.sll_halen = ETHER_ADDR_LEN; + memcpy(sa.sll_addr, hwaddr, ETHER_ADDR_LEN); + sa.sll_ifindex = ifindex; + + size_t len = sizeof(struct ether_header) + sizeof(struct ip) + sizeof(struct udphdr) + n; + ssize_t sent; + if ((sent = sendto(sock, &pkt, len, 0, (struct sockaddr *)&sa, sizeof(sa))) == -1) + eprintf("sendto:"); + + return sent; +} + +static ssize_t +udprecv(int fd, void *data, size_t n) +{ + struct pkt recv; + int r; + + memset(&recv, 0, sizeof(recv)); + if ((r = read(fd, &recv, sizeof(recv))) == -1) + eprintf("read"); + + r -= sizeof(struct ether_header) + sizeof(struct ip) + sizeof(struct udphdr); + + // Make sure it is a dhcp packet + if (recv.udphdr.uh_sport == htons(67) && recv.udphdr.uh_dport == htons(68) && r > 0) { + memcpy(server_mac, &recv.ethhdr.ether_shost, ETHER_ADDR_LEN); + memcpy(data, &recv.bootp, r); + } else + memset(data, 0, n); + return r; +} diff --git a/sdhcp.c b/sdhcp.c index 1fcf5e6..b6d7c4e 100644 --- a/sdhcp.c +++ b/sdhcp.c @@ -1,8 +1,10 @@ +#include #include #include #include #include +#include #include #include @@ -89,13 +91,15 @@ static unsigned char magic[] = { 99, 130, 83, 99 }; /* conf */ static unsigned char xid[sizeof(bp.xid)]; -static unsigned char hwaddr[16]; -static char hostname[HOST_NAME_MAX + 1]; +static unsigned char hwaddr[ETHER_ADDR_LEN]; +static char hostname[_POSIX_HOST_NAME_MAX + 1]; static time_t starttime; static char *ifname = "eth0"; -static unsigned char cid[16]; +static unsigned char cid[24]; +static int cid_len; static char *program = ""; -static int sock, timers[3]; +static int sock = -1; +static int timers[3]; /* sav */ static unsigned char server[4]; static unsigned char client[4]; @@ -130,13 +134,65 @@ iptoaddr(struct sockaddr *ifaddr, unsigned char ip[4], int port) return ifaddr; } +#ifdef __linux__ +#define USE_RAW_SOCKET +#endif + +#ifdef USE_RAW_SOCKET +#include "raw.c" +#else +/* open a socket */ +static int +open_socket(void) +{ + struct ifreq ifreq; + struct sockaddr addr; + int sock_fd; + int bcast = 1; + + if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) + eprintf("socket:"); + + if (setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast)) == -1) + eprintf("setsockopt:"); + + memset(&ifreq, 0, sizeof(ifreq)); + strlcpy(ifreq.ifr_name, ifname, IF_NAMESIZE); + ioctl(sock_fd, SIOCGIFINDEX, &ifreq); + if (setsockopt(sock_fd, SOL_SOCKET, SO_BINDTODEVICE, &ifreq, sizeof(ifreq)) == -1) + eprintf("setsockopt:"); + iptoaddr(&addr, IP(255, 255, 255, 255), 68); + if (bind(sock_fd, (void*)&addr, sizeof(addr)) != 0) + eprintf("bind:"); + + ioctl(sock_fd, SIOCGIFHWADDR, &ifreq); + memcpy(hwaddr, ifreq.ifr_hwaddr.sa_data, sizeof(ifreq.ifr_hwaddr.sa_data)); + + /* If the interface is down... bring it up */ + if (ioctl(sock_fd, SIOCGIFFLAGS, &ifreq)) + eprintf("get flags"); + if ((ifreq.ifr_flags & IFF_UP) == 0) { + ifreq.ifr_flags |= IFF_UP; + if (ioctl(sock_fd, SIOCSIFFLAGS, &ifreq)) + eprintf("interface not up"); + } + + return sock_fd; +} + /* sendto UDP wrapper */ static ssize_t -udpsend(unsigned char ip[4], int fd, void *data, size_t n) +udpsend(int fd, void *data, size_t n, int how) { struct sockaddr addr; socklen_t addrlen = sizeof(addr); ssize_t sent; + unsigned char ip[4]; + + if (how == Broadcast) + memset(ip, 0xff, 4); + else + memcpy(ip, server, 4); iptoaddr(&addr, ip, 67); /* bootp server */ if ((sent = sendto(fd, data, n, 0, &addr, addrlen)) == -1) @@ -147,18 +203,16 @@ udpsend(unsigned char ip[4], int fd, void *data, size_t n) /* recvfrom UDP wrapper */ static ssize_t -udprecv(unsigned char ip[4], int fd, void *data, size_t n) +udprecv(int fd, void *data, size_t n) { - struct sockaddr addr; - socklen_t addrlen = sizeof(addr); ssize_t r; - iptoaddr(&addr, ip, 68); /* bootp client */ - if ((r = recvfrom(fd, data, n, 0, &addr, &addrlen)) == -1) + if ((r = recv(fd, data, n, 0)) == -1) eprintf("recvfrom:"); return r; } +#endif static void setip(unsigned char ip[4], unsigned char mask[4], unsigned char gateway[4]) @@ -267,7 +321,7 @@ hnoptput(unsigned char *p, int opt, uint32_t data, size_t len) static void dhcpsend(int type, int how) { - unsigned char *ip, *p; + unsigned char *p; memset(&bp, 0, sizeof(bp)); hnput(bp.op, Bootrequest, 1); @@ -277,10 +331,10 @@ dhcpsend(int type, int how) hnput(bp.flags, Fbroadcast, sizeof(bp.flags)); hnput(bp.secs, time(NULL) - starttime, sizeof(bp.secs)); memcpy(bp.magic, magic, sizeof(bp.magic)); - memcpy(bp.chaddr, hwaddr, sizeof(bp.chaddr)); + memcpy(bp.chaddr, hwaddr, sizeof(hwaddr)); p = bp.optdata; p = hnoptput(p, ODtype, type, 1); - p = optput(p, ODclientid, cid, sizeof(cid)); + p = optput(p, ODclientid, cid, cid_len); p = optput(p, OBhostname, (unsigned char *)hostname, strlen(hostname)); switch (type) { @@ -299,8 +353,7 @@ dhcpsend(int type, int how) } *p++ = OBend; - ip = (how == Broadcast) ? IP(255, 255, 255, 255) : server; - udpsend(ip, sock, &bp, p - (unsigned char *)&bp); + udpsend(sock, &bp, p - (unsigned char *)&bp, how); } static int @@ -319,7 +372,7 @@ dhcprecv(void) eprintf("poll:"); if (pfd[0].revents) { memset(&bp, 0, sizeof(bp)); - udprecv(IP(255, 255, 255, 255), sock, &bp, sizeof(bp)); + udprecv(sock, &bp, sizeof(bp)); optget(&bp, &type, ODtype, sizeof(type)); return type; } @@ -375,14 +428,6 @@ calctimeout(int n, struct itimerspec *ts) { if (timerfd_gettime(timers[n], ts) < 0) eprintf("timerfd_gettime:"); - ts->it_value.tv_nsec /= 2; - if (ts->it_value.tv_sec % 2) - ts->it_value.tv_nsec += 500000000; - ts->it_value.tv_sec /= 2; - if (ts->it_value.tv_sec < 60) { - ts->it_value.tv_sec = 60; - ts->it_value.tv_nsec = 0; - } } static void @@ -393,6 +438,7 @@ run(void) uint32_t renewaltime, rebindingtime, lease; Init: + memset(client, 0, sizeof(client)); dhcpsend(DHCPdiscover, Broadcast); timeout.it_value.tv_sec = 1; timeout.it_value.tv_nsec = 0; @@ -431,6 +477,11 @@ Requesting: /* no response from DHCPREQUEST after several attempts, go to INIT */ goto Init; Bound: +#ifdef USE_RAW_SOCKET + close(sock); + sock = -1; +#endif + optget(&bp, mask, OBmask, sizeof(mask)); optget(&bp, router, OBrouter, sizeof(router)); optget(&bp, dns, OBdnsserver, sizeof(dns)); @@ -441,12 +492,12 @@ Bound: rebindingtime = ntohl(rebindingtime); lease = ntohl(lease); acceptlease(); - fputs("Congrats! You should be on the 'net.\n", stdout); - if (!fflag && !forked) { + if (!forked) + fputs("Congrats! You should be on the 'net.\n", stdout); + if (!fflag && !forked) if (fork()) exit(0); - forked = 1; - } + forked = 1; /* doesn't hurt to always set this */ timeout.it_value.tv_sec = renewaltime; settimeout(0, &timeout); timeout.it_value.tv_sec = rebindingtime; @@ -511,12 +562,36 @@ usage(void) eprintf("usage: %s [-d] [-e program] [-f] [-i] [ifname] [clientid]\n", argv0); } +static uint8_t fromhex(char nibble) +{ + if (nibble >= '0' && nibble <= '9') + return nibble - '0'; + else if (nibble >= 'a' && nibble <= 'f') + return nibble - 'a' + 10; + else if (nibble >= 'A' && nibble <= 'F') + return nibble - 'A' + 10; + else + eprintf("Bad nibble %c\n", nibble); + return 0; // unreachable +} + +static int str2bytes(const char *str, uint8_t *bytes, int len) +{ + int slen = strlen(str); + if ((slen & 1) || slen > (len * 2)) + printf("invalid CID"); + + while (*str) { + *bytes = (fromhex(*str++) << 4); + *bytes++ |= fromhex(*str++); + } + + return slen / 2; +} + int main(int argc, char *argv[]) { - int bcast = 1; - struct ifreq ifreq; - struct sockaddr addr; int rnd; size_t i; @@ -540,31 +615,27 @@ main(int argc, char *argv[]) if (argc) ifname = argv[0]; /* interface name */ - if (argc >= 2) - strlcpy((char *)cid, argv[1], sizeof(cid)); /* client-id */ + if (argc >= 2) { /* client-id */ + if (strncmp(argv[1], "0x", 2) == 0) + cid_len = str2bytes(argv[1] + 2, cid, sizeof(cid)); + else { + strlcpy((char *)cid, argv[1], sizeof(cid)); + cid_len = strlen((char *)cid); + } + } - memset(&ifreq, 0, sizeof(ifreq)); signal(SIGTERM, cleanexit); if (gethostname(hostname, sizeof(hostname)) == -1) eprintf("gethostname:"); - if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) - eprintf("socket:"); - if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &bcast, sizeof(bcast)) == -1) - eprintf("setsockopt:"); + sock = open_socket(); - strlcpy(ifreq.ifr_name, ifname, IF_NAMESIZE); - ioctl(sock, SIOCGIFINDEX, &ifreq); - if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, &ifreq, sizeof(ifreq)) == -1) - eprintf("setsockopt:"); - iptoaddr(&addr, IP(255, 255, 255, 255), 68); - if (bind(sock, (void*)&addr, sizeof(addr)) != 0) - eprintf("bind:"); - ioctl(sock, SIOCGIFHWADDR, &ifreq); - memcpy(hwaddr, ifreq.ifr_hwaddr.sa_data, sizeof(ifreq.ifr_hwaddr.sa_data)); - if (!cid[0]) - memcpy(cid, hwaddr, sizeof(cid)); + if (cid_len == 0) { + cid[0] = 1; + memcpy(cid + 1, hwaddr, ETHER_ADDR_LEN); + cid_len = ETHER_ADDR_LEN + 1; + } if ((rnd = open("/dev/urandom", O_RDONLY)) == -1) eprintf("can't open /dev/urandom to generate unique transaction identifier:");