/* BPF requires that we read the entire buffer. * So we pass the buffer in the API so we can loop on >1 packet. */ ssize_t if_readrawpacket(struct interface *ifp, int protocol, void *data, size_t len, int *flags) { int fd; struct bpf_hdr packet; ssize_t bytes; const unsigned char *payload; struct dhcp_state *state; state = D_STATE(ifp); if (protocol == ETHERTYPE_ARP) fd = state->arp_fd; else fd = state->raw_fd; *flags = 0; for (;;) { if (state->buffer_len == 0) { bytes = read(fd, state->buffer, state->buffer_size); if (bytes == -1 || bytes == 0) return bytes; state->buffer_len = (size_t)bytes; state->buffer_pos = 0; } bytes = -1; memcpy(&packet, state->buffer + state->buffer_pos, sizeof(packet)); if (packet.bh_caplen != packet.bh_datalen) goto next; /* Incomplete packet, drop. */ if (state->buffer_pos + packet.bh_caplen + packet.bh_hdrlen > state->buffer_len) goto next; /* Packet beyond buffer, drop. */ payload = state->buffer + state->buffer_pos + packet.bh_hdrlen + ETHER_HDR_LEN; bytes = (ssize_t)packet.bh_caplen - ETHER_HDR_LEN; if ((size_t)bytes > len) bytes = (ssize_t)len; memcpy(data, payload, (size_t)bytes); next: state->buffer_pos += BPF_WORDALIGN(packet.bh_hdrlen + packet.bh_caplen); if (state->buffer_pos >= state->buffer_len) { state->buffer_len = state->buffer_pos = 0; *flags |= RAW_EOF; } if (bytes != -1) return bytes; } }
void ipv4ll_start(void *arg) { struct interface *ifp = arg; struct dhcp_state *state = D_STATE(ifp); uint32_t addr; eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp); state->probes = 0; state->claims = 0; if (state->addr.s_addr) { state->conflicts = 0; if (IN_LINKLOCAL(htonl(state->addr.s_addr))) { arp_announce(ifp); return; } } if (state->offer == NULL) addr = 0; else { addr = state->offer->yiaddr; free(state->offer); } /* We maybe rebooting an IPv4LL address. */ if (!IN_LINKLOCAL(htonl(addr))) { syslog(LOG_INFO, "%s: probing for an IPv4LL address", ifp->name); addr = 0; } if (addr == 0) state->offer = ipv4ll_find_lease(addr); else state->offer = ipv4ll_make_lease(addr); if (state->offer == NULL) syslog(LOG_ERR, "%s: %m", __func__); else { state->lease.frominfo = 0; arp_probe(ifp); } }
void ipv4ll_handle_failure(void *arg) { struct interface *ifp = arg; struct dhcp_state *state = D_STATE(ifp); time_t up; if (state->fail.s_addr == state->addr.s_addr) { /* RFC 3927 Section 2.5 */ up = uptime(); if (state->defend + DEFEND_INTERVAL > up) { syslog(LOG_WARNING, "%s: IPv4LL %d second defence failed", ifp->name, DEFEND_INTERVAL); dhcp_drop(ifp, "EXPIRE"); state->conflicts = -1; } else { syslog(LOG_DEBUG, "%s: defended IPv4LL address", ifp->name); state->defend = up; return; } } free(state->offer); state->offer = NULL; eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp); if (++state->conflicts > MAX_CONFLICTS) { syslog(LOG_ERR, "%s: failed to acquire an IPv4LL address", ifp->name); if (ifp->options->options & DHCPCD_DHCP) { state->interval = RATE_LIMIT_INTERVAL / 2; dhcp_discover(ifp); } else eloop_timeout_add_sec(ifp->ctx->eloop, RATE_LIMIT_INTERVAL, ipv4ll_start, ifp); } else { eloop_timeout_add_sec(ifp->ctx->eloop, PROBE_WAIT, ipv4ll_start, ifp); } }
int if_openrawsocket(struct interface *ifp, int protocol) { struct dhcp_state *state; int fd = -1; struct ifreq ifr; int ibuf_len = 0; size_t buf_len; struct bpf_version pv; struct bpf_program pf; #ifdef BIOCIMMEDIATE int flags; #endif #ifdef _PATH_BPF fd = open(_PATH_BPF, O_RDWR | O_CLOEXEC | O_NONBLOCK); #else char device[32]; int n = 0; do { snprintf(device, sizeof(device), "/dev/bpf%d", n++); fd = open(device, O_RDWR | O_CLOEXEC | O_NONBLOCK); } while (fd == -1 && errno == EBUSY); #endif if (fd == -1) return -1; state = D_STATE(ifp); memset(&pv, 0, sizeof(pv)); if (ioctl(fd, BIOCVERSION, &pv) == -1) goto eexit; if (pv.bv_major != BPF_MAJOR_VERSION || pv.bv_minor < BPF_MINOR_VERSION) { syslog(LOG_ERR, "BPF version mismatch - recompile"); goto eexit; } memset(&ifr, 0, sizeof(ifr)); strlcpy(ifr.ifr_name, ifp->name, sizeof(ifr.ifr_name)); if (ioctl(fd, BIOCSETIF, &ifr) == -1) goto eexit; /* Get the required BPF buffer length from the kernel. */ if (ioctl(fd, BIOCGBLEN, &ibuf_len) == -1) goto eexit; buf_len = (size_t)ibuf_len; if (state->buffer_size != buf_len) { free(state->buffer); state->buffer = malloc(buf_len); if (state->buffer == NULL) goto eexit; state->buffer_size = buf_len; state->buffer_len = state->buffer_pos = 0; } #ifdef BIOCIMMEDIATE flags = 1; if (ioctl(fd, BIOCIMMEDIATE, &flags) == -1) goto eexit; #endif /* Install the DHCP filter */ memset(&pf, 0, sizeof(pf)); if (protocol == ETHERTYPE_ARP) { pf.bf_insns = UNCONST(arp_bpf_filter); pf.bf_len = arp_bpf_filter_len; } else { pf.bf_insns = UNCONST(dhcp_bpf_filter); pf.bf_len = dhcp_bpf_filter_len; } if (ioctl(fd, BIOCSETF, &pf) == -1) goto eexit; return fd; eexit: free(state->buffer); state->buffer = NULL; close(fd); return -1; }
int if_route(const struct rt *rt, int action) { struct nlmr *nlm; int retval = 0; struct dhcp_state *state; nlm = calloc(1, sizeof(*nlm)); if (nlm == NULL) return -1; nlm->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); nlm->hdr.nlmsg_type = RTM_NEWROUTE; if (action == 0) nlm->hdr.nlmsg_flags = NLM_F_REPLACE; else if (action == 1) nlm->hdr.nlmsg_flags = NLM_F_CREATE | NLM_F_EXCL; else nlm->hdr.nlmsg_type = RTM_DELROUTE; nlm->hdr.nlmsg_flags |= NLM_F_REQUEST; nlm->rt.rtm_family = AF_INET; nlm->rt.rtm_table = RT_TABLE_MAIN; state = D_STATE(rt->iface); if (action == -1 || action == -2) nlm->rt.rtm_scope = RT_SCOPE_NOWHERE; else { nlm->hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL; /* We only change route metrics for kernel routes */ if (rt->dest.s_addr == (state->addr.s_addr & state->net.s_addr) && rt->net.s_addr == state->net.s_addr) nlm->rt.rtm_protocol = RTPROT_KERNEL; else nlm->rt.rtm_protocol = RTPROT_BOOT; if (rt->gate.s_addr == INADDR_ANY || (rt->gate.s_addr == rt->dest.s_addr && rt->net.s_addr == INADDR_BROADCAST)) nlm->rt.rtm_scope = RT_SCOPE_LINK; else nlm->rt.rtm_scope = RT_SCOPE_UNIVERSE; nlm->rt.rtm_type = RTN_UNICAST; } nlm->rt.rtm_dst_len = inet_ntocidr(rt->net); add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_DST, &rt->dest.s_addr, sizeof(rt->dest.s_addr)); if (nlm->rt.rtm_protocol == RTPROT_KERNEL) { add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_PREFSRC, &state->addr.s_addr, sizeof(state->addr.s_addr)); } /* If destination == gateway then don't add the gateway */ if (rt->dest.s_addr != rt->gate.s_addr || rt->net.s_addr != INADDR_BROADCAST) add_attr_l(&nlm->hdr, sizeof(*nlm), RTA_GATEWAY, &rt->gate.s_addr, sizeof(rt->gate.s_addr)); if (rt->gate.s_addr != htonl(INADDR_LOOPBACK)) add_attr_32(&nlm->hdr, sizeof(*nlm), RTA_OIF, rt->iface->index); add_attr_32(&nlm->hdr, sizeof(*nlm), RTA_PRIORITY, rt->metric); if (send_netlink(&nlm->hdr) == -1) retval = -1; free(nlm); return retval; }