From 91a28e4566bdae6532d3801332bef9999f43605c Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sat, 2 Feb 2019 22:48:18 +0100 Subject: [PATCH 01/16] ndp: answer global-addressed NS manually An upstream router may address solicits to the global address of the target, these will not be answered by the kernel and not routed either due to link-local source. The NS will eventually be retried as multicast, but we want to avoid this. see also https://forum.archive.openwrt.org/viewtopic.php?id=40871 Signed-off-by: Stefan Alfers --- src/ndp.c | 38 ++++++++++++++++++ src/netlink.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/odhcpd.h | 1 + 3 files changed, 146 insertions(+) diff --git a/src/ndp.c b/src/ndp.c index aaaa69c..3f0a037 100644 --- a/src/ndp.c +++ b/src/ndp.c @@ -295,6 +295,33 @@ static void ping6(struct in6_addr *addr, netlink_setup_route(addr, 128, iface->ifindex, NULL, 128, false); } +/* Send a Neighbor Advertisement. */ +static void send_na(struct in6_addr *to_addr, + const struct interface *iface, struct in6_addr *for_addr, + const uint8_t *mac) +{ + struct sockaddr_in6 dest = { .sin6_family = AF_INET6, .sin6_addr = *to_addr }; + char pbuf[sizeof(struct nd_neighbor_advert) + sizeof(struct nd_opt_hdr) + 6]; + struct nd_neighbor_advert *adv = (struct nd_neighbor_advert*)pbuf; + struct nd_opt_hdr *opt = (struct nd_opt_hdr*) &pbuf[sizeof(struct nd_neighbor_advert)]; + struct iovec iov = { .iov_base = &pbuf, .iov_len = sizeof(pbuf) }; + char ipbuf[INET6_ADDRSTRLEN]; + + memset(pbuf, 0, sizeof(pbuf)); + adv->nd_na_hdr = (struct icmp6_hdr) { + .icmp6_type = ND_NEIGHBOR_ADVERT, + .icmp6_dataun.icmp6_un_data32 = { 0x40000000L } + }; + adv->nd_na_target = *for_addr; + *opt = (struct nd_opt_hdr) { .nd_opt_type = ND_OPT_TARGET_LINKADDR, .nd_opt_len = 1 }; + memcpy(&pbuf[sizeof(struct nd_neighbor_advert) + sizeof(struct nd_opt_hdr)], mac, 6); + + inet_ntop(AF_INET6, to_addr, ipbuf, sizeof(ipbuf)); + syslog(LOG_DEBUG, "Answering NS to %s on %s", ipbuf, iface->ifname); + + odhcpd_send(iface->ndp_ping_fd, &dest, &iov, 1, iface); +} + /* Handle solicitations */ static void handle_solicit(void *addr, void *data, size_t len, struct interface *iface, _unused void *dest) @@ -335,6 +362,17 @@ static void handle_solicit(void *addr, void *data, size_t len, (ns_is_dad || !c->external)) ping6(&req->nd_ns_target, c); } + + /* Catch global-addressed NS and answer them manually. + * The kernel won't answer these and cannot route them either. */ + if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) && + IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) { + bool is_proxy_neigh = netlink_get_interface_proxy_neigh(iface->ifindex, + &req->nd_ns_target) == 1; + + if (is_proxy_neigh) + send_na(&ip6->ip6_src, iface, &req->nd_ns_target, mac); + } } /* Use rtnetlink to modify kernel routes */ diff --git a/src/netlink.c b/src/netlink.c index d9c2b40..e3b2c3f 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -684,6 +684,113 @@ out: } +struct neigh_info { + int ifindex; + int pending; + const struct in6_addr *addr; + int ret; +}; + + +static int cb_valid_handler2(struct nl_msg *msg, void *arg) +{ + struct neigh_info *ctxt = (struct neigh_info *)arg; + struct nlmsghdr *hdr = nlmsg_hdr(msg); + struct ndmsg *ndm; + struct nlattr *nla_dst; + + if (hdr->nlmsg_type != RTM_NEWNEIGH) + return NL_SKIP; + + ndm = NLMSG_DATA(hdr); + if (ndm->ndm_family != AF_INET6 || + (ctxt->ifindex && ndm->ndm_ifindex != ctxt->ifindex)) + return NL_SKIP; + + if (!(ndm->ndm_flags & NTF_PROXY)) + return NL_SKIP; + + nla_dst = nlmsg_find_attr(hdr, sizeof(*ndm), NDA_DST); + if (!nla_dst) + return NL_SKIP; + + if (nla_memcmp(nla_dst,ctxt->addr, 16) == 0) + ctxt->ret = 1; + + return NL_OK; +} + + +static int cb_finish_handler2(_unused struct nl_msg *msg, void *arg) +{ + struct neigh_info *ctxt = (struct neigh_info *)arg; + + ctxt->pending = 0; + + return NL_STOP; +} + + +static int cb_error_handler2(_unused struct sockaddr_nl *nla, struct nlmsgerr *err, + void *arg) +{ + struct neigh_info *ctxt = (struct neigh_info *)arg; + + ctxt->pending = 0; + ctxt->ret = err->error; + + return NL_STOP; +} + +/* Detect an IPV6-address proxy neighbor for the given interface */ +int netlink_get_interface_proxy_neigh(int ifindex, const struct in6_addr *addr) +{ + struct nl_msg *msg; + struct ndmsg ndm = { + .ndm_family = AF_INET6, + .ndm_flags = NTF_PROXY, + .ndm_ifindex = ifindex, + }; + struct nl_cb *cb = nl_cb_alloc(NL_CB_DEFAULT); + struct neigh_info ctxt = { + .ifindex = ifindex, + .addr = addr, + .ret = 0, + .pending = 1, + }; + + if (!cb) { + ctxt.ret = -1; + goto out; + } + + msg = nlmsg_alloc_simple(RTM_GETNEIGH, NLM_F_REQUEST | NLM_F_MATCH); + + if (!msg) { + ctxt.ret = -1; + goto out; + } + + nlmsg_append(msg, &ndm, sizeof(ndm), 0); + nla_put(msg, NDA_DST, sizeof(*addr), addr); + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_valid_handler2, &ctxt); + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, cb_finish_handler2, &ctxt); + nl_cb_err(cb, NL_CB_CUSTOM, cb_error_handler2, &ctxt); + + nl_send_auto_complete(rtnl_socket, msg); + while (ctxt.pending > 0) + nl_recvmsgs(rtnl_socket, cb); + + nlmsg_free(msg); + +out: + nl_cb_put(cb); + + return ctxt.ret; +} + + int netlink_setup_route(const struct in6_addr *addr, const int prefixlen, const int ifindex, const struct in6_addr *gw, const uint32_t metric, const bool add) diff --git a/src/odhcpd.h b/src/odhcpd.h index 4a07252..01c9ad7 100644 --- a/src/odhcpd.h +++ b/src/odhcpd.h @@ -387,6 +387,7 @@ void dhcpv6_ia_write_statefile(void); int netlink_add_netevent_handler(struct netevent_handler *hdlr); ssize_t netlink_get_interface_addrs(const int ifindex, bool v6, struct odhcpd_ipaddr **addrs); +int netlink_get_interface_proxy_neigh(int ifindex, const struct in6_addr *addr); int netlink_setup_route(const struct in6_addr *addr, const int prefixlen, const int ifindex, const struct in6_addr *gw, const uint32_t metric, const bool add); -- 2.25.1 From 1d240094472c2a46096dc5a412ba4423a28b8ea3 Mon Sep 17 00:00:00 2001 From: Hans Dedecker Date: Sun, 15 Sep 2019 19:26:37 +0200 Subject: [PATCH 02/16] netlink: rename netlink callback handlers Signed-off-by: Hans Dedecker --- src/netlink.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/netlink.c b/src/netlink.c index e3b2c3f..1a7534d 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -504,7 +504,7 @@ struct addr_info { }; -static int cb_valid_handler(struct nl_msg *msg, void *arg) +static int cb_addr_valid(struct nl_msg *msg, void *arg) { struct addr_info *ctxt = (struct addr_info *)arg; struct odhcpd_ipaddr *addrs = *(ctxt->addrs); @@ -571,7 +571,7 @@ static int cb_valid_handler(struct nl_msg *msg, void *arg) } -static int cb_finish_handler(_unused struct nl_msg *msg, void *arg) +static int cb_addr_finish(_unused struct nl_msg *msg, void *arg) { struct addr_info *ctxt = (struct addr_info *)arg; @@ -581,7 +581,7 @@ static int cb_finish_handler(_unused struct nl_msg *msg, void *arg) } -static int cb_error_handler(_unused struct sockaddr_nl *nla, struct nlmsgerr *err, +static int cb_addr_error(_unused struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) { struct addr_info *ctxt = (struct addr_info *)arg; @@ -651,9 +651,9 @@ ssize_t netlink_get_interface_addrs(int ifindex, bool v6, struct odhcpd_ipaddr * nlmsg_append(msg, &ifa, sizeof(ifa), 0); - nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_valid_handler, &ctxt); - nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, cb_finish_handler, &ctxt); - nl_cb_err(cb, NL_CB_CUSTOM, cb_error_handler, &ctxt); + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_addr_valid, &ctxt); + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, cb_addr_finish, &ctxt); + nl_cb_err(cb, NL_CB_CUSTOM, cb_addr_error, &ctxt); nl_send_auto_complete(rtnl_socket, msg); while (ctxt.pending > 0) @@ -692,7 +692,7 @@ struct neigh_info { }; -static int cb_valid_handler2(struct nl_msg *msg, void *arg) +static int cb_proxy_neigh_valid(struct nl_msg *msg, void *arg) { struct neigh_info *ctxt = (struct neigh_info *)arg; struct nlmsghdr *hdr = nlmsg_hdr(msg); @@ -721,7 +721,7 @@ static int cb_valid_handler2(struct nl_msg *msg, void *arg) } -static int cb_finish_handler2(_unused struct nl_msg *msg, void *arg) +static int cb_proxy_neigh_finish(_unused struct nl_msg *msg, void *arg) { struct neigh_info *ctxt = (struct neigh_info *)arg; @@ -731,7 +731,7 @@ static int cb_finish_handler2(_unused struct nl_msg *msg, void *arg) } -static int cb_error_handler2(_unused struct sockaddr_nl *nla, struct nlmsgerr *err, +static int cb_proxy_neigh_error(_unused struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) { struct neigh_info *ctxt = (struct neigh_info *)arg; @@ -774,9 +774,9 @@ int netlink_get_interface_proxy_neigh(int ifindex, const struct in6_addr *addr) nlmsg_append(msg, &ndm, sizeof(ndm), 0); nla_put(msg, NDA_DST, sizeof(*addr), addr); - nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_valid_handler2, &ctxt); - nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, cb_finish_handler2, &ctxt); - nl_cb_err(cb, NL_CB_CUSTOM, cb_error_handler2, &ctxt); + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_proxy_neigh_valid, &ctxt); + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, cb_proxy_neigh_finish, &ctxt); + nl_cb_err(cb, NL_CB_CUSTOM, cb_proxy_neigh_error, &ctxt); nl_send_auto_complete(rtnl_socket, msg); while (ctxt.pending > 0) -- 2.25.1 From e76ad06d01d31fff4c482974138d2c4566e264cf Mon Sep 17 00:00:00 2001 From: Hans Dedecker Date: Mon, 23 Sep 2019 22:06:00 +0200 Subject: [PATCH 03/16] netlink: fix potential infinite loops Fix potential infinite loops by checking the return code of nl_send_auto_complete; if nl_send_auto_complete fails pending will always have the value 1 as the finish callback will not be called resulting into an infinite loop Signed-off-by: Hans Dedecker --- src/netlink.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/netlink.c b/src/netlink.c index 1a7534d..39f6245 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -655,14 +655,16 @@ ssize_t netlink_get_interface_addrs(int ifindex, bool v6, struct odhcpd_ipaddr * nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, cb_addr_finish, &ctxt); nl_cb_err(cb, NL_CB_CUSTOM, cb_addr_error, &ctxt); - nl_send_auto_complete(rtnl_socket, msg); + ctxt.ret = nl_send_auto_complete(rtnl_socket, msg); + if (ctxt.ret < 0) + goto free; + + ctxt.ret = 0; while (ctxt.pending > 0) nl_recvmsgs(rtnl_socket, cb); - nlmsg_free(msg); - if (ctxt.ret <= 0) - goto out; + goto free; time_t now = odhcpd_time(); struct odhcpd_ipaddr *addr = *addrs; @@ -677,6 +679,8 @@ ssize_t netlink_get_interface_addrs(int ifindex, bool v6, struct odhcpd_ipaddr * addr[i].valid += now; } +free: + nlmsg_free(msg); out: nl_cb_put(cb); @@ -778,12 +782,15 @@ int netlink_get_interface_proxy_neigh(int ifindex, const struct in6_addr *addr) nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, cb_proxy_neigh_finish, &ctxt); nl_cb_err(cb, NL_CB_CUSTOM, cb_proxy_neigh_error, &ctxt); - nl_send_auto_complete(rtnl_socket, msg); + ctxt.ret = nl_send_auto_complete(rtnl_socket, msg); + if (ctxt.ret < 0) + goto free; + while (ctxt.pending > 0) nl_recvmsgs(rtnl_socket, cb); +free: nlmsg_free(msg); - out: nl_cb_put(cb); -- 2.25.1 From 9a4531a4313c4b32a9ffae92c00a59a4d4b738a8 Mon Sep 17 00:00:00 2001 From: Hans Dedecker Date: Sun, 13 Oct 2019 20:37:14 +0200 Subject: [PATCH 04/16] ndp: fix endian issue Fix endian issue introduced in commit 91a28e4 by using ND_NA_FLAG_SOLICITED defined in netinet/icmp6.h Signed-off-by: Hans Dedecker --- src/ndp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ndp.c b/src/ndp.c index 3f0a037..1facceb 100644 --- a/src/ndp.c +++ b/src/ndp.c @@ -310,7 +310,7 @@ static void send_na(struct in6_addr *to_addr, memset(pbuf, 0, sizeof(pbuf)); adv->nd_na_hdr = (struct icmp6_hdr) { .icmp6_type = ND_NEIGHBOR_ADVERT, - .icmp6_dataun.icmp6_un_data32 = { 0x40000000L } + .icmp6_dataun.icmp6_un_data32 = { ND_NA_FLAG_SOLICITED } }; adv->nd_na_target = *for_addr; *opt = (struct nd_opt_hdr) { .nd_opt_type = ND_OPT_TARGET_LINKADDR, .nd_opt_len = 1 }; -- 2.25.1 From d60f0a6284e8f5fac733a1b269ea614ccdf47fcd Mon Sep 17 00:00:00 2001 From: Hans Dedecker Date: Sun, 15 Dec 2019 20:17:32 +0100 Subject: [PATCH 05/16] treewide: optimize syslog priority values Signed-off-by: Hans Dedecker --- src/dhcpv4.c | 31 ++++++++++++++++--------------- src/dhcpv6-ia.c | 6 +++--- src/dhcpv6.c | 12 ++++++------ src/ndp.c | 4 ++-- src/router.c | 2 +- 5 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/dhcpv4.c b/src/dhcpv4.c index 34d925c..1deb097 100644 --- a/src/dhcpv4.c +++ b/src/dhcpv4.c @@ -214,12 +214,12 @@ static int setup_dhcpv4_addresses(struct interface *iface) if (iface->dhcpv4_start.s_addr & htonl(0xffff0000) || iface->dhcpv4_end.s_addr & htonl(0xffff0000) || ntohl(iface->dhcpv4_start.s_addr) > ntohl(iface->dhcpv4_end.s_addr)) { - syslog(LOG_ERR, "Invalid DHCP range for %s", iface->name); + syslog(LOG_WARNING, "Invalid DHCP range for %s", iface->name); return -1; } if (!iface->addr4_len) { - syslog(LOG_ERR, "No network(s) available on %s", iface->name); + syslog(LOG_WARNING, "No network(s) available on %s", iface->name); return -1; } @@ -250,7 +250,7 @@ static int setup_dhcpv4_addresses(struct interface *iface) /* Don't allocate IP range for subnets bigger than 28 */ if (iface->addr4[0].prefix > 28) { - syslog(LOG_ERR, "Auto allocation of DHCP range fails on %s", iface->name); + syslog(LOG_WARNING, "Auto allocation of DHCP range fails on %s", iface->name); return -1; } @@ -383,7 +383,7 @@ static void handle_addrlist_change(struct interface *iface) a = list_first_entry(&iface->dhcpv4_fr_ips, struct odhcpd_ref_ip, head); if (netlink_setup_addr(&a->addr, iface->ifindex, false, true)) { - syslog(LOG_ERR, "Failed to add ip address on %s", iface->name); + syslog(LOG_WARNING, "Failed to add ip address on %s", iface->name); return; } @@ -533,7 +533,7 @@ static void dhcpv4_fr_send(struct dhcp_assignment *a) syslog(LOG_ERR, "Failed to send %s to %s - %s: %m", dhcpv4_msg_to_string(msg), odhcpd_print_mac(a->hwaddr, sizeof(a->hwaddr)), inet_ntoa(dest.sin_addr)); else - syslog(LOG_NOTICE, "Sent %s to %s - %s", dhcpv4_msg_to_string(msg), + syslog(LOG_DEBUG, "Sent %s to %s - %s", dhcpv4_msg_to_string(msg), odhcpd_print_mac(a->hwaddr, sizeof(a->hwaddr)), inet_ntoa(dest.sin_addr)); } @@ -601,10 +601,10 @@ static void handle_dhcpv4(void *addr, void *data, size_t len, req->op != DHCPV4_BOOTREQUEST || req->hlen != 6) return; - syslog(LOG_NOTICE, "Got DHCPv4 request on %s", iface->name); + syslog(LOG_DEBUG, "Got DHCPv4 request on %s", iface->name); if (!iface->dhcpv4_start_ip.s_addr && !iface->dhcpv4_end_ip.s_addr) { - syslog(LOG_ERR, "No DHCP range available on %s", iface->name); + syslog(LOG_WARNING, "No DHCP range available on %s", iface->name); return; } @@ -734,7 +734,7 @@ static void handle_dhcpv4(void *addr, void *data, size_t len, req->ciaddr.s_addr = INADDR_ANY; } - syslog(LOG_NOTICE, "Received %s from %s on %s", dhcpv4_msg_to_string(reqmsg), + syslog(LOG_INFO, "Received %s from %s on %s", dhcpv4_msg_to_string(reqmsg), odhcpd_print_mac(req->chaddr, req->hlen), iface->name); #ifdef WITH_UBUS @@ -886,7 +886,7 @@ static void handle_dhcpv4(void *addr, void *data, size_t len, "ff:ff:ff:ff:ff:ff": odhcpd_print_mac(req->chaddr, req->hlen), inet_ntoa(dest.sin_addr)); else - syslog(LOG_NOTICE, "Sent %s to %s - %s", + syslog(LOG_DEBUG, "Sent %s to %s - %s", dhcpv4_msg_to_string(msg), dest.sin_addr.s_addr == INADDR_BROADCAST ? "ff:ff:ff:ff:ff:ff": odhcpd_print_mac(req->chaddr, req->hlen), @@ -949,7 +949,7 @@ static bool dhcpv4_assign(struct interface *iface, struct dhcp_assignment *a, a, a->addr); if (assigned) - syslog(LOG_INFO, "Assigning static IP: %s", ip4toa(a->addr)); + syslog(LOG_DEBUG, "Assigning static IP: %s", ip4toa(a->addr)); return assigned; } @@ -961,7 +961,7 @@ static bool dhcpv4_assign(struct interface *iface, struct dhcp_assignment *a, a, raddr); if (assigned) { - syslog(LOG_INFO, "Assigning the IP the client asked for: %s", + syslog(LOG_DEBUG, "Assigning the IP the client asked for: %s", ip4toa(a->addr)); return true; @@ -988,14 +988,15 @@ static bool dhcpv4_assign(struct interface *iface, struct dhcp_assignment *a, a, n_try); if (assigned) { - syslog(LOG_INFO, "Assigning mapped IP: %s (try %u of %u)", + syslog(LOG_DEBUG, "Assigning mapped IP: %s (try %u of %u)", ip4toa(a->addr), i + 1, count); return true; } } - syslog(LOG_WARNING, "Can't assign any IP address -> address space is full"); + syslog(LOG_NOTICE, "Can't assign any IP address -> address space is full"); + return false; } @@ -1028,8 +1029,8 @@ dhcpv4_lease(struct interface *iface, enum dhcpv4_msg msg, const uint8_t *mac, /* Create new binding */ a = alloc_assignment(0); if (!a) { - syslog(LOG_ERR, "Failed to alloc assignment on interface %s", - iface->ifname); + syslog(LOG_WARNING, "Failed to alloc assignment on interface %s", + iface->ifname); return NULL; } memcpy(a->hwaddr, mac, sizeof(a->hwaddr)); diff --git a/src/dhcpv6-ia.c b/src/dhcpv6-ia.c index da2501f..6a09b2f 100644 --- a/src/dhcpv6-ia.c +++ b/src/dhcpv6-ia.c @@ -71,7 +71,7 @@ int dhcpv6_ia_setup_interface(struct interface *iface, bool enable) border = alloc_assignment(0); if (!border) { - syslog(LOG_ERR, "Failed to alloc border on %s", iface->name); + syslog(LOG_WARNING, "Failed to alloc border on %s", iface->name); return -1; } @@ -1065,8 +1065,8 @@ static void dhcpv6_log(uint8_t msgtype, struct interface *iface, time_t now, dhcpv6_ia_enum_addrs(iface, a, now, dhcpv6_log_ia_addr, &ctxt); } - syslog(LOG_NOTICE, "DHCPV6 %s %s from %s on %s: %s %s", type, (is_pd) ? "IA_PD" : "IA_NA", - duidbuf, iface->name, status, leasebuf); + syslog(LOG_INFO, "DHCPV6 %s %s from %s on %s: %s %s", type, (is_pd) ? "IA_PD" : "IA_NA", + duidbuf, iface->name, status, leasebuf); } static bool dhcpv6_ia_on_link(const struct dhcpv6_ia_hdr *ia, struct dhcp_assignment *a, diff --git a/src/dhcpv6.c b/src/dhcpv6.c index c61a8aa..21b95fa 100644 --- a/src/dhcpv6.c +++ b/src/dhcpv6.c @@ -246,7 +246,7 @@ static void handle_client_request(void *addr, void *data, size_t len, if (len < sizeof(*hdr)) return; - syslog(LOG_NOTICE, "Got a DHCPv6-request on %s", iface->name); + syslog(LOG_DEBUG, "Got a DHCPv6-request on %s", iface->name); /* Construct reply message */ struct __attribute__((packed)) { @@ -452,7 +452,7 @@ static void handle_client_request(void *addr, void *data, size_t len, iov[IOV_CERID].iov_len + iov[IOV_DHCPV6_RAW].iov_len - (4 + opts_end - opts)); - syslog(LOG_NOTICE, "Sending a DHCPv6-%s on %s", iov[IOV_NESTED].iov_len ? "relay-reply" : "reply", iface->name); + syslog(LOG_DEBUG, "Sending a DHCPv6-%s on %s", iov[IOV_NESTED].iov_len ? "relay-reply" : "reply", iface->name); odhcpd_send(iface->dhcpv6_event.uloop.fd, addr, iov, ARRAY_SIZE(iov), iface); } @@ -487,7 +487,7 @@ static void relay_server_response(uint8_t *data, size_t len) /* Relay DHCPv6 reply from server to client */ struct dhcpv6_relay_header *h = (void*)data; - syslog(LOG_NOTICE, "Got a DHCPv6-relay-reply"); + syslog(LOG_DEBUG, "Got a DHCPv6-relay-reply"); if (len < sizeof(*h) || h->msg_type != DHCPV6_MSG_RELAY_REPL) return; @@ -557,7 +557,7 @@ static void relay_server_response(uint8_t *data, size_t len) struct iovec iov = {payload_data, payload_len}; - syslog(LOG_NOTICE, "Sending a DHCPv6-reply on %s", iface->name); + syslog(LOG_DEBUG, "Sending a DHCPv6-reply on %s", iface->name); odhcpd_send(iface->dhcpv6_event.uloop.fd, &target, &iov, 1, iface); } @@ -608,7 +608,7 @@ static void relay_client_request(struct sockaddr_in6 *source, h->msg_type == DHCPV6_MSG_ADVERTISE) return; /* Invalid message types for client */ - syslog(LOG_NOTICE, "Got a DHCPv6-request on %s", iface->name); + syslog(LOG_DEBUG, "Got a DHCPv6-request on %s", iface->name); if (h->msg_type == DHCPV6_MSG_RELAY_FORW) { /* handle relay-forward */ if (h->hop_count >= DHCPV6_HOP_COUNT_LIMIT) @@ -649,7 +649,7 @@ static void relay_client_request(struct sockaddr_in6 *source, ip = NULL; } - syslog(LOG_NOTICE, "Sending a DHCPv6-relay-forward on %s", c->name); + syslog(LOG_DEBUG, "Sending a DHCPv6-relay-forward on %s", c->name); odhcpd_send(c->dhcpv6_event.uloop.fd, &s, iov, 2, c); } diff --git a/src/ndp.c b/src/ndp.c index 1facceb..dfbb111 100644 --- a/src/ndp.c +++ b/src/ndp.c @@ -288,7 +288,7 @@ static void ping6(struct in6_addr *addr, char ipbuf[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, addr, ipbuf, sizeof(ipbuf)); - syslog(LOG_NOTICE, "Pinging for %s on %s", ipbuf, iface->name); + syslog(LOG_DEBUG, "Pinging for %s on %s", ipbuf, iface->name); netlink_setup_route(addr, 128, iface->ifindex, NULL, 128, true); odhcpd_send(iface->ndp_ping_fd, &dest, &iov, 1, iface); @@ -381,7 +381,7 @@ static void setup_route(struct in6_addr *addr, struct interface *iface, bool add char ipbuf[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, addr, ipbuf, sizeof(ipbuf)); - syslog(LOG_NOTICE, "%s about %s%s on %s", + syslog(LOG_DEBUG, "%s about %s%s on %s", (add) ? "Learning" : "Forgetting", iface->learn_routes ? "proxy routing for " : "", ipbuf, iface->name); diff --git a/src/router.c b/src/router.c index 700e1ff..7c97672 100644 --- a/src/router.c +++ b/src/router.c @@ -600,7 +600,7 @@ static int send_router_advert(struct interface *iface, const struct in6_addr *fr } else adv.h.nd_ra_router_lifetime = 0; - syslog(LOG_INFO, "Using a RA lifetime of %d seconds on %s", ntohs(adv.h.nd_ra_router_lifetime), iface->name); + syslog(LOG_DEBUG, "Using a RA lifetime of %d seconds on %s", ntohs(adv.h.nd_ra_router_lifetime), iface->name); /* DNS options */ if (iface->ra_dns) { -- 2.25.1 From b0902af8a731179b0ab56ec32f23f2cbd9533749 Mon Sep 17 00:00:00 2001 From: Hans Dedecker Date: Tue, 31 Dec 2019 16:13:08 +0100 Subject: [PATCH 06/16] dhcpv6-ia: remove passing interface as parameter to apply_lease As the assignment struct holds a pointer to the interface struct use this one in apply_lease iso passing interface as a parameter Signed-off-by: Hans Dedecker --- src/dhcpv6-ia.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/dhcpv6-ia.c b/src/dhcpv6-ia.c index 6a09b2f..e48123f 100644 --- a/src/dhcpv6-ia.c +++ b/src/dhcpv6-ia.c @@ -415,7 +415,7 @@ void dhcpv6_ia_write_statefile(void) } } -static void __apply_lease(struct interface *iface, struct dhcp_assignment *a, +static void __apply_lease(struct dhcp_assignment *a, struct odhcpd_ipaddr *addrs, ssize_t addr_len, bool add) { if (a->length > 64) @@ -426,16 +426,17 @@ static void __apply_lease(struct interface *iface, struct dhcp_assignment *a, prefix.s6_addr32[1] |= htonl(a->assigned); prefix.s6_addr32[2] = prefix.s6_addr32[3] = 0; netlink_setup_route(&prefix, (a->managed_size) ? addrs[i].prefix : a->length, - iface->ifindex, &a->peer.sin6_addr, 1024, add); + a->iface->ifindex, &a->peer.sin6_addr, 1024, add); } } -static void apply_lease(struct interface *iface, struct dhcp_assignment *a, bool add) +static void apply_lease(struct dhcp_assignment *a, bool add) { + struct interface *iface = a->iface; struct odhcpd_ipaddr *addrs = (a->managed) ? a->managed : iface->addr6; ssize_t addrlen = (a->managed) ? a->managed_size : (ssize_t)iface->addr6_len; - __apply_lease(iface, a, addrs, addrlen, add); + __apply_lease(a, addrs, addrlen, add); } /* Set border assignment size based on the IPv6 address prefixes */ @@ -596,7 +597,7 @@ static bool assign_pd(struct interface *iface, struct dhcp_assignment *assign) list_add_tail(&assign->head, &c->head); if (assign->flags & OAF_BOUND) - apply_lease(iface, assign, true); + apply_lease(assign, true); return true; } @@ -618,7 +619,7 @@ static bool assign_pd(struct interface *iface, struct dhcp_assignment *assign) list_add_tail(&assign->head, &c->head); if (assign->flags & OAF_BOUND) - apply_lease(iface, assign, true); + apply_lease(assign, true); return true; } @@ -689,7 +690,7 @@ static void handle_addrlist_change(struct netevent_handler_info *info) list_for_each_entry(c, &iface->ia_assignments, head) { if (c != border && !(iface->ra_flags & ND_RA_FLAG_MANAGED) && (c->flags & OAF_BOUND)) - __apply_lease(iface, c, info->addrs_old.addrs, + __apply_lease(c, info->addrs_old.addrs, info->addrs_old.len, false); } @@ -703,7 +704,7 @@ static void handle_addrlist_change(struct netevent_handler_info *info) if (c->length < 128 && (c->assigned == 0 || c->assigned >= border->assigned) && c != border) list_move(&c->head, &reassign); else if (c != border && (c->flags & OAF_BOUND)) - apply_lease(iface, c, true); + apply_lease(c, true); if (c->accept_reconf && c->reconf_cnt == 0) { struct dhcp_assignment *a; @@ -1208,7 +1209,7 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac /* Reset state */ if (a->flags & OAF_BOUND) - apply_lease(iface, a, false); + apply_lease(a, false); stop_reconf(a); break; @@ -1348,7 +1349,7 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac a->accept_reconf = accept_reconf; a->flags &= ~OAF_TENTATIVE; a->flags |= OAF_BOUND; - apply_lease(iface, a, true); + apply_lease(a, true); } else if (!assigned && a && a->managed_size == 0) { /* Cleanup failed assignment */ free_assignment(a); @@ -1366,14 +1367,14 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac ia_response_len = build_ia(buf, buflen, status, ia, a, iface, false); if (a) { a->flags |= OAF_BOUND; - apply_lease(iface, a, true); + apply_lease(a, true); } } else if (hdr->msg_type == DHCPV6_MSG_RELEASE) { if (!(a->flags & OAF_STATIC)) a->valid_until = now - 1; if (a->flags & OAF_BOUND) { - apply_lease(iface, a, false); + apply_lease(a, false); a->flags &= ~OAF_BOUND; } } else if (hdr->msg_type == DHCPV6_MSG_DECLINE && a->length == 128) { -- 2.25.1 From b413d8a13ad8d8bdc2e5bc700e9bae9b31232779 Mon Sep 17 00:00:00 2001 From: Hans Dedecker Date: Tue, 31 Dec 2019 16:30:34 +0100 Subject: [PATCH 07/16] dhcpv6-ia: cleanup prefix delegation routes Remove prefix delegation routes as well in free_dhcpv6_assignment when cleaning up the assignment resources Signed-off-by: Hans Dedecker --- src/dhcpv6-ia.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/dhcpv6-ia.c b/src/dhcpv6-ia.c index e48123f..3838d54 100644 --- a/src/dhcpv6-ia.c +++ b/src/dhcpv6-ia.c @@ -40,6 +40,7 @@ (addrs)[(i)].prefix > 64) static void dhcpv6_netevent_cb(unsigned long event, struct netevent_handler_info *info); +static void apply_lease(struct dhcp_assignment *a, bool add); static void set_border_assignment_size(struct interface *iface, struct dhcp_assignment *b); static void handle_addrlist_change(struct netevent_handler_info *info); static void start_reconf(struct dhcp_assignment *a); @@ -206,6 +207,9 @@ static void dhcpv6_ia_free_assignment(struct dhcp_assignment *a) close(a->managed_sock.fd.fd); } + if (a->flags & OAF_BOUND && a->length < 128) + apply_lease(a, false); + if (a->reconf_cnt) stop_reconf(a); @@ -1370,13 +1374,13 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac apply_lease(a, true); } } else if (hdr->msg_type == DHCPV6_MSG_RELEASE) { - if (!(a->flags & OAF_STATIC)) - a->valid_until = now - 1; - - if (a->flags & OAF_BOUND) { + if (a->flags & OAF_STATIC) { apply_lease(a, false); a->flags &= ~OAF_BOUND; } + else + a->valid_until = now - 1; + } else if (hdr->msg_type == DHCPV6_MSG_DECLINE && a->length == 128) { a->flags &= ~OAF_BOUND; -- 2.25.1 From 2520c483781339f6c7feae617b7e5c0137e3534d Mon Sep 17 00:00:00 2001 From: Hans Dedecker Date: Thu, 2 Jan 2020 18:26:51 +0100 Subject: [PATCH 08/16] dhcpv6-ia: introduce DHCPv6 pd and ia assignments flags Simplify the code by using specific flags which identify the assignment either as a DHCPv6 PD or NA assignment. This allows to remove implicit checks for PD and NA assignments based on the value of the assignment length parameter. Signed-off-by: Hans Dedecker --- src/config.c | 2 ++ src/dhcpv6-ia.c | 44 +++++++++++++++++++++----------------------- src/odhcpd.h | 3 ++- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/config.c b/src/config.c index a8458a5..f3ba3e8 100644 --- a/src/config.c +++ b/src/config.c @@ -35,6 +35,8 @@ struct config config = {.legacy = false, .main_dhcpv4 = false, #define START_DEFAULT 100 #define LIMIT_DEFAULT 150 +#define OAF_DHCPV6 (OAF_DHCPV6_NA | OAF_DHCPV6_PD) + enum { IFACE_ATTR_INTERFACE, IFACE_ATTR_IFNAME, diff --git a/src/dhcpv6-ia.c b/src/dhcpv6-ia.c index 3838d54..ee0acaf 100644 --- a/src/dhcpv6-ia.c +++ b/src/dhcpv6-ia.c @@ -207,7 +207,7 @@ static void dhcpv6_ia_free_assignment(struct dhcp_assignment *a) close(a->managed_sock.fd.fd); } - if (a->flags & OAF_BOUND && a->length < 128) + if ((a->flags & OAF_BOUND) && (a->flags & OAF_DHCPV6_PD)) apply_lease(a, false); if (a->reconf_cnt) @@ -275,7 +275,7 @@ static void dhcpv6_write_ia_addr(struct in6_addr *addr, int prefix, _unused uint inet_ntop(AF_INET6, addr, ipbuf, sizeof(ipbuf) - 1); - if (ctxt->c->length == 128 && ctxt->c->hostname && + if ((ctxt->c->flags & OAF_DHCPV6_NA) && ctxt->c->hostname && !(ctxt->c->flags & OAF_BROKEN_HOSTNAME)) { fputs(ipbuf, ctxt->fp); @@ -422,7 +422,7 @@ void dhcpv6_ia_write_statefile(void) static void __apply_lease(struct dhcp_assignment *a, struct odhcpd_ipaddr *addrs, ssize_t addr_len, bool add) { - if (a->length > 64) + if (a->flags & OAF_DHCPV6_NA) return; for (ssize_t i = 0; i < addr_len; ++i) { @@ -594,7 +594,7 @@ static bool assign_pd(struct interface *iface, struct dhcp_assignment *assign) uint32_t current = 1, asize = (1 << (64 - assign->length)) - 1; if (assign->assigned) { list_for_each_entry(c, &iface->ia_assignments, head) { - if (c->length == 128 || c->length == 0) + if (c->flags & OAF_DHCPV6_NA) continue; if (assign->assigned >= current && assign->assigned + asize < c->assigned) { @@ -614,7 +614,7 @@ static bool assign_pd(struct interface *iface, struct dhcp_assignment *assign) /* Fallback to a variable assignment */ current = 1; list_for_each_entry(c, &iface->ia_assignments, head) { - if (c->length == 128 || c->length == 0) + if (c->flags & OAF_DHCPV6_NA) continue; current = (current + asize) & (~asize); @@ -643,10 +643,7 @@ static bool assign_na(struct interface *iface, struct dhcp_assignment *a) /* Preconfigured assignment by static lease */ if (a->assigned) { list_for_each_entry(c, &iface->ia_assignments, head) { - if (c->length == 0) - continue; - - if (c->assigned > a->assigned || c->length != 128) { + if (c->assigned > a->assigned || !(c->flags & OAF_DHCPV6_NA)) { list_add_tail(&a->head, &c->head); return true; } else if (c->assigned == a->assigned) @@ -668,10 +665,7 @@ static bool assign_na(struct interface *iface, struct dhcp_assignment *a) continue; list_for_each_entry(c, &iface->ia_assignments, head) { - if (c->length == 0) - continue; - - if (c->assigned > try || c->length != 128) { + if (c->assigned > try || !(c->flags & OAF_DHCPV6_NA)) { a->assigned = try; list_add_tail(&a->head, &c->head); return true; @@ -692,7 +686,7 @@ static void handle_addrlist_change(struct netevent_handler_info *info) time_t now = odhcpd_time(); list_for_each_entry(c, &iface->ia_assignments, head) { - if (c != border && !(iface->ra_flags & ND_RA_FLAG_MANAGED) + if ((c->flags & OAF_DHCPV6_PD) && !(iface->ra_flags & ND_RA_FLAG_MANAGED) && (c->flags & OAF_BOUND)) __apply_lease(c, info->addrs_old.addrs, info->addrs_old.len, false); @@ -701,13 +695,15 @@ static void handle_addrlist_change(struct netevent_handler_info *info) set_border_assignment_size(iface, border); list_for_each_entry_safe(c, d, &iface->ia_assignments, head) { - if (c->clid_len == 0 || (!INFINITE_VALID(c->valid_until) && c->valid_until < now) || - c->managed_size) + if (c->clid_len == 0 || + !(c->flags & OAF_DHCPV6_PD) || + (!INFINITE_VALID(c->valid_until) && c->valid_until < now) || + c->managed_size) continue; - if (c->length < 128 && (c->assigned == 0 || c->assigned >= border->assigned) && c != border) + if (c->assigned == 0 || c->assigned >= border->assigned) list_move(&c->head, &reassign); - else if (c != border && (c->flags & OAF_BOUND)) + else if (c->flags & OAF_BOUND) apply_lease(c, true); if (c->accept_reconf && c->reconf_cnt == 0) { @@ -849,7 +845,7 @@ static size_t build_ia(uint8_t *buf, size_t buflen, uint16_t status, if (prefix_valid != UINT32_MAX) prefix_valid -= now; - if (a->length < 128) { + if (a->flags & OAF_DHCPV6_PD) { struct dhcpv6_ia_prefix o_ia_p = { .type = htons(DHCPV6_OPT_IA_PREFIX), .len = htons(sizeof(o_ia_p) - 4), @@ -871,7 +867,9 @@ static size_t build_ia(uint8_t *buf, size_t buflen, uint16_t status, memcpy(buf + ia_len, &o_ia_p, sizeof(o_ia_p)); ia_len += sizeof(o_ia_p); - } else { + } + + if (a->flags & OAF_DHCPV6_NA) { struct dhcpv6_ia_addr o_ia_a = { .type = htons(DHCPV6_OPT_IA_ADDR), .len = htons(sizeof(o_ia_a) - 4), @@ -1208,7 +1206,7 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac list_for_each_entry(c, &iface->ia_assignments, head) { if ((c->clid_len == clid_len && !memcmp(c->clid_data, clid_data, clid_len)) && c->iaid == ia->iaid && (INFINITE_VALID(c->valid_until) || now < c->valid_until) && - ((is_pd && c->length <= 64) || (is_na && c->length == 128))) { + ((is_pd && (c->flags & OAF_DHCPV6_PD)) || (is_na && (c->flags & OAF_DHCPV6_NA)))) { a = c; /* Reset state */ @@ -1253,7 +1251,7 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac a->valid_until = l ? 0 : now; a->dhcp_free_cb = dhcpv6_ia_free_assignment; a->iface = iface; - a->flags = OAF_DHCPV6; + a->flags = (is_pd ? OAF_DHCPV6_PD : OAF_DHCPV6_NA); if (first) memcpy(a->key, first->key, sizeof(a->key)); @@ -1381,7 +1379,7 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac else a->valid_until = now - 1; - } else if (hdr->msg_type == DHCPV6_MSG_DECLINE && a->length == 128) { + } else if ((a->flags & OAF_DHCPV6_NA) && hdr->msg_type == DHCPV6_MSG_DECLINE) { a->flags &= ~OAF_BOUND; if (!(a->flags & OAF_STATIC)) { diff --git a/src/odhcpd.h b/src/odhcpd.h index 01c9ad7..ae4826e 100644 --- a/src/odhcpd.h +++ b/src/odhcpd.h @@ -129,7 +129,8 @@ enum odhcpd_assignment_flags { OAF_STATIC = (1 << 2), OAF_BROKEN_HOSTNAME = (1 << 3), OAF_DHCPV4 = (1 << 4), - OAF_DHCPV6 = (1 << 5), + OAF_DHCPV6_NA = (1 << 5), + OAF_DHCPV6_PD = (1 << 6), }; struct config { -- 2.25.1 From 6db312a698e920ff61505ef1f42469880829774d Mon Sep 17 00:00:00 2001 From: Hans Dedecker Date: Tue, 14 Jan 2020 21:16:48 +0100 Subject: [PATCH 09/16] dhcpv6-ia: use dhcp leasetime to set preferred/valid statefull lifetimes Allow to set the preferred/valid lifetimes of IA_NA/IA_PD options based on the configured dhcp leasetime. DHCP leqasetime will be used to set the preferred/valid lifetimes in the IA_NA/IA_PD options unless the preferred/valid lifetimes of the IPv6 address are smaller then the DHCP leasetime. This will avoid IA_NA/IA_PD options being sent with infinite lifetimes due to the IPv6 address having infinite preferred/valid lifetimes like IPv6 ULA addresses. While at it rename dhcpv4_leasetime into dhcp_leasetime as the leasetime is used both for DHCPv4 and DHCPv6 Signed-off-by: Hans Dedecker --- src/config.c | 4 ++-- src/dhcpv4.c | 2 +- src/dhcpv6-ia.c | 8 +++++++- src/odhcpd.h | 4 +++- src/router.c | 8 ++++---- 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/config.c b/src/config.c index f3ba3e8..3d41e13 100644 --- a/src/config.c +++ b/src/config.c @@ -229,7 +229,7 @@ static void set_interface_defaults(struct interface *iface) iface->ra = MODE_DISABLED; iface->ndp = MODE_DISABLED; iface->learn_routes = 1; - iface->dhcpv4_leasetime = 43200; + iface->dhcp_leasetime = 43200; iface->dhcpv4_start.s_addr = htonl(START_DEFAULT); iface->dhcpv4_end.s_addr = htonl(START_DEFAULT + LIMIT_DEFAULT - 1); iface->dhcpv6_assignall = true; @@ -547,7 +547,7 @@ int config_parse_interface(void *data, size_t len, const char *name, bool overwr if (time < 0) goto err; - iface->dhcpv4_leasetime = time; + iface->dhcp_leasetime = time; } if ((c = tb[IFACE_ATTR_START])) { diff --git a/src/dhcpv4.c b/src/dhcpv4.c index 1deb097..24a4fea 100644 --- a/src/dhcpv4.c +++ b/src/dhcpv4.c @@ -1072,7 +1072,7 @@ dhcpv4_lease(struct interface *iface, enum dhcpv4_msg msg, const uint8_t *mac, if (a->leasetime) my_leasetime = a->leasetime; else - my_leasetime = iface->dhcpv4_leasetime; + my_leasetime = iface->dhcp_leasetime; if ((*leasetime == 0) || (my_leasetime < *leasetime)) *leasetime = my_leasetime; diff --git a/src/dhcpv6-ia.c b/src/dhcpv6-ia.c index ee0acaf..d8187ff 100644 --- a/src/dhcpv6-ia.c +++ b/src/dhcpv6-ia.c @@ -823,7 +823,7 @@ static size_t build_ia(uint8_t *buf, size_t buflen, uint16_t status, if (a->leasetime) leasetime = a->leasetime; else - leasetime = iface->dhcpv4_leasetime; + leasetime = iface->dhcp_leasetime; uint32_t pref = leasetime; uint32_t valid = leasetime; @@ -842,9 +842,15 @@ static size_t build_ia(uint8_t *buf, size_t buflen, uint16_t status, if (prefix_pref != UINT32_MAX) prefix_pref -= now; + if (prefix_pref > leasetime) + prefix_pref = leasetime; + if (prefix_valid != UINT32_MAX) prefix_valid -= now; + if (prefix_valid > leasetime) + prefix_valid = leasetime; + if (a->flags & OAF_DHCPV6_PD) { struct dhcpv6_ia_prefix o_ia_p = { .type = htons(DHCPV6_OPT_IA_PREFIX), diff --git a/src/odhcpd.h b/src/odhcpd.h index ae4826e..072a148 100644 --- a/src/odhcpd.h +++ b/src/odhcpd.h @@ -272,6 +272,9 @@ struct interface { uint32_t ra_hoplimit; int ra_mtu; + // DHCP + uint32_t dhcp_leasetime; + // DHCPv4 struct in_addr dhcpv4_start; struct in_addr dhcpv4_end; @@ -284,7 +287,6 @@ struct interface { size_t dhcpv4_router_cnt; struct in_addr *dhcpv4_dns; size_t dhcpv4_dns_cnt; - uint32_t dhcpv4_leasetime; bool dhcpv4_forcereconf; // DNS diff --git a/src/router.c b/src/router.c index 7c97672..295bdfb 100644 --- a/src/router.c +++ b/src/router.c @@ -552,13 +552,13 @@ static int send_router_advert(struct interface *iface, const struct in6_addr *fr preferred = TIME_LEFT(addr->preferred, now); if (iface->ra_useleasetime && - preferred > iface->dhcpv4_leasetime) - preferred = iface->dhcpv4_leasetime; + preferred > iface->dhcp_leasetime) + preferred = iface->dhcp_leasetime; } valid = TIME_LEFT(addr->valid, now); - if (iface->ra_useleasetime && valid > iface->dhcpv4_leasetime) - valid = iface->dhcpv4_leasetime; + if (iface->ra_useleasetime && valid > iface->dhcp_leasetime) + valid = iface->dhcp_leasetime; if (minvalid > valid) minvalid = valid; -- 2.25.1 From bb07fa4b6d0d48cb2e67a4e9979412af8b05e883 Mon Sep 17 00:00:00 2001 From: Hans Dedecker Date: Sun, 16 Feb 2020 21:15:28 +0100 Subject: [PATCH 10/16] dhcpv4: avoid setting lifetime to infinite for static assignments Don't set the valid lifetime to infinite for static assignments but rather set it to the leasetime given to the client. This makes it possible to display the leasetime for static assigments and simplifies the code in several places Signed-off-by: Hans Dedecker --- src/dhcpv4.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/dhcpv4.c b/src/dhcpv4.c index 24a4fea..51236d2 100644 --- a/src/dhcpv4.c +++ b/src/dhcpv4.c @@ -1081,8 +1081,7 @@ dhcpv4_lease(struct interface *iface, enum dhcpv4_msg msg, const uint8_t *mac, a->flags &= ~OAF_BOUND; *incl_fr_opt = accept_fr_nonce; - if (!(a->flags & OAF_STATIC)) - a->valid_until = now; + a->valid_until = now; } else { if ((!(a->flags & OAF_STATIC) || !a->hostname) && hostname_len > 0) { a->hostname = realloc(a->hostname, hostname_len + 1); @@ -1113,8 +1112,7 @@ dhcpv4_lease(struct interface *iface, enum dhcpv4_msg msg, const uint8_t *mac, } else *incl_fr_opt = false; - if (!(a->flags & OAF_STATIC)) - a->valid_until = ((*leasetime == UINT32_MAX) ? 0 : (time_t)(now + *leasetime)); + a->valid_until = ((*leasetime == UINT32_MAX) ? 0 : (time_t)(now + *leasetime)); } } else if (!assigned && a) { /* Cleanup failed assignment */ @@ -1124,17 +1122,16 @@ dhcpv4_lease(struct interface *iface, enum dhcpv4_msg msg, const uint8_t *mac, } else if (msg == DHCPV4_MSG_RELEASE && a) { a->flags &= ~OAF_BOUND; - - if (!(a->flags & OAF_STATIC)) - a->valid_until = now - 1; + a->valid_until = now - 1; } else if (msg == DHCPV4_MSG_DECLINE && a) { a->flags &= ~OAF_BOUND; - if (!(a->flags & OAF_STATIC)) { + if (!(a->flags & OAF_STATIC) || a->lease->ipaddr != a->addr) { memset(a->hwaddr, 0, sizeof(a->hwaddr)); a->valid_until = now + 3600; /* Block address for 1h */ - } + } else + a->valid_until = now - 1; } dhcpv6_ia_write_statefile(); -- 2.25.1 From a90cc2e147b0f6e429c4e5e3e4e42e00b01ec32c Mon Sep 17 00:00:00 2001 From: Hans Dedecker Date: Sun, 16 Feb 2020 21:27:42 +0100 Subject: [PATCH 11/16] dhcpv6-ia: avoid setting lifetime to infinite for static assignments Don't set the valid lifetime to infinite for static assignments but rather set it to the IA lifetime given to the client. This makes it possible to display the leasetime for static assigments and simplifies the code in several places Signed-off-by: Hans Dedecker --- src/dhcpv6-ia.c | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/src/dhcpv6-ia.c b/src/dhcpv6-ia.c index d8187ff..836c111 100644 --- a/src/dhcpv6-ia.c +++ b/src/dhcpv6-ia.c @@ -529,7 +529,7 @@ static void managed_handle_pd_data(struct ustream *s, _unused int bytes_new) if (first && c->managed_size == 0) free_assignment(c); - else if (first && !(c->flags & OAF_STATIC)) + else if (first) c->valid_until = now + 150; } @@ -540,8 +540,7 @@ static void managed_handle_pd_done(struct ustream *s) struct ustream_fd *fd = container_of(s, struct ustream_fd, stream); struct dhcp_assignment *c = container_of(fd, struct dhcp_assignment, managed_sock); - if (!(c->flags & OAF_STATIC)) - c->valid_until = odhcpd_time() + 15; + c->valid_until = odhcpd_time() + 15; c->managed_size = 0; @@ -569,8 +568,7 @@ static bool assign_pd(struct interface *iface, struct dhcp_assignment *assign) ustream_write_pending(&assign->managed_sock.stream); assign->managed_size = -1; - if (!(assign->flags & OAF_STATIC)) - assign->valid_until = odhcpd_time() + 15; + assign->valid_until = odhcpd_time() + 15; list_add(&assign->head, &iface->ia_assignments); @@ -1252,9 +1250,7 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac a->length = reqlen; a->peer = *addr; a->assigned = is_na && l ? l->hostid : reqhint; - /* Set valid time to 0 for static lease indicating */ - /* infinite lifetime otherwise current time */ - a->valid_until = l ? 0 : now; + a->valid_until = now; a->dhcp_free_cb = dhcpv6_ia_free_assignment; a->iface = iface; a->flags = (is_pd ? OAF_DHCPV6_PD : OAF_DHCPV6_NA); @@ -1334,9 +1330,8 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac a->flags &= ~OAF_BOUND; a->flags |= OAF_TENTATIVE; - if (!(a->flags & OAF_STATIC)) - /* Keep tentative assignment around for 60 seconds */ - a->valid_until = now + 60; + /* Keep tentative assignment around for 60 seconds */ + a->valid_until = now + 60; } else if (assigned && ((hdr->msg_type == DHCPV6_MSG_SOLICIT && rapid_commit) || @@ -1378,20 +1373,15 @@ ssize_t dhcpv6_ia_handle_IAs(uint8_t *buf, size_t buflen, struct interface *ifac apply_lease(a, true); } } else if (hdr->msg_type == DHCPV6_MSG_RELEASE) { - if (a->flags & OAF_STATIC) { - apply_lease(a, false); - a->flags &= ~OAF_BOUND; - } - else - a->valid_until = now - 1; - + a->valid_until = now - 1; } else if ((a->flags & OAF_DHCPV6_NA) && hdr->msg_type == DHCPV6_MSG_DECLINE) { a->flags &= ~OAF_BOUND; - if (!(a->flags & OAF_STATIC)) { + if (!(a->flags & OAF_STATIC) || a->lease->hostid != a->assigned) { memset(a->clid_data, 0, a->clid_len); a->valid_until = now + 3600; /* Block address for 1h */ - } + } else + a->valid_until = now - 1; } } else if (hdr->msg_type == DHCPV6_MSG_CONFIRM) { if (ia_addr_present && !dhcpv6_ia_on_link(ia, a, iface)) { -- 2.25.1 From 6594c6b182a9065570316cf3a7a6a72a7afda150 Mon Sep 17 00:00:00 2001 From: Hans Dedecker Date: Sat, 14 Mar 2020 21:03:11 +0100 Subject: [PATCH 12/16] ubus: use dhcpv6 ia assignment flag Further align the code to use DHCPv6 assignment flags to distinguish between prefix delegation and non temporary address assignments Signed-off-by: Hans Dedecker --- src/dhcpv6-ia.c | 2 +- src/ubus.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dhcpv6-ia.c b/src/dhcpv6-ia.c index 836c111..1a13945 100644 --- a/src/dhcpv6-ia.c +++ b/src/dhcpv6-ia.c @@ -234,7 +234,7 @@ void dhcpv6_ia_enum_addrs(struct interface *iface, struct dhcp_assignment *c, addr = addrs[i].addr.in6; pref = addrs[i].preferred; valid = addrs[i].valid; - if (prefix == 128) { + if (c->flags & OAF_DHCPV6_NA) { if (!ADDR_ENTRY_VALID_IA_ADDR(iface, i, m, addrs)) continue; diff --git a/src/ubus.c b/src/ubus.c index e9c5b6f..72d47c4 100644 --- a/src/ubus.c +++ b/src/ubus.c @@ -155,7 +155,7 @@ static int handle_dhcpv6_leases(_unused struct ubus_context *ctx, _unused struct blobmsg_add_string(&b, NULL, "static"); blobmsg_close_array(&b, m); - m = blobmsg_open_array(&b, a->length == 128 ? "ipv6-addr": "ipv6-prefix"); + m = blobmsg_open_array(&b, a->flags & OAF_DHCPV6_NA ? "ipv6-addr": "ipv6-prefix"); dhcpv6_ia_enum_addrs(iface, a, now, dhcpv6_blobmsg_ia_addr, NULL); blobmsg_close_table(&b, m); -- 2.25.1 From fbf14aee4ad321ac3702b60ff8193d82812c2270 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Thu, 26 Mar 2020 16:07:12 -0700 Subject: [PATCH 13/16] odhcpd: fix compilation with musl 1.2.0 SYS_clock_gettime is gone with musl 1.2.0. Switched to the function. Also fixed two format strings that fail as time_t is 64-bit with 1.2.0. Signed-off-by: Rosen Penev --- src/dhcpv6-ia.c | 8 ++++---- src/odhcpd.c | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/dhcpv6-ia.c b/src/dhcpv6-ia.c index 1a13945..b51649c 100644 --- a/src/dhcpv6-ia.c +++ b/src/dhcpv6-ia.c @@ -338,12 +338,12 @@ void dhcpv6_ia_write_statefile(void) odhcpd_hexlify(duidbuf, ctxt.c->clid_data, ctxt.c->clid_len); /* iface DUID iaid hostname lifetime assigned length [addrs...] */ - ctxt.buf_idx = snprintf(ctxt.buf, ctxt.buf_len, "# %s %s %x %s%s %ld %x %u ", + ctxt.buf_idx = snprintf(ctxt.buf, ctxt.buf_len, "# %s %s %x %s%s %"PRId64" %x %u ", ctxt.iface->ifname, duidbuf, ntohl(ctxt.c->iaid), (ctxt.c->flags & OAF_BROKEN_HOSTNAME) ? "broken\\x20" : "", (ctxt.c->hostname ? ctxt.c->hostname : "-"), (ctxt.c->valid_until > now ? - (ctxt.c->valid_until - now + wall_time) : + (int64_t)(ctxt.c->valid_until - now + wall_time) : (INFINITE_VALID(ctxt.c->valid_until) ? -1 : 0)), ctxt.c->assigned, (unsigned)ctxt.c->length); @@ -368,12 +368,12 @@ void dhcpv6_ia_write_statefile(void) odhcpd_hexlify(duidbuf, c->hwaddr, sizeof(c->hwaddr)); /* iface DUID iaid hostname lifetime assigned length [addrs...] */ - ctxt.buf_idx = snprintf(ctxt.buf, ctxt.buf_len, "# %s %s ipv4 %s%s %ld %x 32 ", + ctxt.buf_idx = snprintf(ctxt.buf, ctxt.buf_len, "# %s %s ipv4 %s%s %"PRId64" %x 32 ", ctxt.iface->ifname, duidbuf, (c->flags & OAF_BROKEN_HOSTNAME) ? "broken\\x20" : "", (c->hostname ? c->hostname : "-"), (c->valid_until > now ? - (c->valid_until - now + wall_time) : + (int64_t)(c->valid_until - now + wall_time) : (INFINITE_VALID(c->valid_until) ? -1 : 0)), ntohl(c->addr)); diff --git a/src/odhcpd.c b/src/odhcpd.c index 4b8e589..26094b1 100644 --- a/src/odhcpd.c +++ b/src/odhcpd.c @@ -440,7 +440,7 @@ int odhcpd_urandom(void *data, size_t len) time_t odhcpd_time(void) { struct timespec ts; - syscall(SYS_clock_gettime, CLOCK_MONOTONIC, &ts); + clock_gettime(CLOCK_MONOTONIC, &ts); return ts.tv_sec; } -- 2.25.1 From f61964c66be050716089d31f13688c1ab382f0b7 Mon Sep 17 00:00:00 2001 From: Hans Dedecker Date: Thu, 2 Apr 2020 21:26:57 +0200 Subject: [PATCH 14/16] dhcpv6-ia: fix preferred and valid lifetimes in ubus ipv6leases Since commit 6db312a698e920ff61505ef1f42469880829774d the preferred and valid lifetimes of the addresses/prefixes is based on the configured leasetime; as a result the displayed preferred and valid lifetimes need to be calculated based on the assignment lifetime as this is set to the lowest valid lifetime of the addresses/prefixes. Signed-off-by: Hans Dedecker --- src/dhcpv6-ia.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/dhcpv6-ia.c b/src/dhcpv6-ia.c index b51649c..38a901b 100644 --- a/src/dhcpv6-ia.c +++ b/src/dhcpv6-ia.c @@ -247,9 +247,15 @@ void dhcpv6_ia_enum_addrs(struct interface *iface, struct dhcp_assignment *c, addr.s6_addr32[2] = addr.s6_addr32[3] = 0; } + if (pref > (uint32_t)c->valid_until) + pref = c->valid_until; + if (pref != UINT32_MAX) pref -= now; + if (valid > (uint32_t)c->valid_until) + valid = c->valid_until; + if (valid != UINT32_MAX) valid -= now; -- 2.25.1 From 5ce077026b991f49d96464587386f93d92f56385 Mon Sep 17 00:00:00 2001 From: Hans Dedecker Date: Sun, 3 May 2020 21:17:53 +0200 Subject: [PATCH 15/16] router: fix Lan host reachibility due to identical RIO and PIO prefixes (FS#3056) odhcpd includes RIO RA options according to requirement L3 in RFC7084. However if the delegated prefix length received on the wan is equal to the downstream delegated prefix length on the Lan this may pollute the routing table of type C hosts as the RIO routing entry can take precedence of the PIO routing entry meaning all traffic for the on link hosts will go via the router iso direct on link communication. If the traffic is dropped in the router hosts are unreachable; therefore don't include RIO options with prefixes and prefix length identical to those in a PIO RA option Signed-off-by: Hans Dedecker --- src/router.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/router.c b/src/router.c index 295bdfb..7198ee9 100644 --- a/src/router.c +++ b/src/router.c @@ -659,12 +659,22 @@ static int send_router_advert(struct interface *iface, const struct in6_addr *fr iov[IOV_RA_SEARCH].iov_base = (char *)search; iov[IOV_RA_SEARCH].iov_len = search_sz; + /* + * RFC7084 § 4.3 : + * L-3: An IPv6 CE router MUST advertise itself as a router for the + * delegated prefix(es) (and ULA prefix if configured to provide + * ULA addressing) using the "Route Information Option" specified + * in Section 2.3 of [RFC4191]. This advertisement is + * independent of having or not having IPv6 connectivity on the + * WAN interface. + */ + for (ssize_t i = 0; i < addr_cnt; ++i) { struct odhcpd_ipaddr *addr = &addrs[i]; struct nd_opt_route_info *tmp; uint32_t valid; - if (addr->dprefix > 64 || addr->dprefix == 0 || addr->valid <= (uint32_t)now) { + if (addr->dprefix >= 64 || addr->dprefix == 0 || addr->valid <= (uint32_t)now) { syslog(LOG_INFO, "Address %s (dprefix %d, valid %u) not suitable as RA route on %s", inet_ntop(AF_INET6, &addr->addr.in6, buf, sizeof(buf)), addr->dprefix, addr->valid, iface->name); -- 2.25.1 From 5da52992a3926b36e53d0a5d55955a6ccbc35872 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 21 Jun 2020 21:37:36 -0700 Subject: [PATCH 16/16] odhcpd: fix compilation with GCC10 GCC10 mandates the C++ one definition rule, which breaks on multiple definitions of config. Add the appropriate extern declaration. Signed-off-by: Rosen Penev Signed-off-by: Hans Dedecker --- src/odhcpd.c | 2 -- src/odhcpd.h | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/odhcpd.c b/src/odhcpd.c index 26094b1..39e0e51 100644 --- a/src/odhcpd.c +++ b/src/odhcpd.c @@ -43,8 +43,6 @@ #include #include "odhcpd.h" - - static int ioctl_sock = -1; static int urandom_fd = -1; diff --git a/src/odhcpd.h b/src/odhcpd.h index 072a148..09013c4 100644 --- a/src/odhcpd.h +++ b/src/odhcpd.h @@ -44,6 +44,7 @@ struct interface; struct nl_sock; extern struct vlist_tree leases; +extern struct config config; struct odhcpd_event { struct uloop_fd uloop; @@ -139,7 +140,7 @@ struct config { char *dhcp_cb; char *dhcp_statefile; int log_level; -} config; +}; struct lease { -- 2.25.1