1 /* 2 * lib/cache_mngr.c Cache Manager 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation version 2.1 7 * of the License. 8 * 9 * Copyright (c) 2003-2008 Thomas Graf <tgraf (at) suug.ch> 10 */ 11 12 /** 13 * @ingroup cache_mngt 14 * @defgroup cache_mngr Manager 15 * @brief Helps keeping caches up to date. 16 * 17 * The purpose of a cache manager is to keep track of caches and 18 * automatically receive event notifications to keep the caches 19 * up to date with the kernel state. Each manager has exactly one 20 * netlink socket assigned which limits the scope of each manager 21 * to exactly one netlink family. Therefore all caches committed 22 * to a manager must be part of the same netlink family. Due to the 23 * nature of a manager, it is not possible to have a cache maintain 24 * two instances of the same cache type. The socket is subscribed 25 * to the event notification group of each cache and also put into 26 * non-blocking mode. Functions exist to poll() on the socket to 27 * wait for new events to be received. 28 * 29 * @code 30 * App libnl Kernel 31 * | | 32 * +-----------------+ [ notification, link change ] 33 * | | Cache Manager | | [ (IFF_UP | IFF_RUNNING) ] 34 * | | | 35 * | | +------------+| | | [ notification, new addr ] 36 * <-------|---| route/link |<-------(async)--+ [ 10.0.1.1/32 dev eth1 ] 37 * | | +------------+| | | 38 * | +------------+| | 39 * <---|---|---| route/addr |<------|-(async)--------------+ 40 * | +------------+| 41 * | | +------------+| | 42 * <-------|---| ... || 43 * | | +------------+| | 44 * +-----------------+ 45 * | | 46 * @endcode 47 * 48 * @par 1) Creating a new cache manager 49 * @code 50 * struct nl_cache_mngr *mngr; 51 * 52 * // Allocate a new cache manager for RTNETLINK and automatically 53 * // provide the caches added to the manager. 54 * mngr = nl_cache_mngr_alloc(NETLINK_ROUTE, NL_AUTO_PROVIDE); 55 * @endcode 56 * 57 * @par 2) Keep track of a cache 58 * @code 59 * struct nl_cache *cache; 60 * 61 * // Create a new cache for links/interfaces and ask the manager to 62 * // keep it up to date for us. This will trigger a full dump request 63 * // to initially fill the cache. 64 * cache = nl_cache_mngr_add(mngr, "route/link"); 65 * @endcode 66 * 67 * @par 3) Make the manager receive updates 68 * @code 69 * // Give the manager the ability to receive updates, will call poll() 70 * // with a timeout of 5 seconds. 71 * if (nl_cache_mngr_poll(mngr, 5000) > 0) { 72 * // Manager received at least one update, dump cache? 73 * nl_cache_dump(cache, ...); 74 * } 75 * @endcode 76 * 77 * @par 4) Release cache manager 78 * @code 79 * nl_cache_mngr_free(mngr); 80 * @endcode 81 * @{ 82 */ 83 84 #include <netlink-local.h> 85 #include <netlink/netlink.h> 86 #include <netlink/cache.h> 87 #include <netlink/utils.h> 88 89 static int include_cb(struct nl_object *obj, struct nl_parser_param *p) 90 { 91 struct nl_cache_assoc *ca = p->pp_arg; 92 93 NL_DBG(2, "Including object %p into cache %p\n", obj, ca->ca_cache); 94 #ifdef NL_DEBUG 95 if (nl_debug >= 4) 96 nl_object_dump(obj, &nl_debug_dp); 97 #endif 98 return nl_cache_include(ca->ca_cache, obj, ca->ca_change, ca->ca_change_data); 99 } 100 101 static int event_input(struct nl_msg *msg, void *arg) 102 { 103 struct nl_cache_mngr *mngr = arg; 104 int protocol = nlmsg_get_proto(msg); 105 int type = nlmsg_hdr(msg)->nlmsg_type; 106 struct nl_cache_ops *ops; 107 int i, n; 108 struct nl_parser_param p = { 109 .pp_cb = include_cb, 110 }; 111 112 NL_DBG(2, "Cache manager %p, handling new message %p as event\n", 113 mngr, msg); 114 #ifdef NL_DEBUG 115 if (nl_debug >= 4) 116 nl_msg_dump(msg, stderr); 117 #endif 118 119 if (mngr->cm_protocol != protocol) 120 BUG(); 121 122 for (i = 0; i < mngr->cm_nassocs; i++) { 123 if (mngr->cm_assocs[i].ca_cache) { 124 ops = mngr->cm_assocs[i].ca_cache->c_ops; 125 for (n = 0; ops->co_msgtypes[n].mt_id >= 0; n++) 126 if (ops->co_msgtypes[n].mt_id == type) 127 goto found; 128 } 129 } 130 131 return NL_SKIP; 132 133 found: 134 NL_DBG(2, "Associated message %p to cache %p\n", 135 msg, mngr->cm_assocs[i].ca_cache); 136 p.pp_arg = &mngr->cm_assocs[i]; 137 138 return nl_cache_parse(ops, NULL, nlmsg_hdr(msg), &p); 139 } 140 141 /** 142 * Allocate new cache manager 143 * @arg sk Netlink socket. 144 * @arg protocol Netlink Protocol this manager is used for 145 * @arg flags Flags 146 * 147 * @return Newly allocated cache manager or NULL on failure. 148 */ 149 int nl_cache_mngr_alloc(struct nl_sock *sk, int protocol, int flags, 150 struct nl_cache_mngr **result) 151 { 152 struct nl_cache_mngr *mngr; 153 int err = -NLE_NOMEM; 154 155 if (sk == NULL) 156 BUG(); 157 158 mngr = calloc(1, sizeof(*mngr)); 159 if (!mngr) 160 goto errout; 161 162 mngr->cm_handle = sk; 163 mngr->cm_nassocs = 32; 164 mngr->cm_protocol = protocol; 165 mngr->cm_flags = flags; 166 mngr->cm_assocs = calloc(mngr->cm_nassocs, 167 sizeof(struct nl_cache_assoc)); 168 if (!mngr->cm_assocs) 169 goto errout; 170 171 nl_socket_modify_cb(mngr->cm_handle, NL_CB_VALID, NL_CB_CUSTOM, 172 event_input, mngr); 173 174 /* Required to receive async event notifications */ 175 nl_socket_disable_seq_check(mngr->cm_handle); 176 177 if ((err = nl_connect(mngr->cm_handle, protocol) < 0)) 178 goto errout; 179 180 if ((err = nl_socket_set_nonblocking(mngr->cm_handle) < 0)) 181 goto errout; 182 183 NL_DBG(1, "Allocated cache manager %p, protocol %d, %d caches\n", 184 mngr, protocol, mngr->cm_nassocs); 185 186 *result = mngr; 187 return 0; 188 189 errout: 190 nl_cache_mngr_free(mngr); 191 return err; 192 } 193 194 /** 195 * Add cache responsibility to cache manager 196 * @arg mngr Cache manager. 197 * @arg name Name of cache to keep track of 198 * @arg cb Function to be called upon changes. 199 * @arg result Pointer to store added cache. 200 * 201 * Allocates a new cache of the specified type and adds it to the manager. 202 * The operation will trigger a full dump request from the kernel to 203 * initially fill the contents of the cache. The manager will subscribe 204 * to the notification group of the cache to keep track of any further 205 * changes. 206 * 207 * @return 0 on success or a negative error code. 208 */ 209 int nl_cache_mngr_add(struct nl_cache_mngr *mngr, const char *name, 210 change_func_t cb, void *data, struct nl_cache **result) 211 { 212 struct nl_cache_ops *ops; 213 struct nl_cache *cache; 214 struct nl_af_group *grp; 215 int err, i; 216 217 ops = nl_cache_ops_lookup(name); 218 if (!ops) 219 return -NLE_NOCACHE; 220 221 if (ops->co_protocol != mngr->cm_protocol) 222 return -NLE_PROTO_MISMATCH; 223 224 if (ops->co_groups == NULL) 225 return -NLE_OPNOTSUPP; 226 227 for (i = 0; i < mngr->cm_nassocs; i++) 228 if (mngr->cm_assocs[i].ca_cache && 229 mngr->cm_assocs[i].ca_cache->c_ops == ops) 230 return -NLE_EXIST; 231 232 retry: 233 for (i = 0; i < mngr->cm_nassocs; i++) 234 if (!mngr->cm_assocs[i].ca_cache) 235 break; 236 237 if (i >= mngr->cm_nassocs) { 238 mngr->cm_nassocs += 16; 239 mngr->cm_assocs = realloc(mngr->cm_assocs, 240 mngr->cm_nassocs * 241 sizeof(struct nl_cache_assoc)); 242 if (mngr->cm_assocs == NULL) 243 return -NLE_NOMEM; 244 else { 245 NL_DBG(1, "Increased capacity of cache manager %p " \ 246 "to %d\n", mngr, mngr->cm_nassocs); 247 goto retry; 248 } 249 } 250 251 cache = nl_cache_alloc(ops); 252 if (!cache) 253 return -NLE_NOMEM; 254 255 for (grp = ops->co_groups; grp->ag_group; grp++) { 256 err = nl_socket_add_membership(mngr->cm_handle, grp->ag_group); 257 if (err < 0) 258 goto errout_free_cache; 259 } 260 261 err = nl_cache_refill(mngr->cm_handle, cache); 262 if (err < 0) 263 goto errout_drop_membership; 264 265 mngr->cm_assocs[i].ca_cache = cache; 266 mngr->cm_assocs[i].ca_change = cb; 267 mngr->cm_assocs[i].ca_change_data = data; 268 269 if (mngr->cm_flags & NL_AUTO_PROVIDE) 270 nl_cache_mngt_provide(cache); 271 272 NL_DBG(1, "Added cache %p <%s> to cache manager %p\n", 273 cache, nl_cache_name(cache), mngr); 274 275 *result = cache; 276 return 0; 277 278 errout_drop_membership: 279 for (grp = ops->co_groups; grp->ag_group; grp++) 280 nl_socket_drop_membership(mngr->cm_handle, grp->ag_group); 281 errout_free_cache: 282 nl_cache_free(cache); 283 284 return err; 285 } 286 287 /** 288 * Get file descriptor 289 * @arg mngr Cache Manager 290 * 291 * Get the file descriptor of the socket associated to the manager. 292 * This can be used to change socket options or monitor activity 293 * using poll()/select(). 294 */ 295 int nl_cache_mngr_get_fd(struct nl_cache_mngr *mngr) 296 { 297 return nl_socket_get_fd(mngr->cm_handle); 298 } 299 300 /** 301 * Check for event notifications 302 * @arg mngr Cache Manager 303 * @arg timeout Upper limit poll() will block, in milliseconds. 304 * 305 * Causes poll() to be called to check for new event notifications 306 * being available. Automatically receives and handles available 307 * notifications. 308 * 309 * This functionally is ideally called regularly during an idle 310 * period. 311 * 312 * @return A positive value if at least one update was handled, 0 313 * for none, or a negative error code. 314 */ 315 int nl_cache_mngr_poll(struct nl_cache_mngr *mngr, int timeout) 316 { 317 int ret; 318 struct pollfd fds = { 319 .fd = nl_socket_get_fd(mngr->cm_handle), 320 .events = POLLIN, 321 }; 322 323 NL_DBG(3, "Cache manager %p, poll() fd %d\n", mngr, fds.fd); 324 ret = poll(&fds, 1, timeout); 325 NL_DBG(3, "Cache manager %p, poll() returned %d\n", mngr, ret); 326 if (ret < 0) 327 return -nl_syserr2nlerr(errno); 328 329 if (ret == 0) 330 return 0; 331 332 return nl_cache_mngr_data_ready(mngr); 333 } 334 335 /** 336 * Receive available event notifications 337 * @arg mngr Cache manager 338 * 339 * This function can be called if the socket associated to the manager 340 * contains updates to be received. This function should not be used 341 * if nl_cache_mngr_poll() is used. 342 * 343 * @return A positive value if at least one update was handled, 0 344 * for none, or a negative error code. 345 */ 346 int nl_cache_mngr_data_ready(struct nl_cache_mngr *mngr) 347 { 348 int err; 349 350 err = nl_recvmsgs_default(mngr->cm_handle); 351 if (err < 0) 352 return err; 353 354 return 1; 355 } 356 357 /** 358 * Free cache manager and all caches. 359 * @arg mngr Cache manager. 360 * 361 * Release all resources after usage of a cache manager. 362 */ 363 void nl_cache_mngr_free(struct nl_cache_mngr *mngr) 364 { 365 int i; 366 367 if (!mngr) 368 return; 369 370 if (mngr->cm_handle) 371 nl_close(mngr->cm_handle); 372 373 for (i = 0; i < mngr->cm_nassocs; i++) 374 if (mngr->cm_assocs[i].ca_cache) 375 nl_cache_free(mngr->cm_assocs[i].ca_cache); 376 377 free(mngr->cm_assocs); 378 free(mngr); 379 380 NL_DBG(1, "Cache manager %p freed\n", mngr); 381 } 382 383 /** @} */ 384