X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fodhcpd.git;a=blobdiff_plain;f=src%2Fdhcpv4.c;h=3002c1e1721f63bf17e1a6b37cdcdebd63bf8b71;hp=826a1f80304b2fa4741756dd9970a2606a4523e1;hb=d834ae3ca59cf75cd7f26a5201a7223eaa468fbb;hpb=e3b49f30d5cd8fa2c6e5b5301d7ffde5fd8b50cc diff --git a/src/dhcpv4.c b/src/dhcpv4.c index 826a1f8..3002c1e 100644 --- a/src/dhcpv4.c +++ b/src/dhcpv4.c @@ -35,19 +35,22 @@ #include "dhcpv4.h" #include "dhcpv6.h" +static void dhcpv4_netevent_cb(unsigned long event, struct netevent_handler_info *info); static int setup_dhcpv4_addresses(struct interface *iface); static void update_static_assignments(struct interface *iface); static void valid_until_cb(struct uloop_timeout *event); +static void handle_addrlist_change(struct interface *iface); static void free_dhcpv4_assignment(struct dhcpv4_assignment *a); static void dhcpv4_fr_start(struct dhcpv4_assignment *a); static void dhcpv4_fr_stop(struct dhcpv4_assignment *a); static void handle_dhcpv4(void *addr, void *data, size_t len, struct interface *iface, void *dest_addr); static struct dhcpv4_assignment* dhcpv4_lease(struct interface *iface, - enum dhcpv4_msg msg, const uint8_t *mac, struct in_addr reqaddr, + enum dhcpv4_msg msg, const uint8_t *mac, const uint32_t reqaddr, uint32_t *leasetime, const char *hostname, const size_t hostname_len, const bool accept_fr_nonce, bool *incl_fr_opt, uint32_t *fr_serverid); +static struct netevent_handler dhcpv4_netevent_handler = { .cb = dhcpv4_netevent_cb, }; static struct uloop_timeout valid_until_timeout = {.cb = valid_until_cb}; static uint32_t serial = 0; @@ -58,14 +61,18 @@ struct odhcpd_ref_ip { }; /* Create socket and register events */ -int init_dhcpv4(void) +int dhcpv4_init(void) { uloop_timeout_set(&valid_until_timeout, 1000); + netlink_add_netevent_handler(&dhcpv4_netevent_handler); + return 0; } -int setup_dhcpv4_interface(struct interface *iface, bool enable) +int dhcpv4_setup_interface(struct interface *iface, bool enable) { + int ret = 0; + if (iface->dhcpv4_event.uloop.fd > 0) { uloop_fd_delete(&iface->dhcpv4_event.uloop); close(iface->dhcpv4_event.uloop.fd); @@ -73,57 +80,117 @@ int setup_dhcpv4_interface(struct interface *iface, bool enable) } if (iface->dhcpv4 && enable) { + struct sockaddr_in bind_addr = {AF_INET, htons(DHCPV4_SERVER_PORT), + {INADDR_ANY}, {0}}; + int val = 1; + if (!iface->dhcpv4_assignments.next) INIT_LIST_HEAD(&iface->dhcpv4_assignments); if (!iface->dhcpv4_fr_ips.next) INIT_LIST_HEAD(&iface->dhcpv4_fr_ips); - int sock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP); - if (sock < 0) { - syslog(LOG_ERR, "Failed to create DHCPv4 server socket: %s", - strerror(errno)); - return -1; + iface->dhcpv4_event.uloop.fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP); + if (iface->dhcpv4_event.uloop.fd < 0) { + syslog(LOG_ERR, "socket(AF_INET): %m"); + ret = -1; + goto out; } /* Basic IPv4 configuration */ - int val = 1; - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); - setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &val, sizeof(val)); - setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &val, sizeof(val)); + if (setsockopt(iface->dhcpv4_event.uloop.fd, SOL_SOCKET, SO_REUSEADDR, + &val, sizeof(val)) < 0) { + syslog(LOG_ERR, "setsockopt(SO_REUSEADDR): %m"); + ret = -1; + goto out; + } + + if (setsockopt(iface->dhcpv4_event.uloop.fd, SOL_SOCKET, SO_BROADCAST, + &val, sizeof(val)) < 0) { + syslog(LOG_ERR, "setsockopt(SO_BROADCAST): %m"); + ret = -1; + goto out; + } + + if (setsockopt(iface->dhcpv4_event.uloop.fd, IPPROTO_IP, IP_PKTINFO, + &val, sizeof(val)) < 0) { + syslog(LOG_ERR, "setsockopt(IP_PKTINFO): %m"); + ret = -1; + goto out; + } val = IPTOS_PREC_INTERNETCONTROL; - setsockopt(sock, IPPROTO_IP, IP_TOS, &val, sizeof(val)); + if (setsockopt(iface->dhcpv4_event.uloop.fd, IPPROTO_IP, IP_TOS, + &val, sizeof(val)) < 0) { + syslog(LOG_ERR, "setsockopt(IP_TOS): %m"); + ret = -1; + goto out; + } val = IP_PMTUDISC_DONT; - setsockopt(sock, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val)); + if (setsockopt(iface->dhcpv4_event.uloop.fd, IPPROTO_IP, IP_MTU_DISCOVER, + &val, sizeof(val)) < 0) { + syslog(LOG_ERR, "setsockopt(IP_MTU_DISCOVER): %m"); + ret = -1; + goto out; + } - setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, - iface->ifname, strlen(iface->ifname)); + if (setsockopt(iface->dhcpv4_event.uloop.fd, SOL_SOCKET, SO_BINDTODEVICE, + iface->ifname, strlen(iface->ifname)) < 0) { + syslog(LOG_ERR, "setsockopt(SO_BINDTODEVICE): %m"); + ret = -1; + goto out; + } - struct sockaddr_in bind_addr = {AF_INET, htons(DHCPV4_SERVER_PORT), - {INADDR_ANY}, {0}}; + if (bind(iface->dhcpv4_event.uloop.fd, (struct sockaddr*)&bind_addr, + sizeof(bind_addr)) < 0) { + syslog(LOG_ERR, "bind(): %m"); + ret = -1; + goto out; + } - if (bind(sock, (struct sockaddr*)&bind_addr, sizeof(bind_addr))) { - syslog(LOG_ERR, "Failed to open DHCPv4 server socket: %s", - strerror(errno)); - return -1; + if (setup_dhcpv4_addresses(iface) < 0) { + ret = -1; + goto out; } - iface->dhcpv4_event.uloop.fd = sock; iface->dhcpv4_event.handle_dgram = handle_dhcpv4; odhcpd_register(&iface->dhcpv4_event); - if (setup_dhcpv4_addresses(iface) < 0) - return -1; - update_static_assignments(iface); } else if (iface->dhcpv4_assignments.next) { while (!list_empty(&iface->dhcpv4_assignments)) free_dhcpv4_assignment(list_first_entry(&iface->dhcpv4_assignments, struct dhcpv4_assignment, head)); } - return 0; + +out: + if (ret < 0 && iface->dhcpv4_event.uloop.fd > 0) { + close(iface->dhcpv4_event.uloop.fd); + iface->dhcpv4_event.uloop.fd = -1; + } + + return ret; +} + + +static void dhcpv4_netevent_cb(unsigned long event, struct netevent_handler_info *info) +{ + struct interface *iface = info->iface; + + if (!iface || iface->dhcpv4 == MODE_DISABLED) + return; + + switch (event) { + case NETEV_IFINDEX_CHANGE: + dhcpv4_setup_interface(iface, true); + break; + case NETEV_ADDRLIST_CHANGE: + handle_addrlist_change(iface); + break; + default: + break; + } } static struct dhcpv4_assignment *find_assignment_by_hwaddr(struct interface *iface, const uint8_t *hwaddr) @@ -293,7 +360,7 @@ static void decr_ref_cnt_ip(struct odhcpd_ref_ip **ptr, struct interface *iface) struct odhcpd_ref_ip *ip = *ptr; if (--ip->ref_cnt == 0) { - odhcpd_setup_addr(&ip->addr, iface, false, false); + netlink_setup_addr(&ip->addr, iface->ifindex, false, false); list_del(&ip->head); free(ip); @@ -344,17 +411,14 @@ static void valid_until_cb(struct uloop_timeout *event) uloop_timeout_set(event, 1000); } -void dhcpv4_addr_update(struct interface *iface) +static void handle_addrlist_change(struct interface *iface) { - if (iface->dhcpv4 == MODE_DISABLED) - return; - struct odhcpd_ipaddr ip; struct odhcpd_ref_ip *a; struct dhcpv4_assignment *c; uint32_t mask = iface->dhcpv4_mask.s_addr; - memset(&ip, sizeof(ip), 0); + memset(&ip, 0, sizeof(ip)); ip.addr.in = iface->dhcpv4_local; ip.prefix = odhcpd_netmask2bitlen(false, &iface->dhcpv4_mask); ip.broadcast = iface->dhcpv4_bcast; @@ -373,7 +437,7 @@ void dhcpv4_addr_update(struct interface *iface) a = list_first_entry(&iface->dhcpv4_fr_ips, struct odhcpd_ref_ip, head); - if (odhcpd_setup_addr(&a->addr, iface, false, true)) { + if (netlink_setup_addr(&a->addr, iface->ifindex, false, true)) { syslog(LOG_ERR, "Failed to add ip address"); return; } @@ -430,7 +494,9 @@ static void dhcpv4_put(struct dhcpv4_message *msg, uint8_t **cookie, uint8_t type, uint8_t len, const void *data) { uint8_t *c = *cookie; - if (*cookie + 2 + len > (uint8_t*)&msg[1]) + uint8_t *end = (uint8_t *)msg + sizeof(*msg); + + if (*cookie + 2 + len > end) return; *c++ = type; @@ -602,7 +668,7 @@ static void handle_dhcpv4(void *addr, void *data, size_t len, uint8_t reqmsg = DHCPV4_MSG_REQUEST; uint8_t msg = DHCPV4_MSG_ACK; - struct in_addr reqaddr = {INADDR_ANY}; + uint32_t reqaddr = INADDR_ANY; uint32_t leasetime = 0; size_t hostname_len = 0; char hostname[256]; @@ -666,7 +732,7 @@ static void handle_dhcpv4(void *addr, void *data, size_t len, } else if (reqmsg == DHCPV4_MSG_DISCOVER) msg = DHCPV4_MSG_OFFER; else if (reqmsg == DHCPV4_MSG_REQUEST && - ((reqaddr.s_addr && reqaddr.s_addr != lease->addr) || + ((reqaddr && reqaddr != lease->addr) || (req->ciaddr.s_addr && req->ciaddr.s_addr != lease->addr))) { msg = DHCPV4_MSG_NAK; /* @@ -701,6 +767,11 @@ static void handle_dhcpv4(void *addr, void *data, size_t len, req->chaddr[0],req->chaddr[1],req->chaddr[2], req->chaddr[3],req->chaddr[4],req->chaddr[5]); +#ifdef WITH_UBUS + if (reqmsg == DHCPV4_MSG_RELEASE) + ubus_bcast_dhcp_event("dhcp.release", req->chaddr, req->hlen, + &req->ciaddr, hostname, iface->ifname); +#endif if (reqmsg == DHCPV4_MSG_DECLINE || reqmsg == DHCPV4_MSG_RELEASE) return; @@ -754,8 +825,10 @@ static void handle_dhcpv4(void *addr, void *data, size_t len, } } - struct ifreq ifr = {.ifr_name = ""}; - strncpy(ifr.ifr_name, iface->ifname, sizeof(ifr.ifr_name)); + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, iface->ifname, sizeof(ifr.ifr_name) - 1); if (!ioctl(sock, SIOCGIFMTU, &ifr)) { uint16_t mtu = htons(ifr.ifr_mtu); @@ -850,6 +923,12 @@ static void handle_dhcpv4(void *addr, void *data, size_t len, sendto(sock, &reply, sizeof(reply), MSG_DONTWAIT, (struct sockaddr*)&dest, sizeof(dest)); + +#ifdef WITH_UBUS + if (msg == DHCPV4_MSG_ACK) + ubus_bcast_dhcp_event("dhcp.ack", req->chaddr, req->hlen, &reply.yiaddr, + hostname, iface->ifname); +#endif } static bool dhcpv4_assign(struct interface *iface, @@ -914,22 +993,14 @@ static bool dhcpv4_assign(struct interface *iface, static struct dhcpv4_assignment* dhcpv4_lease(struct interface *iface, - enum dhcpv4_msg msg, const uint8_t *mac, struct in_addr reqaddr, + enum dhcpv4_msg msg, const uint8_t *mac, const uint32_t reqaddr, uint32_t *leasetime, const char *hostname, const size_t hostname_len, const bool accept_fr_nonce, bool *incl_fr_opt, uint32_t *fr_serverid) { + struct dhcpv4_assignment *a = find_assignment_by_hwaddr(iface, mac); struct dhcpv4_assignment *lease = NULL; time_t now = odhcpd_time(); - struct dhcpv4_assignment *c, *a = NULL; - list_for_each_entry(c, &iface->dhcpv4_assignments, head) { - if (!memcmp(c->hwaddr, mac, 6)) { - a = c; - if (c->addr == reqaddr.s_addr) - break; - } - } - if (a && (a->flags & OAF_BOUND) && a->fr_ip) { *fr_serverid = a->fr_ip->addr.addr.in.s_addr; dhcpv4_fr_stop(a); @@ -951,7 +1022,7 @@ static struct dhcpv4_assignment* dhcpv4_lease(struct interface *iface, /* Don't consider new assignment as infinite */ a->valid_until = now; - assigned = dhcpv4_assign(iface, a, reqaddr.s_addr); + assigned = dhcpv4_assign(iface, a, reqaddr); if (assigned) { a->iface = iface; list_add(&a->head, &iface->dhcpv4_assignments); @@ -961,7 +1032,7 @@ static struct dhcpv4_assignment* dhcpv4_lease(struct interface *iface, (iface->dhcpv4_start_ip.s_addr & iface->dhcpv4_mask.s_addr)) { list_del(&a->head); - assigned = dhcpv4_assign(iface, a, reqaddr.s_addr); + assigned = dhcpv4_assign(iface, a, reqaddr); if (assigned) list_add(&a->head, &iface->dhcpv4_assignments); }