X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fodhcpd.git;a=blobdiff_plain;f=src%2Frouter.c;h=0463d6875eef211b66e51f6168889fbbb0e473b6;hp=6e5111a0b5f5cb94a77af94e086c5d01b03cdbdc;hb=803b83ec64d6f77262b378b83af0a9d84da68c49;hpb=eea7d037b93020dd5d5d2e5a698e88650a0f0912 diff --git a/src/router.c b/src/router.c index 6e5111a..0463d68 100644 --- a/src/router.c +++ b/src/router.c @@ -38,7 +38,6 @@ 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 TIME_LEFT(t1, now) ((t1) != UINT32_MAX ? (t1) - (now) : UINT32_MAX) @@ -255,21 +254,37 @@ static uint16_t calc_ra_lifetime(struct interface *iface, uint32_t maxival) return lifetime; } +enum { + IOV_RA_ADV=0, + IOV_RA_PFXS, + IOV_RA_ROUTES, + IOV_RA_DNS, + IOV_RA_DNS_ADDR, + IOV_RA_SEARCH, + IOV_RA_ADV_INTERVAL, + IOV_RA_TOTAL, +}; + // Router Advert server mode static uint64_t send_router_advert(struct interface *iface, const struct in6_addr *from) { time_t now = odhcpd_time(); - int mtu = odhcpd_get_interface_config(iface->ifname, "mtu"); - int hlim = odhcpd_get_interface_config(iface->ifname, "hop_limit"); + int mtu = iface->ra_mtu; + int hlim = iface->ra_hoplimit; + + if (mtu == 0) + mtu = odhcpd_get_interface_config(iface->ifname, "mtu"); if (mtu < 1280) mtu = 1280; + if (hlim == 0) + hlim = odhcpd_get_interface_config(iface->ifname, "hop_limit"); + struct { 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}}, @@ -289,10 +304,14 @@ static uint64_t send_router_advert(struct interface *iface, const struct in6_add adv.h.nd_ra_flags_reserved |= ND_RA_PREF_LOW; else if (iface->route_preference > 0) adv.h.nd_ra_flags_reserved |= ND_RA_PREF_HIGH; + + adv.h.nd_ra_reachable = htonl(iface->ra_reachabletime); + adv.h.nd_ra_retransmit = htonl(iface->ra_retranstime); + 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; @@ -300,8 +319,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) { @@ -313,13 +335,15 @@ 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_linklocal_interface_address(iface->ifindex, &dns_pref); + 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]; @@ -336,18 +360,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) { @@ -418,8 +449,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; @@ -461,7 +490,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]; @@ -475,6 +504,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; @@ -501,13 +539,14 @@ static uint64_t send_router_advert(struct interface *iface, const struct in6_add .data = {0, 0, (maxival*1000) >> 24, (maxival*1000) >> 16, (maxival*1000) >> 8, maxival*1000} }; - struct iovec iov[RA_IOV_LEN] = { - {&adv, (uint8_t*)&adv.prefix[cnt] - (uint8_t*)&adv}, - {&routes, routes_cnt * sizeof(*routes)}, - {&dns, (dns_cnt) ? sizeof(dns) : 0}, - {dns_addr, dns_cnt * sizeof(*dns_addr)}, - {search, search->len * 8}, - {&adv_interval, adv_interval.len * 8}}; + struct iovec iov[IOV_RA_TOTAL] = { + [IOV_RA_ADV] = {&adv, sizeof(adv)}, + [IOV_RA_PFXS] = {pfxs, pfxs_cnt * sizeof(*pfxs)}, + [IOV_RA_ROUTES] = {routes, routes_cnt * sizeof(*routes)}, + [IOV_RA_DNS] = {&dns, (dns_cnt) ? sizeof(dns) : 0}, + [IOV_RA_DNS_ADDR] = {dns_addr, dns_cnt * sizeof(*dns_addr)}, + [IOV_RA_SEARCH] = {search, search->len * 8}, + [IOV_RA_ADV_INTERVAL] = {&adv_interval, adv_interval.len * 8}}; struct sockaddr_in6 dest = {AF_INET6, 0, 0, ALL_IPV6_NODES, 0}; if (from && !IN6_IS_ADDR_UNSPECIFIED(from)) @@ -516,6 +555,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; } @@ -617,7 +659,7 @@ static void forward_router_advertisement(uint8_t *data, size_t len) size_t rewrite_cnt = iface->dns_cnt; if (rewrite_cnt == 0) { - if (odhcpd_get_linklocal_interface_address(iface->ifindex, &addr)) + if (odhcpd_get_interface_dns_addr(iface, &addr)) continue; // Unable to comply rewrite = &addr;