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 NET_EPOCH_CALL(destroy_subscription_epoch, &rs->epoch_ctx);
161 }
162
163 void
164 rib_unsubscribe_locked(struct rib_subscription *rs)
165 {
166 struct rib_head *rnh = rs->rnh;
167
168 NET_EPOCH_ASSERT();
169 RIB_WLOCK_ASSERT(rnh);
170
171 CK_STAILQ_REMOVE(&rnh->rnh_subscribers, rs, rib_subscription, next);
172
173 NET_EPOCH_CALL(destroy_subscription_epoch, &rs->epoch_ctx);
174 }
175
176 /*
177 * Epoch callback indicating subscription is safe to destroy
178 */
179 static void
180 destroy_subscription_epoch(epoch_context_t ctx)
181 {
182 struct rib_subscription *rs;
183
184 rs = __containerof(ctx, struct rib_subscription, epoch_ctx);
185
186 free(rs, M_RTABLE);
187 }
188
189 void
190 rib_init_subscriptions(struct rib_head *rnh)
191 {
192
193 CK_STAILQ_INIT(&rnh->rnh_subscribers);
194 }
195
196 void
197 rib_destroy_subscriptions(struct rib_head *rnh)
198 {
199 struct rib_subscription *rs;
200 struct epoch_tracker et;
201
202 NET_EPOCH_ENTER(et);
203 RIB_WLOCK(rnh);
204 while ((rs = CK_STAILQ_FIRST(&rnh->rnh_subscribers)) != NULL) {
205 CK_STAILQ_REMOVE_HEAD(&rnh->rnh_subscribers, next);
206 NET_EPOCH_CALL(destroy_subscription_epoch, &rs->epoch_ctx);
207 }
208 RIB_WUNLOCK(rnh);
209 NET_EPOCH_EXIT(et);
210 }
Cache object: 221479b3cad865fd36287a4ec79a4dba
|