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 44struct 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 53static void destroy_subscription_epoch(epoch_context_t ctx); 54 55void 56rib_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 67static struct rib_subscription * 68allocate_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 */ 91struct rib_subscription * 92rib_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 106struct rib_subscription * 107rib_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 126struct rib_subscription * 127rib_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 */ 148void 149rib_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 162void 163rib_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 */ 178static void 179destroy_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 188void 189rib_init_subscriptions(struct rib_head *rnh) 190{ 191 192 CK_STAILQ_INIT(&rnh->rnh_subscribers); 193} 194 195void 196rib_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