xref: /freebsd/sys/net/route/route_subscription.c (revision 4d846d260e2b9a3d4d0a701462568268cbfe7a5b)
15c4d2252SAlexander V. Chernikov /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
35c4d2252SAlexander V. Chernikov  *
45c4d2252SAlexander V. Chernikov  * Copyright (c) 2021-2022 Alexander V. Chernikov
55c4d2252SAlexander V. Chernikov  *
65c4d2252SAlexander V. Chernikov  * Redistribution and use in source and binary forms, with or without
75c4d2252SAlexander V. Chernikov  * modification, are permitted provided that the following conditions
85c4d2252SAlexander V. Chernikov  * are met:
95c4d2252SAlexander V. Chernikov  * 1. Redistributions of source code must retain the above copyright
105c4d2252SAlexander V. Chernikov  *    notice, this list of conditions and the following disclaimer.
115c4d2252SAlexander V. Chernikov  * 2. Redistributions in binary form must reproduce the above copyright
125c4d2252SAlexander V. Chernikov  *    notice, this list of conditions and the following disclaimer in the
135c4d2252SAlexander V. Chernikov  *    documentation and/or other materials provided with the distribution.
145c4d2252SAlexander V. Chernikov  *
155c4d2252SAlexander V. Chernikov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
165c4d2252SAlexander V. Chernikov  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
175c4d2252SAlexander V. Chernikov  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
185c4d2252SAlexander V. Chernikov  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
195c4d2252SAlexander V. Chernikov  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
205c4d2252SAlexander V. Chernikov  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
215c4d2252SAlexander V. Chernikov  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
225c4d2252SAlexander V. Chernikov  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
235c4d2252SAlexander V. Chernikov  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
245c4d2252SAlexander V. Chernikov  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
255c4d2252SAlexander V. Chernikov  * SUCH DAMAGE.
265c4d2252SAlexander V. Chernikov  */
275c4d2252SAlexander V. Chernikov 
285c4d2252SAlexander V. Chernikov #include <sys/cdefs.h>
295c4d2252SAlexander V. Chernikov __FBSDID("$FreeBSD$");
305c4d2252SAlexander V. Chernikov #include "opt_route.h"
315c4d2252SAlexander V. Chernikov 
325c4d2252SAlexander V. Chernikov #include <sys/param.h>
335c4d2252SAlexander V. Chernikov #include <sys/systm.h>
345c4d2252SAlexander V. Chernikov #include <sys/malloc.h>
355c4d2252SAlexander V. Chernikov #include <sys/socket.h>
365c4d2252SAlexander V. Chernikov #include <sys/kernel.h>
375c4d2252SAlexander V. Chernikov #include <sys/lock.h>
385c4d2252SAlexander V. Chernikov #include <sys/rmlock.h>
395c4d2252SAlexander V. Chernikov 
405c4d2252SAlexander V. Chernikov #include <net/route.h>
415c4d2252SAlexander V. Chernikov #include <net/route/route_ctl.h>
425c4d2252SAlexander V. Chernikov #include <net/route/route_var.h>
435c4d2252SAlexander V. Chernikov #include <net/route/nhop.h>
445c4d2252SAlexander V. Chernikov 
455c4d2252SAlexander V. Chernikov struct rib_subscription {
465c4d2252SAlexander V. Chernikov 	CK_STAILQ_ENTRY(rib_subscription)	next;
475c4d2252SAlexander V. Chernikov 	rib_subscription_cb_t			*func;
485c4d2252SAlexander V. Chernikov 	void					*arg;
495c4d2252SAlexander V. Chernikov 	struct rib_head				*rnh;
505c4d2252SAlexander V. Chernikov 	enum rib_subscription_type		type;
515c4d2252SAlexander V. Chernikov 	struct epoch_context			epoch_ctx;
525c4d2252SAlexander V. Chernikov };
535c4d2252SAlexander V. Chernikov 
545c4d2252SAlexander V. Chernikov static void destroy_subscription_epoch(epoch_context_t ctx);
555c4d2252SAlexander V. Chernikov 
565c4d2252SAlexander V. Chernikov void
575c4d2252SAlexander V. Chernikov rib_notify(struct rib_head *rnh, enum rib_subscription_type type,
585c4d2252SAlexander V. Chernikov     struct rib_cmd_info *rc)
595c4d2252SAlexander V. Chernikov {
605c4d2252SAlexander V. Chernikov 	struct rib_subscription *rs;
615c4d2252SAlexander V. Chernikov 
625c4d2252SAlexander V. Chernikov 	CK_STAILQ_FOREACH(rs, &rnh->rnh_subscribers, next) {
635c4d2252SAlexander V. Chernikov 		if (rs->type == type)
645c4d2252SAlexander V. Chernikov 			rs->func(rnh, rc, rs->arg);
655c4d2252SAlexander V. Chernikov 	}
665c4d2252SAlexander V. Chernikov }
675c4d2252SAlexander V. Chernikov 
685c4d2252SAlexander V. Chernikov static struct rib_subscription *
695c4d2252SAlexander V. Chernikov allocate_subscription(rib_subscription_cb_t *f, void *arg,
705c4d2252SAlexander V. Chernikov     enum rib_subscription_type type, bool waitok)
715c4d2252SAlexander V. Chernikov {
725c4d2252SAlexander V. Chernikov 	struct rib_subscription *rs;
735c4d2252SAlexander V. Chernikov 	int flags = M_ZERO | (waitok ? M_WAITOK : M_NOWAIT);
745c4d2252SAlexander V. Chernikov 
755c4d2252SAlexander V. Chernikov 	rs = malloc(sizeof(struct rib_subscription), M_RTABLE, flags);
765c4d2252SAlexander V. Chernikov 	if (rs == NULL)
775c4d2252SAlexander V. Chernikov 		return (NULL);
785c4d2252SAlexander V. Chernikov 
795c4d2252SAlexander V. Chernikov 	rs->func = f;
805c4d2252SAlexander V. Chernikov 	rs->arg = arg;
815c4d2252SAlexander V. Chernikov 	rs->type = type;
825c4d2252SAlexander V. Chernikov 
835c4d2252SAlexander V. Chernikov 	return (rs);
845c4d2252SAlexander V. Chernikov }
855c4d2252SAlexander V. Chernikov 
865c4d2252SAlexander V. Chernikov /*
875c4d2252SAlexander V. Chernikov  * Subscribe for the changes in the routing table specified by @fibnum and
885c4d2252SAlexander V. Chernikov  *  @family.
895c4d2252SAlexander V. Chernikov  *
905c4d2252SAlexander V. Chernikov  * Returns pointer to the subscription structure on success.
915c4d2252SAlexander V. Chernikov  */
925c4d2252SAlexander V. Chernikov struct rib_subscription *
935c4d2252SAlexander V. Chernikov rib_subscribe(uint32_t fibnum, int family, rib_subscription_cb_t *f, void *arg,
945c4d2252SAlexander V. Chernikov     enum rib_subscription_type type, bool waitok)
955c4d2252SAlexander V. Chernikov {
965c4d2252SAlexander V. Chernikov 	struct rib_head *rnh;
975c4d2252SAlexander V. Chernikov 	struct epoch_tracker et;
985c4d2252SAlexander V. Chernikov 
995c4d2252SAlexander V. Chernikov 	NET_EPOCH_ENTER(et);
1005c4d2252SAlexander V. Chernikov 	KASSERT((fibnum < rt_numfibs), ("%s: bad fibnum", __func__));
1015c4d2252SAlexander V. Chernikov 	rnh = rt_tables_get_rnh(fibnum, family);
1025c4d2252SAlexander V. Chernikov 	NET_EPOCH_EXIT(et);
1035c4d2252SAlexander V. Chernikov 
1045c4d2252SAlexander V. Chernikov 	return (rib_subscribe_internal(rnh, f, arg, type, waitok));
1055c4d2252SAlexander V. Chernikov }
1065c4d2252SAlexander V. Chernikov 
1075c4d2252SAlexander V. Chernikov struct rib_subscription *
1085c4d2252SAlexander V. Chernikov rib_subscribe_internal(struct rib_head *rnh, rib_subscription_cb_t *f, void *arg,
1095c4d2252SAlexander V. Chernikov     enum rib_subscription_type type, bool waitok)
1105c4d2252SAlexander V. Chernikov {
1115c4d2252SAlexander V. Chernikov 	struct rib_subscription *rs;
1125c4d2252SAlexander V. Chernikov 	struct epoch_tracker et;
1135c4d2252SAlexander V. Chernikov 
1145c4d2252SAlexander V. Chernikov 	if ((rs = allocate_subscription(f, arg, type, waitok)) == NULL)
1155c4d2252SAlexander V. Chernikov 		return (NULL);
1165c4d2252SAlexander V. Chernikov 	rs->rnh = rnh;
1175c4d2252SAlexander V. Chernikov 
1185c4d2252SAlexander V. Chernikov 	NET_EPOCH_ENTER(et);
1195c4d2252SAlexander V. Chernikov 	RIB_WLOCK(rnh);
1205c4d2252SAlexander V. Chernikov 	CK_STAILQ_INSERT_HEAD(&rnh->rnh_subscribers, rs, next);
1215c4d2252SAlexander V. Chernikov 	RIB_WUNLOCK(rnh);
1225c4d2252SAlexander V. Chernikov 	NET_EPOCH_EXIT(et);
1235c4d2252SAlexander V. Chernikov 
1245c4d2252SAlexander V. Chernikov 	return (rs);
1255c4d2252SAlexander V. Chernikov }
1265c4d2252SAlexander V. Chernikov 
1275c4d2252SAlexander V. Chernikov struct rib_subscription *
1285c4d2252SAlexander V. Chernikov rib_subscribe_locked(struct rib_head *rnh, rib_subscription_cb_t *f, void *arg,
1295c4d2252SAlexander V. Chernikov     enum rib_subscription_type type)
1305c4d2252SAlexander V. Chernikov {
1315c4d2252SAlexander V. Chernikov 	struct rib_subscription *rs;
1325c4d2252SAlexander V. Chernikov 
1335c4d2252SAlexander V. Chernikov 	NET_EPOCH_ASSERT();
1345c4d2252SAlexander V. Chernikov 	RIB_WLOCK_ASSERT(rnh);
1355c4d2252SAlexander V. Chernikov 
1365c4d2252SAlexander V. Chernikov 	if ((rs = allocate_subscription(f, arg, type, false)) == NULL)
1375c4d2252SAlexander V. Chernikov 		return (NULL);
1385c4d2252SAlexander V. Chernikov 	rs->rnh = rnh;
1395c4d2252SAlexander V. Chernikov 
1405c4d2252SAlexander V. Chernikov 	CK_STAILQ_INSERT_HEAD(&rnh->rnh_subscribers, rs, next);
1415c4d2252SAlexander V. Chernikov 
1425c4d2252SAlexander V. Chernikov 	return (rs);
1435c4d2252SAlexander V. Chernikov }
1445c4d2252SAlexander V. Chernikov 
1455c4d2252SAlexander V. Chernikov /*
1465c4d2252SAlexander V. Chernikov  * Remove rtable subscription @rs from the routing table.
1475c4d2252SAlexander V. Chernikov  * Needs to be run in network epoch.
1485c4d2252SAlexander V. Chernikov  */
1495c4d2252SAlexander V. Chernikov void
1505c4d2252SAlexander V. Chernikov rib_unsubscribe(struct rib_subscription *rs)
1515c4d2252SAlexander V. Chernikov {
1525c4d2252SAlexander V. Chernikov 	struct rib_head *rnh = rs->rnh;
1535c4d2252SAlexander V. Chernikov 
1545c4d2252SAlexander V. Chernikov 	NET_EPOCH_ASSERT();
1555c4d2252SAlexander V. Chernikov 
1565c4d2252SAlexander V. Chernikov 	RIB_WLOCK(rnh);
1575c4d2252SAlexander V. Chernikov 	CK_STAILQ_REMOVE(&rnh->rnh_subscribers, rs, rib_subscription, next);
1585c4d2252SAlexander V. Chernikov 	RIB_WUNLOCK(rnh);
1595c4d2252SAlexander V. Chernikov 
16073336a6fSZhenlei Huang 	NET_EPOCH_CALL(destroy_subscription_epoch, &rs->epoch_ctx);
1615c4d2252SAlexander V. Chernikov }
1625c4d2252SAlexander V. Chernikov 
1635c4d2252SAlexander V. Chernikov void
1645c4d2252SAlexander V. Chernikov rib_unsubscribe_locked(struct rib_subscription *rs)
1655c4d2252SAlexander V. Chernikov {
1665c4d2252SAlexander V. Chernikov 	struct rib_head *rnh = rs->rnh;
1675c4d2252SAlexander V. Chernikov 
1685c4d2252SAlexander V. Chernikov 	NET_EPOCH_ASSERT();
1695c4d2252SAlexander V. Chernikov 	RIB_WLOCK_ASSERT(rnh);
1705c4d2252SAlexander V. Chernikov 
1715c4d2252SAlexander V. Chernikov 	CK_STAILQ_REMOVE(&rnh->rnh_subscribers, rs, rib_subscription, next);
1725c4d2252SAlexander V. Chernikov 
17373336a6fSZhenlei Huang 	NET_EPOCH_CALL(destroy_subscription_epoch, &rs->epoch_ctx);
1745c4d2252SAlexander V. Chernikov }
1755c4d2252SAlexander V. Chernikov 
1765c4d2252SAlexander V. Chernikov /*
1775c4d2252SAlexander V. Chernikov  * Epoch callback indicating subscription is safe to destroy
1785c4d2252SAlexander V. Chernikov  */
1795c4d2252SAlexander V. Chernikov static void
1805c4d2252SAlexander V. Chernikov destroy_subscription_epoch(epoch_context_t ctx)
1815c4d2252SAlexander V. Chernikov {
1825c4d2252SAlexander V. Chernikov 	struct rib_subscription *rs;
1835c4d2252SAlexander V. Chernikov 
1845c4d2252SAlexander V. Chernikov 	rs = __containerof(ctx, struct rib_subscription, epoch_ctx);
1855c4d2252SAlexander V. Chernikov 
1865c4d2252SAlexander V. Chernikov 	free(rs, M_RTABLE);
1875c4d2252SAlexander V. Chernikov }
1885c4d2252SAlexander V. Chernikov 
1895c4d2252SAlexander V. Chernikov void
1905c4d2252SAlexander V. Chernikov rib_init_subscriptions(struct rib_head *rnh)
1915c4d2252SAlexander V. Chernikov {
1925c4d2252SAlexander V. Chernikov 
1935c4d2252SAlexander V. Chernikov 	CK_STAILQ_INIT(&rnh->rnh_subscribers);
1945c4d2252SAlexander V. Chernikov }
1955c4d2252SAlexander V. Chernikov 
1965c4d2252SAlexander V. Chernikov void
1975c4d2252SAlexander V. Chernikov rib_destroy_subscriptions(struct rib_head *rnh)
1985c4d2252SAlexander V. Chernikov {
1995c4d2252SAlexander V. Chernikov 	struct rib_subscription *rs;
2005c4d2252SAlexander V. Chernikov 	struct epoch_tracker et;
2015c4d2252SAlexander V. Chernikov 
2025c4d2252SAlexander V. Chernikov 	NET_EPOCH_ENTER(et);
2035c4d2252SAlexander V. Chernikov 	RIB_WLOCK(rnh);
2045c4d2252SAlexander V. Chernikov 	while ((rs = CK_STAILQ_FIRST(&rnh->rnh_subscribers)) != NULL) {
2055c4d2252SAlexander V. Chernikov 		CK_STAILQ_REMOVE_HEAD(&rnh->rnh_subscribers, next);
20673336a6fSZhenlei Huang 		NET_EPOCH_CALL(destroy_subscription_epoch, &rs->epoch_ctx);
2075c4d2252SAlexander V. Chernikov 	}
2085c4d2252SAlexander V. Chernikov 	RIB_WUNLOCK(rnh);
2095c4d2252SAlexander V. Chernikov 	NET_EPOCH_EXIT(et);
2105c4d2252SAlexander V. Chernikov }
211