xref: /freebsd/sys/net/route/route_subscription.c (revision c7046f76c2c027b00c0e6ba57cfd28f1a78f5e23)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2021-2022 Alexander V. Chernikov
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30 #include "opt_route.h"
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/malloc.h>
35 #include <sys/socket.h>
36 #include <sys/kernel.h>
37 #include <sys/lock.h>
38 #include <sys/rmlock.h>
39 
40 #include <net/route.h>
41 #include <net/route/route_ctl.h>
42 #include <net/route/route_var.h>
43 #include <net/route/nhop.h>
44 
45 struct rib_subscription {
46 	CK_STAILQ_ENTRY(rib_subscription)	next;
47 	rib_subscription_cb_t			*func;
48 	void					*arg;
49 	struct rib_head				*rnh;
50 	enum rib_subscription_type		type;
51 	struct epoch_context			epoch_ctx;
52 };
53 
54 static void destroy_subscription_epoch(epoch_context_t ctx);
55 
56 void
57 rib_notify(struct rib_head *rnh, enum rib_subscription_type type,
58     struct rib_cmd_info *rc)
59 {
60 	struct rib_subscription *rs;
61 
62 	CK_STAILQ_FOREACH(rs, &rnh->rnh_subscribers, next) {
63 		if (rs->type == type)
64 			rs->func(rnh, rc, rs->arg);
65 	}
66 }
67 
68 static struct rib_subscription *
69 allocate_subscription(rib_subscription_cb_t *f, void *arg,
70     enum rib_subscription_type type, bool waitok)
71 {
72 	struct rib_subscription *rs;
73 	int flags = M_ZERO | (waitok ? M_WAITOK : M_NOWAIT);
74 
75 	rs = malloc(sizeof(struct rib_subscription), M_RTABLE, flags);
76 	if (rs == NULL)
77 		return (NULL);
78 
79 	rs->func = f;
80 	rs->arg = arg;
81 	rs->type = type;
82 
83 	return (rs);
84 }
85 
86 /*
87  * Subscribe for the changes in the routing table specified by @fibnum and
88  *  @family.
89  *
90  * Returns pointer to the subscription structure on success.
91  */
92 struct rib_subscription *
93 rib_subscribe(uint32_t fibnum, int family, rib_subscription_cb_t *f, void *arg,
94     enum rib_subscription_type type, bool waitok)
95 {
96 	struct rib_head *rnh;
97 	struct epoch_tracker et;
98 
99 	NET_EPOCH_ENTER(et);
100 	KASSERT((fibnum < rt_numfibs), ("%s: bad fibnum", __func__));
101 	rnh = rt_tables_get_rnh(fibnum, family);
102 	NET_EPOCH_EXIT(et);
103 
104 	return (rib_subscribe_internal(rnh, f, arg, type, waitok));
105 }
106 
107 struct rib_subscription *
108 rib_subscribe_internal(struct rib_head *rnh, rib_subscription_cb_t *f, void *arg,
109     enum rib_subscription_type type, bool waitok)
110 {
111 	struct rib_subscription *rs;
112 	struct epoch_tracker et;
113 
114 	if ((rs = allocate_subscription(f, arg, type, waitok)) == NULL)
115 		return (NULL);
116 	rs->rnh = rnh;
117 
118 	NET_EPOCH_ENTER(et);
119 	RIB_WLOCK(rnh);
120 	CK_STAILQ_INSERT_HEAD(&rnh->rnh_subscribers, rs, next);
121 	RIB_WUNLOCK(rnh);
122 	NET_EPOCH_EXIT(et);
123 
124 	return (rs);
125 }
126 
127 struct rib_subscription *
128 rib_subscribe_locked(struct rib_head *rnh, rib_subscription_cb_t *f, void *arg,
129     enum rib_subscription_type type)
130 {
131 	struct rib_subscription *rs;
132 
133 	NET_EPOCH_ASSERT();
134 	RIB_WLOCK_ASSERT(rnh);
135 
136 	if ((rs = allocate_subscription(f, arg, type, false)) == NULL)
137 		return (NULL);
138 	rs->rnh = rnh;
139 
140 	CK_STAILQ_INSERT_HEAD(&rnh->rnh_subscribers, rs, next);
141 
142 	return (rs);
143 }
144 
145 /*
146  * Remove rtable subscription @rs from the routing table.
147  * Needs to be run in network epoch.
148  */
149 void
150 rib_unsubscribe(struct rib_subscription *rs)
151 {
152 	struct rib_head *rnh = rs->rnh;
153 
154 	NET_EPOCH_ASSERT();
155 
156 	RIB_WLOCK(rnh);
157 	CK_STAILQ_REMOVE(&rnh->rnh_subscribers, rs, rib_subscription, next);
158 	RIB_WUNLOCK(rnh);
159 
160 	epoch_call(net_epoch_preempt, destroy_subscription_epoch,
161 	    &rs->epoch_ctx);
162 }
163 
164 void
165 rib_unsubscribe_locked(struct rib_subscription *rs)
166 {
167 	struct rib_head *rnh = rs->rnh;
168 
169 	NET_EPOCH_ASSERT();
170 	RIB_WLOCK_ASSERT(rnh);
171 
172 	CK_STAILQ_REMOVE(&rnh->rnh_subscribers, rs, rib_subscription, next);
173 
174 	epoch_call(net_epoch_preempt, destroy_subscription_epoch,
175 	    &rs->epoch_ctx);
176 }
177 
178 /*
179  * Epoch callback indicating subscription is safe to destroy
180  */
181 static void
182 destroy_subscription_epoch(epoch_context_t ctx)
183 {
184 	struct rib_subscription *rs;
185 
186 	rs = __containerof(ctx, struct rib_subscription, epoch_ctx);
187 
188 	free(rs, M_RTABLE);
189 }
190 
191 void
192 rib_init_subscriptions(struct rib_head *rnh)
193 {
194 
195 	CK_STAILQ_INIT(&rnh->rnh_subscribers);
196 }
197 
198 void
199 rib_destroy_subscriptions(struct rib_head *rnh)
200 {
201 	struct rib_subscription *rs;
202 	struct epoch_tracker et;
203 
204 	NET_EPOCH_ENTER(et);
205 	RIB_WLOCK(rnh);
206 	while ((rs = CK_STAILQ_FIRST(&rnh->rnh_subscribers)) != NULL) {
207 		CK_STAILQ_REMOVE_HEAD(&rnh->rnh_subscribers, next);
208 		epoch_call(net_epoch_preempt, destroy_subscription_epoch,
209 		    &rs->epoch_ctx);
210 	}
211 	RIB_WUNLOCK(rnh);
212 	NET_EPOCH_EXIT(et);
213 }
214