xref: /freebsd/sys/net/route/route_subscription.c (revision 254b23eb1f540844cf2a90f2781ae4231c5701ce)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
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 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/malloc.h>
33 #include <sys/socket.h>
34 #include <sys/kernel.h>
35 #include <sys/lock.h>
36 #include <sys/rmlock.h>
37 
38 #include <net/route.h>
39 #include <net/route/route_ctl.h>
40 #include <net/route/route_var.h>
41 #include <net/route/nhop.h>
42 
43 struct rib_subscription {
44 	CK_STAILQ_ENTRY(rib_subscription)	next;
45 	rib_subscription_cb_t			*func;
46 	void					*arg;
47 	struct rib_head				*rnh;
48 	enum rib_subscription_type		type;
49 	struct epoch_context			epoch_ctx;
50 };
51 
52 static void destroy_subscription_epoch(epoch_context_t ctx);
53 
54 void
rib_notify(struct rib_head * rnh,enum rib_subscription_type type,struct rib_cmd_info * rc)55 rib_notify(struct rib_head *rnh, enum rib_subscription_type type,
56     struct rib_cmd_info *rc)
57 {
58 	struct rib_subscription *rs;
59 
60 	CK_STAILQ_FOREACH(rs, &rnh->rnh_subscribers, next) {
61 		if (rs->type == type)
62 			rs->func(rnh, rc, rs->arg);
63 	}
64 }
65 
66 static struct rib_subscription *
allocate_subscription(rib_subscription_cb_t * f,void * arg,enum rib_subscription_type type,bool waitok)67 allocate_subscription(rib_subscription_cb_t *f, void *arg,
68     enum rib_subscription_type type, bool waitok)
69 {
70 	struct rib_subscription *rs;
71 	int flags = M_ZERO | (waitok ? M_WAITOK : M_NOWAIT);
72 
73 	rs = malloc(sizeof(struct rib_subscription), M_RTABLE, flags);
74 	if (rs == NULL)
75 		return (NULL);
76 
77 	rs->func = f;
78 	rs->arg = arg;
79 	rs->type = type;
80 
81 	return (rs);
82 }
83 
84 /*
85  * Subscribe for the changes in the routing table specified by @fibnum and
86  *  @family.
87  *
88  * Returns pointer to the subscription structure on success.
89  */
90 struct rib_subscription *
rib_subscribe(uint32_t fibnum,int family,rib_subscription_cb_t * f,void * arg,enum rib_subscription_type type,bool waitok)91 rib_subscribe(uint32_t fibnum, int family, rib_subscription_cb_t *f, void *arg,
92     enum rib_subscription_type type, bool waitok)
93 {
94 	struct rib_head *rnh;
95 	struct epoch_tracker et;
96 
97 	NET_EPOCH_ENTER(et);
98 	KASSERT((fibnum < rt_numfibs), ("%s: bad fibnum", __func__));
99 	rnh = rt_tables_get_rnh(fibnum, family);
100 	NET_EPOCH_EXIT(et);
101 
102 	return (rib_subscribe_internal(rnh, f, arg, type, waitok));
103 }
104 
105 struct rib_subscription *
rib_subscribe_internal(struct rib_head * rnh,rib_subscription_cb_t * f,void * arg,enum rib_subscription_type type,bool waitok)106 rib_subscribe_internal(struct rib_head *rnh, rib_subscription_cb_t *f, void *arg,
107     enum rib_subscription_type type, bool waitok)
108 {
109 	struct rib_subscription *rs;
110 	struct epoch_tracker et;
111 
112 	if ((rs = allocate_subscription(f, arg, type, waitok)) == NULL)
113 		return (NULL);
114 	rs->rnh = rnh;
115 
116 	NET_EPOCH_ENTER(et);
117 	RIB_WLOCK(rnh);
118 	CK_STAILQ_INSERT_HEAD(&rnh->rnh_subscribers, rs, next);
119 	RIB_WUNLOCK(rnh);
120 	NET_EPOCH_EXIT(et);
121 
122 	return (rs);
123 }
124 
125 struct rib_subscription *
rib_subscribe_locked(struct rib_head * rnh,rib_subscription_cb_t * f,void * arg,enum rib_subscription_type type)126 rib_subscribe_locked(struct rib_head *rnh, rib_subscription_cb_t *f, void *arg,
127     enum rib_subscription_type type)
128 {
129 	struct rib_subscription *rs;
130 
131 	NET_EPOCH_ASSERT();
132 	RIB_WLOCK_ASSERT(rnh);
133 
134 	if ((rs = allocate_subscription(f, arg, type, false)) == NULL)
135 		return (NULL);
136 	rs->rnh = rnh;
137 
138 	CK_STAILQ_INSERT_HEAD(&rnh->rnh_subscribers, rs, next);
139 
140 	return (rs);
141 }
142 
143 /*
144  * Remove rtable subscription @rs from the routing table.
145  * Needs to be run in network epoch.
146  */
147 void
rib_unsubscribe(struct rib_subscription * rs)148 rib_unsubscribe(struct rib_subscription *rs)
149 {
150 	struct rib_head *rnh = rs->rnh;
151 
152 	NET_EPOCH_ASSERT();
153 
154 	RIB_WLOCK(rnh);
155 	CK_STAILQ_REMOVE(&rnh->rnh_subscribers, rs, rib_subscription, next);
156 	RIB_WUNLOCK(rnh);
157 
158 	NET_EPOCH_CALL(destroy_subscription_epoch, &rs->epoch_ctx);
159 }
160 
161 void
rib_unsubscribe_locked(struct rib_subscription * rs)162 rib_unsubscribe_locked(struct rib_subscription *rs)
163 {
164 	struct rib_head *rnh = rs->rnh;
165 
166 	NET_EPOCH_ASSERT();
167 	RIB_WLOCK_ASSERT(rnh);
168 
169 	CK_STAILQ_REMOVE(&rnh->rnh_subscribers, rs, rib_subscription, next);
170 
171 	NET_EPOCH_CALL(destroy_subscription_epoch, &rs->epoch_ctx);
172 }
173 
174 /*
175  * Epoch callback indicating subscription is safe to destroy
176  */
177 static void
destroy_subscription_epoch(epoch_context_t ctx)178 destroy_subscription_epoch(epoch_context_t ctx)
179 {
180 	struct rib_subscription *rs;
181 
182 	rs = __containerof(ctx, struct rib_subscription, epoch_ctx);
183 
184 	free(rs, M_RTABLE);
185 }
186 
187 void
rib_init_subscriptions(struct rib_head * rnh)188 rib_init_subscriptions(struct rib_head *rnh)
189 {
190 
191 	CK_STAILQ_INIT(&rnh->rnh_subscribers);
192 }
193 
194 void
rib_destroy_subscriptions(struct rib_head * rnh)195 rib_destroy_subscriptions(struct rib_head *rnh)
196 {
197 	struct rib_subscription *rs;
198 	struct epoch_tracker et;
199 
200 	NET_EPOCH_ENTER(et);
201 	RIB_WLOCK(rnh);
202 	while ((rs = CK_STAILQ_FIRST(&rnh->rnh_subscribers)) != NULL) {
203 		CK_STAILQ_REMOVE_HEAD(&rnh->rnh_subscribers, next);
204 		NET_EPOCH_CALL(destroy_subscription_epoch, &rs->epoch_ctx);
205 	}
206 	RIB_WUNLOCK(rnh);
207 	NET_EPOCH_EXIT(et);
208 }
209