diff --git a/sys/net/if.c b/sys/net/if.c --- a/sys/net/if.c +++ b/sys/net/if.c @@ -4535,6 +4535,45 @@ return (error); } +struct _if_iter_ext { + union { + if_t ifp; + int array_index; + }; + union { + if_foreach_match_t match_cb; + struct ifnet **array; + }; + union { + void *match_arg; + int array_len; + }; + uint32_t can_sleep:1; +}; +_Static_assert(sizeof(struct _if_iter_ext) <= sizeof(struct if_iter), + "Public version of if_iter structure is too small"); + + +int +if_foreach_sleep(if_foreach_match_t match_cb, void *match_arg, if_foreach_cb_t cb, + void *cb_arg) +{ + struct if_iter_ext_params params = { + .match_cb = match_cb, + .match_arg = match_arg, + .can_sleep = true, + }; + struct if_iter it; + int error = 0; + + for (if_t ifp = if_iter_ext_start(&it, ¶ms); ifp; ifp = if_iter_ext_next(&it)) { + if (error == 0) + error = cb(ifp, cb_arg); + } + + return (error); +} + /* * Iterates over the list of interfaces, permitting callback function @cb to sleep. * Stops iteration if @cb returns non-zero error code. @@ -4544,15 +4583,11 @@ * @cb: iteration callback * @cb_arg: argument to pass to @cb */ -int -if_foreach_sleep(if_foreach_match_t match_cb, void *match_arg, if_foreach_cb_t cb, - void *cb_arg) +static void +prepare_iflist(struct _if_iter_ext *iter) { int match_count = 0, array_size = 16; /* 128 bytes for malloc */ struct ifnet **match_array = NULL; - int error = 0; - - MPASS(cb); while (true) { struct ifnet **new_array; @@ -4572,7 +4607,7 @@ match_count = 0; NET_EPOCH_ENTER(et); CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) { - if (match_cb != NULL && !match_cb(ifp, match_arg)) + if (!iter->match_cb(ifp, iter->match_arg)) continue; if (match_count < array_size) { if (if_try_ref(ifp)) @@ -4587,17 +4622,80 @@ if_rele(match_array[i]); continue; } else { - for (int i = 0; i < match_count; i++) { - if (error == 0) - error = cb(match_array[i], cb_arg); - if_rele(match_array[i]); - } - free(match_array, M_TEMP); + iter->array = match_array; + iter->array_index = 0; + iter->array_len = match_count; break; } } +} - return (error); +static bool +match_each_ifp(struct ifnet *ifp __unused, void *_arg __unused) +{ + return (true); +} + +/* + * Starts iteration over the list of interfaces. + * If @params->can_sleep is true, permits sleeping while iterating + * + * Returns: first matching interface or NULL + */ +if_t +if_iter_ext_start(struct if_iter *_iter, struct if_iter_ext_params *params) +{ + struct _if_iter_ext *iter = (struct _if_iter_ext *)_iter; + + bzero(iter, sizeof(*iter)); + + iter->match_cb = params->match_cb ? params->match_cb : match_each_ifp; + if (params != NULL) { + iter->match_arg = params->match_arg; + iter->can_sleep = params->can_sleep; + } + + if (iter->can_sleep) + prepare_iflist(iter); + else + iter->ifp = CK_STAILQ_FIRST(&V_ifnet); + + return (if_iter_ext_next(_iter)); +} + +if_t +if_iter_ext_next(struct if_iter *_iter) +{ + struct _if_iter_ext *iter = (struct _if_iter_ext *)_iter; + + if (iter->can_sleep) { + if (iter->array_index < iter->array_len) + return (iter->array[iter->array_index++]); + } else { + if_t ifp = iter->ifp; + + while (ifp != NULL && !iter->match_cb(ifp, iter->match_arg)) { + ifp = CK_STAILQ_NEXT(ifp, if_link); + } + + if (ifp != NULL) + iter->ifp = CK_STAILQ_NEXT(ifp, if_link); + return (ifp); + } + + return (NULL); +} + +void +if_iter_ext_finish(struct if_iter *_iter) +{ + struct _if_iter_ext *iter = (struct _if_iter_ext *)_iter; + + if (iter->can_sleep && iter->array != NULL) { + for (int i = 0; i < iter->array_len; i++) + if_rele(iter->array[i]); + free(iter->array, M_TEMP); + } } @@ -4718,6 +4816,105 @@ return (count); } +struct _ifa_iter_ext { + struct ifaddr *ifa; + sa_family_t type; +}; +_Static_assert(sizeof(struct _ifa_iter_ext) < sizeof(struct ifa_iter), + "Public version of ifa_iter structure is too small"); + +struct ifaddr * +ifa_iter_ext_start(if_t ifp, struct ifa_iter *_iter, struct ifa_iter_ext_params *params) +{ + struct _ifa_iter_ext *iter = (struct _ifa_iter_ext *)_iter; + + NET_EPOCH_ASSERT(); + + bzero(iter, sizeof(*iter)); + if (params != NULL) + iter->type = params->type; + iter->ifa = CK_STAILQ_FIRST(&ifp->if_addrhead); + + return (ifa_iter_ext_next(_iter)); +} + +struct ifaddr * +ifa_iter_ext_next(struct ifa_iter *_iter) +{ + struct _ifa_iter_ext *iter = (struct _ifa_iter_ext *)_iter; + struct ifaddr *ifa = iter->ifa; + + while (ifa != NULL && iter->type != 0 && iter->type != ifa->ifa_addr->sa_family) + ifa = CK_STAILQ_NEXT(ifa, ifa_link); + if (ifa != NULL) + iter->ifa = CK_STAILQ_NEXT(ifa, ifa_link); + return (ifa); +} + +void +ifa_iter_ext_finish(struct ifa_iter *_iter __unused) +{ +} + +struct ifaddr * +ifa_ll_iter_start(if_t ifp, struct ifa_iter *_iter) +{ + struct ifa_iter_ext_params params = { .type = AF_LINK }; + + return (ifa_iter_ext_start(ifp, _iter, ¶ms)); +} + +struct ifaddr * +ifa_ll_iter_next(struct ifa_iter *_iter) +{ + return (ifa_iter_ext_next(_iter)); +} + +void +ifa_ll_iter_finish(struct ifa_iter *_iter __unused) +{ +} + +struct _ifma_iter { + struct ifmultiaddr *ifma; +}; +_Static_assert(sizeof(struct _ifma_iter) < sizeof(struct ifa_iter), + "Public version of ifa_iter structure is too small"); + +struct ifmultiaddr * +ifma_ll_iter_start(if_t ifp, struct ifa_iter *_iter) +{ + struct _ifma_iter *iter = (struct _ifma_iter *)_iter; + + NET_EPOCH_ASSERT(); + + bzero(iter, sizeof(*iter)); + + iter->ifma = CK_STAILQ_FIRST(&ifp->if_multiaddrs); + + return (ifma_ll_iter_next(_iter)); +} + +struct ifmultiaddr * +ifma_ll_iter_next(struct ifa_iter *_iter) +{ + struct _ifma_iter *iter = (struct _ifma_iter *)_iter; + struct ifmultiaddr *ifma = iter->ifma; + + while (ifma != NULL && ifma->ifma_addr->sa_family != AF_LINK) + ifma = CK_STAILQ_NEXT(ifma, ifma_link); + + if (ifma != NULL) + iter->ifma = CK_STAILQ_NEXT(ifma, ifma_link); + + return (ifma); +} + +void +ifma_ll_iter_finish(struct ifa_iter *_iter __unused) +{ +} + int if_setsoftc(if_t ifp, void *softc) { diff --git a/sys/net/if_var.h b/sys/net/if_var.h --- a/sys/net/if_var.h +++ b/sys/net/if_var.h @@ -689,9 +689,42 @@ void *context[4]; }; +struct if_iter_ext_params { + if_foreach_match_t match_cb; + void *match_arg; + uint32_t can_sleep:1; + uint32_t spare; +}; + if_t if_iter_start(struct if_iter *); if_t if_iter_next(struct if_iter *); void if_iter_finish(struct if_iter *); +if_t if_iter_ext_start(struct if_iter *, struct if_iter_ext_params *); +if_t if_iter_ext_next(struct if_iter *); +void if_iter_ext_finish(struct if_iter *iter); + +/* Opaque iterator structure for iterating over interface addresses. */ +struct ifa_iter { + void *context[4]; +}; + +struct ifa_iter_ext_params { + sa_family_t type; + uint8_t spare0[7]; + uint64_t spare1[3]; +}; + +struct ifaddr *ifa_iter_ext_start(if_t ifp, struct ifa_iter *, struct ifa_iter_ext_params *); +struct ifaddr *ifa_iter_ext_next(struct ifa_iter *); +void ifa_iter_ext_finish(struct ifa_iter *); + +struct ifaddr *ifa_ll_iter_start(if_t, struct ifa_iter *); +struct ifaddr *ifa_ll_iter_next(struct ifa_iter *); +void ifa_ll_iter_finish(struct ifa_iter *); + +struct ifmultiaddr *ifma_ll_iter_start(if_t, struct ifa_iter *); +struct ifmultiaddr *ifma_ll_iter_next(struct ifa_iter *); +void ifma_ll_iter_finish(struct ifa_iter *); /* Functions */ void if_setinitfn(if_t ifp, if_init_fn_t);