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