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