diff --git a/Makefile b/Makefile index 7126a36..687e420 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,7 @@ include config.mk HDR = util.h arg.h LIB = \ + util/compat.o \ util/strlcpy.o \ util/eprintf.o @@ -29,6 +30,8 @@ bin: $(BIN) $(OBJ): $(HDR) config.mk +$(SRC:.c=.o): util.a + .o: @echo LD $@ @$(LD) -o $@ $< util.a $(LDFLAGS) diff --git a/TODO.sam b/TODO.sam new file mode 100644 index 0000000..af36406 --- /dev/null +++ b/TODO.sam @@ -0,0 +1,4 @@ +* multiple DNS +* domain search +* NTP? +* bring interface up diff --git a/config.mk b/config.mk index 8f32578..bc449eb 100644 --- a/config.mk +++ b/config.mk @@ -10,3 +10,6 @@ LD = $(CC) CPPFLAGS = -D_DEFAULT_SOURCE CFLAGS = -Wall -Wextra -pedantic -std=c99 $(CPPFLAGS) LDFLAGS = -s + +# This is Linux specific +CFLAGS += -DUSE_RAW_SOCKET diff --git a/sdhcp.c b/sdhcp.c index 1fcf5e6..4e0a39f 100644 --- a/sdhcp.c +++ b/sdhcp.c @@ -1,9 +1,9 @@ +#include #include #include -#include #include -#include +#include #include #include @@ -82,23 +82,29 @@ enum { OBend = 255, }; -enum { Broadcast, Unicast }; - static Bootp bp; -static unsigned char magic[] = { 99, 130, 83, 99 }; +static const unsigned char magic[] = { 99, 130, 83, 99 }; + +static const unsigned char params[] = { + OBmask, OBrouter, OBdnsserver, ODlease, ODrenewaltime, ODrebindingtime +}; + +/* One socket to rule them all */ +int sock = -1; /* conf */ static unsigned char xid[sizeof(bp.xid)]; -static unsigned char hwaddr[16]; -static char hostname[HOST_NAME_MAX + 1]; +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]; +char *ifname = "eth0"; +static unsigned char cid[24]; +static int cid_len; static char *program = ""; -static int sock, timers[3]; +int timers[N_TIMERS]; /* sav */ -static unsigned char server[4]; -static unsigned char client[4]; +unsigned char server[4]; +unsigned char client[4]; static unsigned char mask[4]; static unsigned char router[4]; static unsigned char dns[4]; @@ -107,8 +113,6 @@ static int dflag = 1; /* change DNS in /etc/resolv.conf ? */ static int iflag = 1; /* set IP ? */ static int fflag = 0; /* run in foreground */ -#define IP(a, b, c, d) (unsigned char[4]){ a, b, c, d } - static void hnput(unsigned char *dst, uint32_t src, size_t n) { @@ -118,7 +122,7 @@ hnput(unsigned char *dst, uint32_t src, size_t n) dst[i] = (src >> (n * 8)) & 0xff; } -static struct sockaddr * +struct sockaddr * iptoaddr(struct sockaddr *ifaddr, unsigned char ip[4], int port) { struct sockaddr_in *in = (struct sockaddr_in *)ifaddr; @@ -130,39 +134,16 @@ iptoaddr(struct sockaddr *ifaddr, unsigned char ip[4], int port) return ifaddr; } -/* sendto UDP wrapper */ -static ssize_t -udpsend(unsigned char ip[4], int fd, void *data, size_t n) -{ - struct sockaddr addr; - socklen_t addrlen = sizeof(addr); - ssize_t sent; - - iptoaddr(&addr, ip, 67); /* bootp server */ - if ((sent = sendto(fd, data, n, 0, &addr, addrlen)) == -1) - eprintf("sendto:"); - - return sent; -} - -/* recvfrom UDP wrapper */ -static ssize_t -udprecv(unsigned char ip[4], 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) - eprintf("recvfrom:"); - - return r; -} - static void setip(unsigned char ip[4], unsigned char mask[4], unsigned char gateway[4]) { +#ifndef __linux__ + /* TODO I believe this could work under other OSes. But since the + * -e callout makes it so easy to work around this, I am just + * going to leave it out for now. + */ + (void)ip; (void)mask; (void)gateway; +#else struct ifreq ifreq; struct rtentry rtreq; int fd; @@ -187,6 +168,7 @@ setip(unsigned char ip[4], unsigned char mask[4], unsigned char gateway[4]) ioctl(fd, SIOCADDRT, &rtreq); close(fd); +#endif } static void @@ -245,7 +227,7 @@ optget(Bootp *bp, void *data, int opt, int n) } static unsigned char * -optput(unsigned char *p, int opt, unsigned char *data, size_t len) +optput(unsigned char *p, int opt, const unsigned char *data, size_t len) { *p++ = opt; *p++ = (unsigned char)len; @@ -267,7 +249,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 +259,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) { @@ -290,6 +272,7 @@ dhcpsend(int type, int how) /* memcpy(bp.ciaddr, client, sizeof bp.ciaddr); */ p = optput(p, ODipaddr, client, sizeof(client)); p = optput(p, ODserverid, server, sizeof(server)); + p = optput(p, ODparams, params, sizeof(params)); break; case DHCPrelease: memcpy(bp.ciaddr, client, sizeof(client)); @@ -299,8 +282,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(&bp, p - (unsigned char *)&bp, how); } static int @@ -315,11 +297,12 @@ dhcprecv(void) }; uint64_t n; - if (poll(pfd, LEN(pfd), -1) == -1) - eprintf("poll:"); + while (poll(pfd, LEN(pfd), -1) == -1) + if (errno != EINTR) + eprintf("poll:"); if (pfd[0].revents) { memset(&bp, 0, sizeof(bp)); - udprecv(IP(255, 255, 255, 255), sock, &bp, sizeof(bp)); + udprecv(&bp, sizeof(bp)); optget(&bp, &type, ODtype, sizeof(type)); return type; } @@ -389,10 +372,11 @@ static void run(void) { int forked = 0, t; - struct itimerspec timeout = { 0 }; + struct itimerspec timeout = { { 0, 0 }, { 0, 0 } }; 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 +415,8 @@ Requesting: /* no response from DHCPREQUEST after several attempts, go to INIT */ goto Init; Bound: + close_socket(); /* currently raw sockets only */ + optget(&bp, mask, OBmask, sizeof(mask)); optget(&bp, router, OBrouter, sizeof(router)); optget(&bp, dns, OBdnsserver, sizeof(dns)); @@ -441,12 +427,14 @@ Bound: rebindingtime = ntohl(rebindingtime); lease = ntohl(lease); acceptlease(); - fputs("Congrats! You should be on the 'net.\n", stdout); + if (!forked) + fputs("Congrats! You should be on the 'net.\n", stdout); if (!fflag && !forked) { if (fork()) exit(0); - forked = 1; + create_timers(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,14 +499,38 @@ 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; ARGBEGIN { case 'd': /* don't update DNS in /etc/resolv.conf */ @@ -540,42 +552,45 @@ 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:"); + open_socket(ifname); + get_hw_addr(ifname, hwaddr); + /* Check if the interface is down... */ + memset(&ifreq, 0, sizeof(ifreq)); 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)); + + const int want_flags = IFF_UP | IFF_RUNNING; + if (ioctl(sock, SIOCGIFFLAGS, &ifreq)) + eprintf("get flags"); + if ((ifreq.ifr_flags & want_flags) != want_flags) + eprintf("interface down"); + + 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:"); read(rnd, xid, sizeof(xid)); close(rnd); - for (i = 0; i < LEN(timers); ++i) { - timers[i] = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC); - if (timers[i] == -1) - eprintf("timerfd_create:"); - } + create_timers(0); starttime = time(NULL); run(); diff --git a/util.h b/util.h index 9071712..5fb616d 100644 --- a/util.h +++ b/util.h @@ -1,3 +1,6 @@ +#include +#include + #define MIN(a,b) (((a)<(b))?(a):(b)) #define LEN(a) (sizeof(a) / sizeof((a)[0])) #define bpdump(p,n) 1 @@ -8,3 +11,41 @@ size_t strlcpy(char *, const char *, size_t); void weprintf(const char *, ...); void eprintf(const char *, ...); void enprintf(int, const char *, ...); + +/* compat.c */ + +enum { Broadcast, Unicast }; + +extern int sock; + +#define N_TIMERS 3 +extern int timers[]; +extern unsigned char hwaddr[]; +extern unsigned char server[]; +extern unsigned char client[]; + +extern char *ifname; + +void open_socket(const char *ifname); +void close_socket(void); +ssize_t udpsend(void *data, size_t n, int how); +ssize_t udprecv(void *data, size_t n); +void get_hw_addr(const char *ifname, unsigned char *hwaddr); +void create_timers(int recreate); + +struct sockaddr *iptoaddr(struct sockaddr *ifaddr, + unsigned char ip[4], int port); +#define IP(a, b, c, d) (unsigned char[4]){ a, b, c, d } + +#ifdef __linux__ + +#include + +#else + +int timerfd_gettime(int fd, struct itimerspec *curr_value); +int timerfd_settime(int fd, int flags, + const struct itimerspec *new_value, + struct itimerspec *old_value); + +#endif diff --git a/util/Makefile b/util/Makefile new file mode 100644 index 0000000..1844eaf --- /dev/null +++ b/util/Makefile @@ -0,0 +1,2 @@ +all: + make -C .. diff --git a/util/compat.c b/util/compat.c new file mode 100644 index 0000000..7b349e2 --- /dev/null +++ b/util/compat.c @@ -0,0 +1,387 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../util.h" + +#ifdef __linux__ + +void +get_hw_addr(const char *ifname, unsigned char *hwaddr) +{ + struct ifreq ifreq; + + memset(&ifreq, 0, sizeof(ifreq)); + strlcpy(ifreq.ifr_name, ifname, IF_NAMESIZE); + if (ioctl(sock, SIOCGIFHWADDR, &ifreq)) + eprintf("SIOCGIFHWADDR"); + + memcpy(hwaddr, ifreq.ifr_hwaddr.sa_data, sizeof(ifreq.ifr_hwaddr.sa_data)); +} + +void +create_timers(int recreate) +{ /* timerfd survives a fork, don't need to recreate */ + if (recreate == 0) + for (int i = 0; i < N_TIMERS; ++i) { + timers[i] = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC); + if (timers[i] == -1) + eprintf("timerfd_create:"); + } +} + +#else + +#include +#include + +void +get_hw_addr(const char *ifname, unsigned char *hwaddr) +{ + struct ifaddrs *ifa = NULL; + struct sockaddr_dl *sa = NULL; + + if (getifaddrs(&ifa)) + eprintf("getifaddrs"); + + for (struct ifaddrs *p = ifa; p; p = p->ifa_next) { + if (p->ifa_addr->sa_family == AF_LINK && + strcmp(p->ifa_name, ifname) == 0) { + sa = (struct sockaddr_dl *)p->ifa_addr; + if (sa->sdl_type == 1 || sa->sdl_type == 6) { // ethernet + memcpy(hwaddr, LLADDR(sa), sa->sdl_alen); + freeifaddrs(ifa); + return; + } else + eprintf("INVALID %d", sa->sdl_type); + } + } + + eprintf("No interface called '%s'", ifname); +} + +#include +#include + +static timer_t t_id[N_TIMERS]; +static int t_wr_pipe[N_TIMERS]; + +static void +sigalrm(int sig, siginfo_t *si, void *ctx) +{ + (void)sig; (void)ctx; + + unsigned char n = si->si_value.sival_int; + + if (n < N_TIMERS) + write(t_wr_pipe[n], &n, sizeof(n)); +} + +void +create_timers(int recreate) +{ + struct sigaction act = { + .sa_flags = SA_SIGINFO, + .sa_sigaction = sigalrm, + }; + struct sigevent ev = { + .sigev_notify = SIGEV_SIGNAL, + .sigev_signo = SIGALRM, + }; + + for (int id = 0; id < N_TIMERS; ++id) { + ev.sigev_value.sival_int = id; + + if (timer_create(CLOCK_MONOTONIC, &ev, &t_id[id])) + eprintf("timer_create"); + } + + if (recreate) + /* the pipes survive the fork() */ + return; + + if (sigaction(SIGALRM, &act, NULL) < 0) + eprintf("sigaction SIGALRM:"); + + for (int id = 0; id < N_TIMERS; ++id) { + int pipes[2]; + if (pipe(pipes)) + eprintf("pipe"); + + timers[id] = pipes[0]; /* read end */ + t_wr_pipe[id] = pipes[1]; /* write end */ + } +} + +int +timerfd_gettime(int fd, struct itimerspec *curr_value) +{ + for (int i = 0; i < N_TIMERS; ++i) + if (timers[i] == fd) + return timer_gettime(t_id[i], curr_value); + + errno = EBADF; + return -1; +} + +int +timerfd_settime(int fd, int flags, + const struct itimerspec *new_value, + struct itimerspec *old_value) +{ + for (int i = 0; i < N_TIMERS; ++i) + if (timers[i] == fd) + return timer_settime(t_id[i], flags, new_value, old_value); + + errno = EBADF; + return -1; +} + +#endif + +#ifdef USE_RAW_SOCKET + +#include +#include +#include +#include + +/* Fixed bootp header + 312 for optional */ +#define BOOTP_SIZE (236 + 312) + +static struct pkt { + struct ether_header ethhdr; + struct ip iphdr; + struct udphdr udphdr; + unsigned char bootp[BOOTP_SIZE]; +} __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; + unsigned char bootp[BOOTP_SIZE]; +} __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; +} + +/* open a socket - ifreq will have ifname filled in */ +void +open_socket(const char *ifname) +{ + int 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:"); + + struct ifreq ifreq; + memset(&ifreq, 0, sizeof(ifreq)); + strlcpy(ifreq.ifr_name, ifname, IF_NAMESIZE); + + if (ioctl(sock, SIOCGIFINDEX, &ifreq)) + eprintf("SIOCGIFINDEX"); + ifindex = ifreq.ifr_ifindex; +} + +void +close_socket(void) +{ /* We close the socket for performance reasons */ + close(sock); + sock = -1; +} + +ssize_t +udpsend(void *data, size_t n, int how) +{ + if (sock == -1) + open_socket(ifname); + + 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) - BOOTP_SIZE + 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; +} + +ssize_t +udprecv(void *data, size_t n) +{ + struct pkt recv; + int r; + + memset(&recv, 0, sizeof(recv)); + if ((r = read(sock, &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; +} + +#else + +/* open a socket - ifreq will have ifname filled in */ +void +open_socket(const char *ifname) +{ + struct ifreq ifreq; + int set = 1; + + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) + eprintf("socket:"); + + if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &set, sizeof(set)) == -1) + eprintf("SO_BROADCAST:"); + + memset(&ifreq, 0, sizeof(ifreq)); + strlcpy(ifreq.ifr_name, ifname, IF_NAMESIZE); + +#ifdef SIOCGIFINDEX + // SAM I am pretty sure this is not needed + if (ioctl(sock, SIOCGIFINDEX, &ifreq)) + eprintf("SIOCGIFINDEX:"); +#endif + +#ifdef SO_BINDTODEVICE + if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, &ifreq, sizeof(ifreq)) == -1) + eprintf("SO_BINDTODEVICE:"); +#endif + + /* needed */ + struct sockaddr addr; + iptoaddr(&addr, IP(0, 0, 0, 0), 68); + if (bind(sock, (void*)&addr, sizeof(addr)) != 0) + eprintf("bind:"); +} + +void close_socket(void) {} + +/* sendto UDP wrapper */ +ssize_t +udpsend(void *data, size_t n, int how) +{ + struct sockaddr addr; + socklen_t addrlen = sizeof(addr); + ssize_t sent; + unsigned char ip[4]; + int flags = 0; + + if (how == Broadcast) { + *(uint32_t *)ip = INADDR_BROADCAST; + flags |= MSG_DONTROUTE; + } else + memcpy(ip, server, 4); + + iptoaddr(&addr, ip, 67); /* bootp server */ + if ((sent = sendto(sock, data, n, flags, &addr, addrlen)) == -1) + eprintf("sendto:"); + + return sent; +} + +/* recvfrom UDP wrapper */ +ssize_t +udprecv(void *data, size_t n) +{ + ssize_t r; + + if ((r = recv(sock, data, n, 0)) == -1) + eprintf("recvfrom:"); + + return r; +} + +#endif