From 5dad295c283a8ab8101d58ec3d8ead49a3a62a19 Mon Sep 17 00:00:00 2001 From: Hans Dedecker Date: Wed, 28 Jun 2017 15:40:05 +0200 Subject: [PATCH] treewide: rework code to get rid of fixed IPv6 address arrays Rework code to get rid of RELAYD_MAX_PREFIXES and RELAYD_MAX_ADDRS by using dynamic IPv6 address array allocation. Signed-off-by: Hans Dedecker --- src/config.c | 1 + src/dhcpv6.c | 16 ++++++++------- src/ndp.c | 7 ++++--- src/odhcpd.c | 30 +++++++++++++++------------ src/odhcpd.h | 6 ++---- src/router.c | 58 ++++++++++++++++++++++++++++++++++++---------------- 6 files changed, 73 insertions(+), 45 deletions(-) diff --git a/src/config.c b/src/config.c index 9ce20ef..1276b6f 100644 --- a/src/config.c +++ b/src/config.c @@ -237,6 +237,7 @@ static void close_interface(struct interface *iface) setup_dhcpv4_interface(iface, false); clean_interface(iface); + free(iface->ia_addr); free(iface->ifname); free(iface); } diff --git a/src/dhcpv6.c b/src/dhcpv6.c index ebb0c80..3128968 100644 --- a/src/dhcpv6.c +++ b/src/dhcpv6.c @@ -335,14 +335,15 @@ static void handle_client_request(void *addr, void *data, size_t len, iov[IOV_CERID].iov_len = sizeof(cerid); if (IN6_IS_ADDR_UNSPECIFIED(&cerid.addr)) { - struct odhcpd_ipaddr addrs[32]; - ssize_t len = odhcpd_get_interface_addresses(0, addrs, - ARRAY_SIZE(addrs)); + struct odhcpd_ipaddr *addrs; + ssize_t len = odhcpd_get_interface_addresses(0, &addrs); for (ssize_t i = 0; i < len; ++i) if (IN6_IS_ADDR_UNSPECIFIED(&cerid.addr) || memcmp(&addrs[i].addr, &cerid.addr, sizeof(cerid.addr)) < 0) cerid.addr = addrs[i].addr; + + free(addrs); } #endif } @@ -521,17 +522,18 @@ static void relay_client_request(struct sockaddr_in6 *source, memcpy(&hdr.interface_id_data, &ifindex, sizeof(ifindex)); // Detect public IP of slave interface to use as link-address - struct odhcpd_ipaddr ip; - if (odhcpd_get_interface_addresses(iface->ifindex, &ip, 1) < 1) { + struct odhcpd_ipaddr *ip = NULL; + if (odhcpd_get_interface_addresses(iface->ifindex, &ip) < 1) { // No suitable address! Is the slave not configured yet? // Detect public IP of master interface and use it instead // This is WRONG and probably violates the RFC. However // otherwise we have a hen and egg problem because the // slave-interface cannot be auto-configured. - if (odhcpd_get_interface_addresses(master->ifindex, &ip, 1) < 1) + if (odhcpd_get_interface_addresses(master->ifindex, &ip) < 1) return; // Could not obtain a suitable address } - memcpy(&hdr.link_address, &ip.addr, sizeof(hdr.link_address)); + memcpy(&hdr.link_address, &ip[0].addr, sizeof(hdr.link_address)); + free(ip); struct sockaddr_in6 dhcpv6_servers = {AF_INET6, htons(DHCPV6_SERVER_PORT), 0, ALL_DHCPV6_SERVERS, 0}; diff --git a/src/ndp.c b/src/ndp.c index b686262..8488fc5 100644 --- a/src/ndp.c +++ b/src/ndp.c @@ -352,9 +352,9 @@ static int prefixcmp(const void *va, const void *vb) // Check address update static void check_addr_updates(struct interface *iface) { - struct odhcpd_ipaddr addr[RELAYD_MAX_ADDRS] = {{IN6ADDR_ANY_INIT, 0, 0, 0, 0}}; + struct odhcpd_ipaddr *addr = NULL; time_t now = odhcpd_time(); - ssize_t len = odhcpd_get_interface_addresses(iface->ifindex, addr, ARRAY_SIZE(addr)); + ssize_t len = odhcpd_get_interface_addresses(iface->ifindex, &addr); if (len < 0) return; @@ -380,7 +380,8 @@ static void check_addr_updates(struct interface *iface) if (change) dhcpv6_ia_preupdate(iface); - memcpy(iface->ia_addr, addr, len * sizeof(*addr)); + free(iface->ia_addr); + iface->ia_addr = addr; iface->ia_addr_len = len; if (change) diff --git a/src/odhcpd.c b/src/odhcpd.c index 8a1c66e..ee628a5 100644 --- a/src/odhcpd.c +++ b/src/odhcpd.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -221,8 +222,7 @@ ssize_t odhcpd_send(int socket, struct sockaddr_in6 *dest, struct addr_info { int ifindex; - struct odhcpd_ipaddr *addrs; - size_t addrs_sz; + struct odhcpd_ipaddr **addrs; int pending; ssize_t ret; }; @@ -230,11 +230,12 @@ struct addr_info { static int cb_valid_handler(struct nl_msg *msg, void *arg) { struct addr_info *ctxt = (struct addr_info *)arg; + struct odhcpd_ipaddr *addrs = *(ctxt->addrs); struct nlmsghdr *hdr = nlmsg_hdr(msg); struct ifaddrmsg *ifa; struct nlattr *nla[__IFA_MAX]; - if (hdr->nlmsg_type != RTM_NEWADDR || ctxt->ret >= (ssize_t)ctxt->addrs_sz) + if (hdr->nlmsg_type != RTM_NEWADDR) return NL_SKIP; ifa = NLMSG_DATA(hdr); @@ -246,23 +247,28 @@ static int cb_valid_handler(struct nl_msg *msg, void *arg) if (!nla[IFA_ADDRESS]) return NL_SKIP; - memset(&ctxt->addrs[ctxt->ret], 0, sizeof(ctxt->addrs[ctxt->ret])); - ctxt->addrs[ctxt->ret].prefix = ifa->ifa_prefixlen; + addrs = realloc(addrs, sizeof(*addrs)*(ctxt->ret + 1)); + if (!addrs) + return NL_SKIP; + + memset(&addrs[ctxt->ret], 0, sizeof(addrs[ctxt->ret])); + addrs[ctxt->ret].prefix = ifa->ifa_prefixlen; - nla_memcpy(&ctxt->addrs[ctxt->ret].addr, nla[IFA_ADDRESS], - sizeof(ctxt->addrs[ctxt->ret].addr)); + nla_memcpy(&addrs[ctxt->ret].addr, nla[IFA_ADDRESS], + sizeof(addrs[ctxt->ret].addr)); if (nla[IFA_CACHEINFO]) { struct ifa_cacheinfo *ifc = nla_data(nla[IFA_CACHEINFO]); - ctxt->addrs[ctxt->ret].preferred = ifc->ifa_prefered; - ctxt->addrs[ctxt->ret].valid = ifc->ifa_valid; + addrs[ctxt->ret].preferred = ifc->ifa_prefered; + addrs[ctxt->ret].valid = ifc->ifa_valid; } if (ifa->ifa_flags & IFA_F_DEPRECATED) - ctxt->addrs[ctxt->ret].preferred = 0; + addrs[ctxt->ret].preferred = 0; ctxt->ret++; + *(ctxt->addrs) = addrs; return NL_OK; } @@ -288,8 +294,7 @@ static int cb_error_handler(_unused struct sockaddr_nl *nla, struct nlmsgerr *er } // Detect an IPV6-address currently assigned to the given interface -ssize_t odhcpd_get_interface_addresses(int ifindex, - struct odhcpd_ipaddr *addrs, size_t cnt) +ssize_t odhcpd_get_interface_addresses(int ifindex, struct odhcpd_ipaddr **addrs) { struct nl_msg *msg; struct ifaddrmsg ifa = { @@ -302,7 +307,6 @@ ssize_t odhcpd_get_interface_addresses(int ifindex, struct addr_info ctxt = { .ifindex = ifindex, .addrs = addrs, - .addrs_sz = cnt, .ret = 0, .pending = 1, }; diff --git a/src/odhcpd.h b/src/odhcpd.h index 93adca6..8a196ea 100644 --- a/src/odhcpd.h +++ b/src/odhcpd.h @@ -41,8 +41,6 @@ #define ND_OPT_DNS_SEARCH 31 #define RELAYD_BUFFER_SIZE 8192 -#define RELAYD_MAX_PREFIXES 8 -#define RELAYD_MAX_ADDRS 8 #define INFINITE_VALID(x) ((x) == 0) @@ -123,7 +121,7 @@ struct interface { // Runtime data struct uloop_timeout timer_rs; struct list_head ia_assignments; - struct odhcpd_ipaddr ia_addr[RELAYD_MAX_ADDRS]; + struct odhcpd_ipaddr *ia_addr; size_t ia_addr_len; // DHCPv4 @@ -206,7 +204,7 @@ ssize_t odhcpd_send(int socket, struct sockaddr_in6 *dest, struct iovec *iov, size_t iov_len, const struct interface *iface); ssize_t odhcpd_get_interface_addresses(int ifindex, - struct odhcpd_ipaddr *addrs, size_t cnt); + struct odhcpd_ipaddr **addrs); int odhcpd_get_interface_dns_addr(const struct interface *iface, struct in6_addr *addr); struct interface* odhcpd_get_interface_by_name(const char *name); diff --git a/src/router.c b/src/router.c index f08a7b0..85ca6b0 100644 --- a/src/router.c +++ b/src/router.c @@ -38,7 +38,7 @@ static void sigusr1_refresh(int signal); static struct odhcpd_event router_event = {.uloop = {.fd = -1}, .handle_dgram = handle_icmpv6, }; static FILE *fp_route = NULL; -#define RA_IOV_LEN 6 +#define RA_IOV_LEN 7 #define TIME_LEFT(t1, now) ((t1) != UINT32_MAX ? (t1) - (now) : UINT32_MAX) @@ -275,7 +275,6 @@ static uint64_t send_router_advert(struct interface *iface, const struct in6_add struct nd_router_advert h; struct icmpv6_opt lladdr; struct nd_opt_mtu mtu; - struct nd_opt_prefix_info prefix[sizeof(iface->ia_addr) / sizeof(*iface->ia_addr)]; } adv = { .h = {{.icmp6_type = ND_ROUTER_ADVERT, .icmp6_code = 0}, 0, 0}, .lladdr = {ND_OPT_SOURCE_LINKADDR, 1, {0}}, @@ -302,7 +301,7 @@ static uint64_t send_router_advert(struct interface *iface, const struct in6_add odhcpd_get_mac(iface, adv.lladdr.data); // If not currently shutting down - struct odhcpd_ipaddr addrs[RELAYD_MAX_ADDRS]; + struct odhcpd_ipaddr *addrs = NULL; ssize_t ipcnt = 0; uint32_t minvalid = UINT32_MAX; bool default_route = false; @@ -310,8 +309,11 @@ static uint64_t send_router_advert(struct interface *iface, const struct in6_add // If not shutdown if (iface->timer_rs.cb) { + size_t size = sizeof(*addrs) * iface->ia_addr_len; + addrs = alloca(size); + memcpy(addrs, iface->ia_addr, size); + ipcnt = iface->ia_addr_len; - memcpy(addrs, iface->ia_addr, ipcnt * sizeof(*addrs)); // Check default route if (iface->default_router) { @@ -323,14 +325,16 @@ static uint64_t send_router_advert(struct interface *iface, const struct in6_add default_route = true; } - // Construct Prefix Information options - size_t cnt = 0; struct in6_addr dns_pref, *dns_addr = &dns_pref; size_t dns_cnt = 1; odhcpd_get_interface_dns_addr(iface, &dns_pref); + // Construct Prefix Information options + size_t pfxs_cnt = 0; + struct nd_opt_prefix_info *pfxs = NULL; + for (ssize_t i = 0; i < ipcnt; ++i) { struct odhcpd_ipaddr *addr = &addrs[i]; uint32_t preferred = 0; @@ -346,18 +350,25 @@ static uint64_t send_router_advert(struct interface *iface, const struct in6_add } struct nd_opt_prefix_info *p = NULL; - for (size_t i = 0; i < cnt; ++i) { - if (addr->prefix == adv.prefix[i].nd_opt_pi_prefix_len && - !odhcpd_bmemcmp(&adv.prefix[i].nd_opt_pi_prefix, + for (size_t i = 0; i < pfxs_cnt; ++i) { + if (addr->prefix == pfxs[i].nd_opt_pi_prefix_len && + !odhcpd_bmemcmp(&pfxs[i].nd_opt_pi_prefix, &addr->addr, addr->prefix)) - p = &adv.prefix[i]; + p = &pfxs[i]; } if (!p) { - if (cnt >= ARRAY_SIZE(adv.prefix)) - break; + struct nd_opt_prefix_info *tmp; - p = &adv.prefix[cnt++]; + tmp = realloc(pfxs, sizeof(*pfxs) * (pfxs_cnt + 1)); + if (!tmp) { + syslog(LOG_ERR, "Realloc failed for RA prefix option on interface %s", iface->ifname); + continue; + } + + pfxs = tmp; + p = &pfxs[pfxs_cnt++]; + memset(p, 0, sizeof(*p)); } if (addr->preferred > (uint32_t)now) { @@ -428,8 +439,6 @@ static uint64_t send_router_advert(struct interface *iface, const struct in6_add uint32_t lifetime; } dns = {ND_OPT_RECURSIVE_DNS, (1 + (2 * dns_cnt)), 0, 0, 0}; - - // DNS Search options uint8_t search_buf[256], *search_domain = iface->search; size_t search_len = iface->search_len, search_padded = 0; @@ -471,7 +480,7 @@ static uint64_t send_router_advert(struct interface *iface, const struct in6_add uint8_t flags; uint32_t lifetime; uint32_t addr[4]; - } routes[RELAYD_MAX_PREFIXES]; + } *tmp, *routes = NULL; for (ssize_t i = 0; i < ipcnt; ++i) { struct odhcpd_ipaddr *addr = &addrs[i]; @@ -485,6 +494,15 @@ static uint64_t send_router_advert(struct interface *iface, const struct in6_add addr->addr.s6_addr32[1] = 0; } + tmp = realloc(routes, sizeof(*routes) * (routes_cnt + 1)); + if (!tmp) { + syslog(LOG_ERR, "Realloc failed for RA route option on interface %s", iface->ifname); + continue; + } + + routes = tmp; + + memset(&routes[routes_cnt], 0, sizeof(*routes)); routes[routes_cnt].type = ND_OPT_ROUTE_INFO; routes[routes_cnt].len = sizeof(*routes) / 8; routes[routes_cnt].prefix = addr->dprefix; @@ -512,8 +530,9 @@ static uint64_t send_router_advert(struct interface *iface, const struct in6_add }; struct iovec iov[RA_IOV_LEN] = { - {&adv, (uint8_t*)&adv.prefix[cnt] - (uint8_t*)&adv}, - {&routes, routes_cnt * sizeof(*routes)}, + {&adv, sizeof(adv)}, + {pfxs, pfxs_cnt * sizeof(*pfxs)}, + {routes, routes_cnt * sizeof(*routes)}, {&dns, (dns_cnt) ? sizeof(dns) : 0}, {dns_addr, dns_cnt * sizeof(*dns_addr)}, {search, search->len * 8}, @@ -526,6 +545,9 @@ static uint64_t send_router_advert(struct interface *iface, const struct in6_add odhcpd_send(router_event.uloop.fd, &dest, iov, ARRAY_SIZE(iov), iface); + free(pfxs); + free(routes); + return msecs; } -- 2.30.2