On Tue, Jan 13, 2015 at 02:02:17PM +0200, Patrik Flykt wrote: > Save each new onlink IPv6 prefix and attach an expiry timer to it. > If the prefixes overlap, take the shorter prefix and write a debug > message about the event. Once the prefix is resent in a Router > Advertisement, update the timer. Add a new event for the expiring > prefix. > > Add two helper functions, one for returning a prefix length given a > Router Advertisement and the other for generic prefix matching given > an IPv6 prefix and address. > --- > src/libsystemd-network/sd-icmp6-nd.c | 222 > +++++++++++++++++++++++++++++++++++ > src/systemd/sd-icmp6-nd.h | 6 + > 2 files changed, 228 insertions(+) > > diff --git a/src/libsystemd-network/sd-icmp6-nd.c > b/src/libsystemd-network/sd-icmp6-nd.c > index 3501055..4848912 100644 > --- a/src/libsystemd-network/sd-icmp6-nd.c > +++ b/src/libsystemd-network/sd-icmp6-nd.c > @@ -283,11 +283,227 @@ int sd_icmp6_ra_get_mtu(sd_icmp6_nd *nd, uint32_t > *mtu) { > return 0; > } > > +static int icmp6_ra_prefix_timeout(sd_event_source *s, uint64_t usec, > + void *userdata) { Indentation, here and below too.
> + sd_icmp6_nd *nd = userdata; > + struct icmp6_prefix *prefix, *p; > + > + assert(nd); > + assert(nd->link); > + assert(nd->link->prefixes); > + > + LIST_FOREACH_SAFE(prefixes, prefix, p, nd->link->prefixes) { > + if (prefix->timeout_valid != s) > + continue; > + > + log_icmp6_nd(nd, "Prefix expired > %02x%02x:%02x%02x:%02x%02x:%02x%02x/%d", > + prefix->addr.s6_addr[0], prefix->addr.s6_addr[1], > + prefix->addr.s6_addr[2], prefix->addr.s6_addr[3], > + prefix->addr.s6_addr[4], prefix->addr.s6_addr[5], > + prefix->addr.s6_addr[6], prefix->addr.s6_addr[7], > + prefix->len); > + > + LIST_REMOVE(prefixes, nd->link->prefixes, prefix); > + > + icmp6_nd_notify(nd, > + > ICMP6_EVENT_ROUTER_ADVERTISMENT_PREFIX_EXPIRED); > + prefix = icmp6_prefix_unref(prefix); > + > + break; > + } > + > + return 0; > +} > + > +static int icmp6_ra_prefix_set_timeout(sd_icmp6_nd *nd, > + struct icmp6_prefix *prefix, usec_t valid) { > + usec_t time_now; > + int r; > + > + assert_return(prefix, -EINVAL); > + > + r = sd_event_now(nd->event, clock_boottime_or_monotonic(), > &time_now); > + if (r < 0) > + return r; > + > + prefix->timeout_valid = sd_event_source_unref(prefix->timeout_valid); > + > + r = sd_event_add_time(nd->event, &prefix->timeout_valid, > + clock_boottime_or_monotonic(), time_now + valid, > + USEC_PER_SEC, icmp6_ra_prefix_timeout, nd); > + if (r < 0) > + goto error; > + > + r = sd_event_source_set_priority(prefix->timeout_valid, > + nd->event_priority); > + if (r < 0) > + goto error; > + > + r = sd_event_source_set_description(prefix->timeout_valid, > + "icmp6-prefix-timeout"); > + > +error: > + if (r < 0) > + prefix->timeout_valid = > + sd_event_source_unref(prefix->timeout_valid); > + > + return r; > +} > + > +static int icmp6_prefix_match(const struct in6_addr *prefix, uint8_t > prefixlen, > + const struct in6_addr *addr, uint8_t addr_prefixlen) > { > + uint8_t bytes, mask, len; > + > + assert_return(prefix, -EINVAL); > + assert_return(addr, -EINVAL); > + > + len = (prefixlen < addr_prefixlen)? prefixlen: addr_prefixlen; MIN() > + > + bytes = len / 8; > + > + if (memcmp(prefix, addr, bytes)) > + return -EADDRNOTAVAIL; > + > + if (len % 8) { I guess you can just remove the if() and say mask = 0xff << (8 - len % 8); if (memcmp(prefix, addr, bytes) != 0 || (prefix->s6_addr[bytes] & mask) != (addr->s6_addr[bytes] & mask)) return ...; > + > + > + if ((prefix->s6_addr[bytes] & mask) != > + (addr->s6_addr[bytes] & mask)) > + return -EADDRNOTAVAIL; > + } > + > + return 0; > +} > + > +static int icmp6_ra_prefix_match(struct icmp6_prefix *head, > + const struct in6_addr *addr, uint8_t > addr_len, > + struct icmp6_prefix **result) { > + struct icmp6_prefix *prefix; > + > + LIST_FOREACH(prefixes, prefix, head) { > + if (icmp6_prefix_match(&prefix->addr, prefix->len, addr, > + addr_len) >= 0) { > + *result = prefix; > + return 0; > + } > + } > + > + return -EADDRNOTAVAIL; > +} > + > +int sd_icmp6_prefix_match(struct in6_addr *prefix, uint8_t prefixlen, > + struct in6_addr *addr) { > + return icmp6_prefix_match(prefix, prefixlen, addr, > + sizeof(addr->s6_addr) * 8); > +} > + > +int sd_icmp6_ra_get_prefixlen(sd_icmp6_nd *nd, const struct in6_addr *addr, > + uint8_t *prefixlen) { > + int r; > + struct icmp6_prefix *prefix; > + > + assert_return(nd, -EINVAL); > + assert_return(addr, -EINVAL); > + assert_return(prefixlen, -EINVAL); > + > + if (!nd->link) > + return -EADDRNOTAVAIL; > + > + r = icmp6_ra_prefix_match(nd->link->prefixes, addr, > + sizeof(addr->s6_addr) * 8, &prefix); > + if (r < 0) > + return r; > + > + *prefixlen = prefix->len; > + > + return 0; > +} > + > +static int icmp6_ra_prefix_update(sd_icmp6_nd *nd, ssize_t len, > + const struct nd_opt_prefix_info *prefix_opt) > { > + int r; > + struct icmp6_prefix *prefix; > + uint32_t lifetime; > + char time_string[FORMAT_TIMESPAN_MAX]; > + > + assert_return(nd, -EINVAL); > + assert_return(nd->link, -EINVAL); > + assert_return(prefix_opt, -EINVAL); > + > + if (len < prefix_opt->nd_opt_pi_len) > + return -ENOMSG; > + > + if (!(prefix_opt->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK)) > + return 0; > + > + lifetime = be32toh(prefix_opt->nd_opt_pi_valid_time); > + > + r = icmp6_ra_prefix_match(nd->link->prefixes, > + &prefix_opt->nd_opt_pi_prefix, > + prefix_opt->nd_opt_pi_prefix_len, &prefix); > + > + if (r < 0 && r != -EADDRNOTAVAIL) > + return r; > + > + /* if router advertisment prefix valid timeout is zero, the timeout > + callback will be called immediately to clean up the prefix */ > + > + if (r == -EADDRNOTAVAIL) { > + r = icmp6_prefix_new(&prefix); > + if (r < 0) > + return r; > + > + prefix->len = prefix_opt->nd_opt_pi_prefix_len; > + > + memcpy(&prefix->addr, &prefix_opt->nd_opt_pi_prefix, > + sizeof(prefix->addr)); > + > + log_icmp6_nd(nd, "New prefix > %02x%02x:%02x%02x:%02x%02x:%02x%02x/%d lifetime %d expires in %s", > + prefix->addr.s6_addr[0], prefix->addr.s6_addr[1], > + prefix->addr.s6_addr[2], prefix->addr.s6_addr[3], > + prefix->addr.s6_addr[4], prefix->addr.s6_addr[5], > + prefix->addr.s6_addr[6], prefix->addr.s6_addr[7], > + prefix->len, lifetime, Maybe add a macro for this like SD_ID128_FORMAT_STR and SD_ID128_FORMAT_VAL? > + format_timespan(time_string, FORMAT_TIMESPAN_MAX, > + lifetime * USEC_PER_SEC, 0)); > + > + LIST_PREPEND(prefixes, nd->link->prefixes, prefix); > + > + } else { > + if (prefix->len != prefix_opt->nd_opt_pi_prefix_len) { > + uint8_t prefixlen; > + > + prefixlen = (prefix->len < > prefix_opt->nd_opt_pi_prefix_len)? > + prefix->len: > prefix_opt->nd_opt_pi_prefix_len; MIN... > + log_icmp6_nd(nd, "Prefix length mismatch %d/%d using > %d", > + prefix->len, > prefix_opt->nd_opt_pi_prefix_len, > + prefixlen); > + > + prefix->len = prefixlen; > + } > + > + log_icmp6_nd(nd, "Update prefix > %02x%02x:%02x%02x:%02x%02x:%02x%02x/%d lifetime %d expires in %s", > + prefix->addr.s6_addr[0], prefix->addr.s6_addr[1], > + prefix->addr.s6_addr[2], prefix->addr.s6_addr[3], > + prefix->addr.s6_addr[4], prefix->addr.s6_addr[5], > + prefix->addr.s6_addr[6], prefix->addr.s6_addr[7], > + prefix->len, lifetime, > + format_timespan(time_string, FORMAT_TIMESPAN_MAX, > + lifetime * USEC_PER_SEC, 0)); > + } > + > + r = icmp6_ra_prefix_set_timeout(nd, prefix, lifetime * USEC_PER_SEC); > + > + return r; > +} > + > static int icmp6_ra_parse(sd_icmp6_nd *nd, struct nd_router_advert *ra, > ssize_t len) { > void *opt; > struct nd_opt_hdr *opt_hdr; > struct nd_opt_mtu *opt_mtu; > + struct nd_opt_prefix_info *opt_prefix; > uint32_t mtu; > > assert_return(nd, -EINVAL); > @@ -324,6 +540,12 @@ static int icmp6_ra_parse(sd_icmp6_nd *nd, struct > nd_router_advert *ra, > > break; > > + case ND_OPT_PREFIX_INFORMATION: > + opt_prefix = opt; > + > + icmp6_ra_prefix_update(nd, len, opt_prefix); > + > + break; > } > > len -= opt_hdr->nd_opt_len * ICMP6_OPT_LEN_UNITS; > diff --git a/src/systemd/sd-icmp6-nd.h b/src/systemd/sd-icmp6-nd.h > index 73ebccf..9139c06 100644 > --- a/src/systemd/sd-icmp6-nd.h > +++ b/src/systemd/sd-icmp6-nd.h > @@ -31,6 +31,7 @@ enum { > ICMP6_EVENT_ROUTER_ADVERTISMENT_TIMEOUT = 1, > ICMP6_EVENT_ROUTER_ADVERTISMENT_OTHER = 2, > ICMP6_EVENT_ROUTER_ADVERTISMENT_MANAGED = 3, Indent the others too? > + ICMP6_EVENT_ROUTER_ADVERTISMENT_PREFIX_EXPIRED = 4, > }; > > typedef struct sd_icmp6_nd sd_icmp6_nd; > @@ -51,7 +52,12 @@ sd_icmp6_nd *sd_icmp6_nd_ref(sd_icmp6_nd *nd); > sd_icmp6_nd *sd_icmp6_nd_unref(sd_icmp6_nd *nd); > int sd_icmp6_nd_new(sd_icmp6_nd **ret); > > +int sd_icmp6_prefix_match(struct in6_addr *prefix, uint8_t prefixlen, > + struct in6_addr *addr); > + > int sd_icmp6_ra_get_mtu(sd_icmp6_nd *nd, uint32_t *mtu); > +int sd_icmp6_ra_get_prefixlen(sd_icmp6_nd *nd, const struct in6_addr *addr, > + uint8_t *prefixlen); > > int sd_icmp6_nd_stop(sd_icmp6_nd *nd); > int sd_icmp6_router_solicitation_start(sd_icmp6_nd *nd); Zbyszek _______________________________________________ systemd-devel mailing list systemd-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/systemd-devel