1 /* SPDX-License-Identifier: LGPL-2.1-only */ 2 /* 3 * lib/cache_mngr.c Cache Manager 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Lesser General Public 7 * License as published by the Free Software Foundation version 2.1 8 * of the License. 9 * 10 * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch> 11 */ 12 13 /** 14 * @ingroup cache_mngt 15 * @defgroup cache_mngr Manager 16 * @brief Manager keeping caches up to date automatically. 17 * 18 * The cache manager keeps caches up to date automatically by listening to 19 * netlink notifications and integrating the received information into the 20 * existing cache. 21 * 22 * @note This functionality is still considered experimental. 23 * 24 * Related sections in the development guide: 25 * - @core_doc{_cache_manager,Cache Manager} 26 * 27 * @{ 28 * 29 * Header 30 * ------ 31 * ~~~~{.c} 32 * #include <netlink/cache.h> 33 * ~~~~ 34 */ 35 36 #include <netlink-private/netlink.h> 37 #include <netlink-private/utils.h> 38 #include <netlink/netlink.h> 39 #include <netlink/cache.h> 40 #include <netlink/utils.h> 41 42 /** @cond SKIP */ 43 #define NASSOC_INIT 16 44 #define NASSOC_EXPAND 8 45 /** @endcond */ 46 include_cb(struct nl_object * obj,struct nl_parser_param * p)47 static int include_cb(struct nl_object *obj, struct nl_parser_param *p) 48 { 49 struct nl_cache_assoc *ca = p->pp_arg; 50 struct nl_cache_ops *ops = ca->ca_cache->c_ops; 51 52 NL_DBG(2, "Including object %p into cache %p\n", obj, ca->ca_cache); 53 #ifdef NL_DEBUG 54 if (nl_debug >= 4) 55 nl_object_dump(obj, &nl_debug_dp); 56 #endif 57 58 if (ops->co_event_filter) 59 if (ops->co_event_filter(ca->ca_cache, obj) != NL_OK) 60 return 0; 61 62 if (ops->co_include_event) 63 return ops->co_include_event(ca->ca_cache, obj, ca->ca_change, 64 ca->ca_change_v2, 65 ca->ca_change_data); 66 else { 67 if (ca->ca_change_v2) 68 return nl_cache_include_v2(ca->ca_cache, obj, ca->ca_change_v2, ca->ca_change_data); 69 else 70 return nl_cache_include(ca->ca_cache, obj, ca->ca_change, ca->ca_change_data); 71 } 72 73 } 74 event_input(struct nl_msg * msg,void * arg)75 static int event_input(struct nl_msg *msg, void *arg) 76 { 77 struct nl_cache_mngr *mngr = arg; 78 int protocol = nlmsg_get_proto(msg); 79 int type = nlmsg_hdr(msg)->nlmsg_type; 80 struct nl_cache_ops *ops; 81 int i, n; 82 struct nl_parser_param p = { 83 .pp_cb = include_cb, 84 }; 85 86 NL_DBG(2, "Cache manager %p, handling new message %p as event\n", 87 mngr, msg); 88 #ifdef NL_DEBUG 89 if (nl_debug >= 4) 90 nl_msg_dump(msg, stderr); 91 #endif 92 93 if (mngr->cm_protocol != protocol) 94 BUG(); 95 96 for (i = 0; i < mngr->cm_nassocs; i++) { 97 if (mngr->cm_assocs[i].ca_cache) { 98 ops = mngr->cm_assocs[i].ca_cache->c_ops; 99 for (n = 0; ops->co_msgtypes[n].mt_id >= 0; n++) 100 if (ops->co_msgtypes[n].mt_id == type) 101 goto found; 102 } 103 } 104 105 return NL_SKIP; 106 107 found: 108 NL_DBG(2, "Associated message %p to cache %p\n", 109 msg, mngr->cm_assocs[i].ca_cache); 110 p.pp_arg = &mngr->cm_assocs[i]; 111 112 return nl_cache_parse(ops, NULL, nlmsg_hdr(msg), &p); 113 } 114 115 /** 116 * Allocate new cache manager 117 * @arg sk Netlink socket or NULL to auto allocate 118 * @arg protocol Netlink protocol this manager is used for 119 * @arg flags Flags (\c NL_AUTO_PROVIDE) 120 * @arg result Result pointer 121 * 122 * Allocates a new cache manager for the specified netlink protocol. 123 * 124 * 1. If sk is not specified (\c NULL) a netlink socket matching the 125 * specified protocol will be automatically allocated. 126 * 127 * 2. The socket will be put in non-blocking mode and sequence checking 128 * will be disabled regardless of whether the socket was provided by 129 * the caller or automatically allocated. 130 * 131 * 3. The socket will be connected. 132 * 133 * If the flag \c NL_AUTO_PROVIDE is specified, any cache added to the 134 * manager will automatically be made available to other users using 135 * nl_cache_mngt_provide(). 136 * 137 * @note If the socket is provided by the caller, it is NOT recommended 138 * to use the socket for anything else besides receiving netlink 139 * notifications. 140 * 141 * @return 0 on success or a negative error code. 142 */ nl_cache_mngr_alloc(struct nl_sock * sk,int protocol,int flags,struct nl_cache_mngr ** result)143 int nl_cache_mngr_alloc(struct nl_sock *sk, int protocol, int flags, 144 struct nl_cache_mngr **result) 145 { 146 struct nl_cache_mngr *mngr; 147 int err = -NLE_NOMEM; 148 149 /* Catch abuse of flags */ 150 if (flags & NL_ALLOCATED_SOCK) 151 BUG(); 152 153 mngr = calloc(1, sizeof(*mngr)); 154 if (!mngr) 155 return -NLE_NOMEM; 156 157 if (!sk) { 158 if (!(sk = nl_socket_alloc())) 159 goto errout; 160 161 flags |= NL_ALLOCATED_SOCK; 162 } 163 164 mngr->cm_sock = sk; 165 mngr->cm_nassocs = NASSOC_INIT; 166 mngr->cm_protocol = protocol; 167 mngr->cm_flags = flags; 168 mngr->cm_assocs = calloc(mngr->cm_nassocs, 169 sizeof(struct nl_cache_assoc)); 170 if (!mngr->cm_assocs) 171 goto errout; 172 173 /* Required to receive async event notifications */ 174 nl_socket_disable_seq_check(mngr->cm_sock); 175 176 if ((err = nl_connect(mngr->cm_sock, protocol)) < 0) 177 goto errout; 178 179 if ((err = nl_socket_set_nonblocking(mngr->cm_sock)) < 0) 180 goto errout; 181 182 /* Create and allocate socket for sync cache fills */ 183 mngr->cm_sync_sock = nl_socket_alloc(); 184 if (!mngr->cm_sync_sock) { 185 err = -NLE_NOMEM; 186 goto errout; 187 } 188 if ((err = nl_connect(mngr->cm_sync_sock, protocol)) < 0) 189 goto errout_free_sync_sock; 190 191 NL_DBG(1, "Allocated cache manager %p, protocol %d, %d caches\n", 192 mngr, protocol, mngr->cm_nassocs); 193 194 *result = mngr; 195 return 0; 196 197 errout_free_sync_sock: 198 nl_socket_free(mngr->cm_sync_sock); 199 errout: 200 nl_cache_mngr_free(mngr); 201 return err; 202 } 203 204 /** 205 * Set change_func_v2 for cache manager 206 * @arg mngr Cache manager. 207 * @arg cache Cache associated with the callback 208 * @arg cb Function to be called upon changes. 209 * @arg data Argument passed on to change callback 210 * 211 * Adds callback change_func_v2 to a registered cache. This callback provides 212 * in like the standard change_func the added or remove netlink object. In case 213 * of a change the old and the new object is provided as well as the according 214 * diff. If this callback is registered this has a higher priority then the 215 * change_func registered during cache registration. Hence only one callback is 216 * executed. 217 * 218 * The first netlink object in the callback is refering to the old object and 219 * the second to the new. This means on NL_ACT_CHANGE the first is the previous 220 * object in the cache and the second the updated version. On NL_ACT_DEL the 221 * first is the deleted object the second is NULL. On NL_ACT_NEW the first is 222 * NULL and the second the new netlink object. 223 * 224 * The user is responsible for calling nl_cache_mngr_poll() or monitor 225 * the socket and call nl_cache_mngr_data_ready() to allow the library 226 * to process netlink notification events. 227 * 228 * @see nl_cache_mngr_poll() 229 * @see nl_cache_mngr_data_ready() 230 * 231 * @return 0 on success or a negative error code. 232 * @return -NLE_PROTO_MISMATCH Protocol mismatch between cache manager and 233 * cache type 234 * @return -NLE_OPNOTSUPP Cache type does not support updates 235 * @return -NLE_RANGE Cache of this type is not registered 236 */ nl_cache_mngr_set_change_func_v2(struct nl_cache_mngr * mngr,struct nl_cache * cache,change_func_v2_t cb,void * data)237 static int nl_cache_mngr_set_change_func_v2(struct nl_cache_mngr *mngr, 238 struct nl_cache *cache, 239 change_func_v2_t cb, void *data) 240 { 241 struct nl_cache_ops *ops; 242 int i; 243 244 ops = cache->c_ops; 245 if (!ops) 246 return -NLE_INVAL; 247 248 if (ops->co_protocol != mngr->cm_protocol) 249 return -NLE_PROTO_MISMATCH; 250 251 if (ops->co_groups == NULL) 252 return -NLE_OPNOTSUPP; 253 254 for (i = 0; i < mngr->cm_nassocs; i++) 255 if (mngr->cm_assocs[i].ca_cache == cache) 256 break; 257 258 if (i >= mngr->cm_nassocs) { 259 return -NLE_RANGE; 260 } 261 262 mngr->cm_assocs[i].ca_change_v2 = cb; 263 mngr->cm_assocs[i].ca_change_data = data; 264 265 return 0; 266 } 267 268 /** 269 * Add cache to cache manager 270 * @arg mngr Cache manager. 271 * @arg cache Cache to be added to cache manager 272 * @arg cb Function to be called upon changes. 273 * @arg data Argument passed on to change callback 274 * 275 * Adds cache to the manager. The operation will trigger a full 276 * dump request from the kernel to initially fill the contents 277 * of the cache. The manager will subscribe to the notification group 278 * of the cache and keep track of any further changes. 279 * 280 * The user is responsible for calling nl_cache_mngr_poll() or monitor 281 * the socket and call nl_cache_mngr_data_ready() to allow the library 282 * to process netlink notification events. 283 * 284 * @see nl_cache_mngr_poll() 285 * @see nl_cache_mngr_data_ready() 286 * 287 * @return 0 on success or a negative error code. 288 * @return -NLE_PROTO_MISMATCH Protocol mismatch between cache manager and 289 * cache type 290 * @return -NLE_OPNOTSUPP Cache type does not support updates 291 * @return -NLE_EXIST Cache of this type already being managed 292 */ nl_cache_mngr_add_cache(struct nl_cache_mngr * mngr,struct nl_cache * cache,change_func_t cb,void * data)293 int nl_cache_mngr_add_cache(struct nl_cache_mngr *mngr, struct nl_cache *cache, 294 change_func_t cb, void *data) 295 { 296 struct nl_cache_ops *ops; 297 struct nl_af_group *grp; 298 int err, i; 299 300 ops = cache->c_ops; 301 if (!ops) 302 return -NLE_INVAL; 303 304 if (ops->co_protocol != mngr->cm_protocol) 305 return -NLE_PROTO_MISMATCH; 306 307 if (ops->co_groups == NULL) 308 return -NLE_OPNOTSUPP; 309 310 for (i = 0; i < mngr->cm_nassocs; i++) 311 if (mngr->cm_assocs[i].ca_cache && 312 mngr->cm_assocs[i].ca_cache->c_ops == ops) 313 return -NLE_EXIST; 314 315 for (i = 0; i < mngr->cm_nassocs; i++) 316 if (!mngr->cm_assocs[i].ca_cache) 317 break; 318 319 if (i >= mngr->cm_nassocs) { 320 struct nl_cache_assoc *cm_assocs; 321 int cm_nassocs = mngr->cm_nassocs + NASSOC_EXPAND; 322 323 cm_assocs = realloc(mngr->cm_assocs, 324 cm_nassocs * sizeof(struct nl_cache_assoc)); 325 if (cm_assocs == NULL) 326 return -NLE_NOMEM; 327 328 memset(cm_assocs + mngr->cm_nassocs, 0, 329 NASSOC_EXPAND * sizeof(struct nl_cache_assoc)); 330 mngr->cm_assocs = cm_assocs; 331 mngr->cm_nassocs = cm_nassocs; 332 333 NL_DBG(1, "Increased capacity of cache manager %p " \ 334 "to %d\n", mngr, mngr->cm_nassocs); 335 } 336 337 for (grp = ops->co_groups; grp->ag_group; grp++) { 338 err = nl_socket_add_membership(mngr->cm_sock, grp->ag_group); 339 if (err < 0) 340 return err; 341 } 342 343 err = nl_cache_refill(mngr->cm_sync_sock, cache); 344 if (err < 0) 345 goto errout_drop_membership; 346 347 mngr->cm_assocs[i].ca_cache = cache; 348 mngr->cm_assocs[i].ca_change = cb; 349 mngr->cm_assocs[i].ca_change_data = data; 350 351 if (mngr->cm_flags & NL_AUTO_PROVIDE) 352 nl_cache_mngt_provide(cache); 353 354 NL_DBG(1, "Added cache %p <%s> to cache manager %p\n", 355 cache, nl_cache_name(cache), mngr); 356 357 return 0; 358 359 errout_drop_membership: 360 for (grp = ops->co_groups; grp->ag_group; grp++) 361 nl_socket_drop_membership(mngr->cm_sock, grp->ag_group); 362 363 return err; 364 } 365 366 /** 367 * Add cache to cache manager 368 * @arg mngr Cache manager. 369 * @arg cache Cache to be added to cache manager 370 * @arg cb V2 function to be called upon changes. 371 * @arg data Argument passed on to change callback 372 * 373 * Adds cache to the manager. The operation will trigger a full 374 * dump request from the kernel to initially fill the contents 375 * of the cache. The manager will subscribe to the notification group 376 * of the cache and keep track of any further changes. 377 * 378 * The user is responsible for calling nl_cache_mngr_poll() or monitor 379 * the socket and call nl_cache_mngr_data_ready() to allow the library 380 * to process netlink notification events. 381 * 382 * @see nl_cache_mngr_poll() 383 * @see nl_cache_mngr_data_ready() 384 * 385 * @return 0 on success or a negative error code. 386 * @return -NLE_PROTO_MISMATCH Protocol mismatch between cache manager and 387 * cache type 388 * @return -NLE_OPNOTSUPP Cache type does not support updates 389 * @return -NLE_EXIST Cache of this type already being managed 390 */ nl_cache_mngr_add_cache_v2(struct nl_cache_mngr * mngr,struct nl_cache * cache,change_func_v2_t cb,void * data)391 int nl_cache_mngr_add_cache_v2(struct nl_cache_mngr *mngr, struct nl_cache *cache, 392 change_func_v2_t cb, void *data) { 393 int err; 394 err = nl_cache_mngr_add_cache(mngr, cache, NULL, NULL); 395 if (err < 0) 396 return err; 397 398 return nl_cache_mngr_set_change_func_v2(mngr, cache, cb, data); 399 } 400 401 /** 402 * Add cache to cache manager 403 * @arg mngr Cache manager. 404 * @arg name Name of cache to keep track of 405 * @arg cb Function to be called upon changes. 406 * @arg data Argument passed on to change callback 407 * @arg result Pointer to store added cache (optional) 408 * 409 * Allocates a new cache of the specified type and adds it to the manager. 410 * The operation will trigger a full dump request from the kernel to 411 * initially fill the contents of the cache. The manager will subscribe 412 * to the notification group of the cache and keep track of any further 413 * changes. 414 * 415 * The user is responsible for calling nl_cache_mngr_poll() or monitor 416 * the socket and call nl_cache_mngr_data_ready() to allow the library 417 * to process netlink notification events. 418 * 419 * @note Versions up to 3.4.0 actually required the result argument, preventing 420 * NULL to be passed. 421 * 422 * @see nl_cache_mngr_poll() 423 * @see nl_cache_mngr_data_ready() 424 * 425 * @return 0 on success or a negative error code. 426 * @return -NLE_NOCACHE Unknown cache type 427 * @return -NLE_PROTO_MISMATCH Protocol mismatch between cache manager and 428 * cache type 429 * @return -NLE_OPNOTSUPP Cache type does not support updates 430 * @return -NLE_EXIST Cache of this type already being managed 431 */ nl_cache_mngr_add(struct nl_cache_mngr * mngr,const char * name,change_func_t cb,void * data,struct nl_cache ** result)432 int nl_cache_mngr_add(struct nl_cache_mngr *mngr, const char *name, 433 change_func_t cb, void *data, struct nl_cache **result) 434 { 435 struct nl_cache_ops *ops; 436 struct nl_cache *cache; 437 int err; 438 439 ops = nl_cache_ops_lookup_safe(name); 440 if (!ops) 441 return -NLE_NOCACHE; 442 443 cache = nl_cache_alloc(ops); 444 nl_cache_ops_put(ops); 445 if (!cache) 446 return -NLE_NOMEM; 447 448 err = nl_cache_mngr_add_cache(mngr, cache, cb, data); 449 if (err < 0) 450 goto errout_free_cache; 451 452 if (result) 453 *result = cache; 454 return 0; 455 456 errout_free_cache: 457 nl_cache_free(cache); 458 459 return err; 460 } 461 462 /** 463 * Get socket file descriptor 464 * @arg mngr Cache Manager 465 * 466 * Get the file descriptor of the socket associated with the manager. 467 * 468 * @note Do not use the socket for anything besides receiving 469 * notifications. 470 */ nl_cache_mngr_get_fd(struct nl_cache_mngr * mngr)471 int nl_cache_mngr_get_fd(struct nl_cache_mngr *mngr) 472 { 473 return nl_socket_get_fd(mngr->cm_sock); 474 } 475 476 /** 477 * Check for event notifications 478 * @arg mngr Cache Manager 479 * @arg timeout Upper limit poll() will block, in milliseconds. 480 * 481 * Causes poll() to be called to check for new event notifications 482 * being available. Calls nl_cache_mngr_data_ready() to process 483 * available data. 484 * 485 * This functionally is ideally called regularly during an idle 486 * period. 487 * 488 * A timeout can be specified in milliseconds to limit the time the 489 * function will wait for updates. 490 * 491 * @see nl_cache_mngr_data_ready() 492 * 493 * @return The number of messages processed or a negative error code. 494 */ nl_cache_mngr_poll(struct nl_cache_mngr * mngr,int timeout)495 int nl_cache_mngr_poll(struct nl_cache_mngr *mngr, int timeout) 496 { 497 int ret; 498 struct pollfd fds = { 499 .fd = nl_socket_get_fd(mngr->cm_sock), 500 .events = POLLIN, 501 }; 502 503 NL_DBG(3, "Cache manager %p, poll() fd %d\n", mngr, fds.fd); 504 ret = poll(&fds, 1, timeout); 505 NL_DBG(3, "Cache manager %p, poll() returned %d\n", mngr, ret); 506 if (ret < 0) { 507 NL_DBG(4, "nl_cache_mngr_poll(%p): poll() failed with %d (%s)\n", 508 mngr, errno, nl_strerror_l(errno)); 509 return -nl_syserr2nlerr(errno); 510 } 511 512 /* No events, return */ 513 if (ret == 0) 514 return 0; 515 516 return nl_cache_mngr_data_ready(mngr); 517 } 518 519 /** 520 * Receive available event notifications 521 * @arg mngr Cache manager 522 * 523 * This function can be called if the socket associated to the manager 524 * contains updates to be received. This function should only be used 525 * if nl_cache_mngr_poll() is not used. 526 * 527 * The function will process messages until there is no more data to 528 * be read from the socket. 529 * 530 * @see nl_cache_mngr_poll() 531 * 532 * @return The number of messages processed or a negative error code. 533 */ nl_cache_mngr_data_ready(struct nl_cache_mngr * mngr)534 int nl_cache_mngr_data_ready(struct nl_cache_mngr *mngr) 535 { 536 int err, nread = 0; 537 struct nl_cb *cb; 538 539 NL_DBG(2, "Cache manager %p, reading new data from fd %d\n", 540 mngr, nl_socket_get_fd(mngr->cm_sock)); 541 542 cb = nl_cb_clone(mngr->cm_sock->s_cb); 543 if (cb == NULL) 544 return -NLE_NOMEM; 545 546 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, event_input, mngr); 547 548 while ((err = nl_recvmsgs_report(mngr->cm_sock, cb)) > 0) { 549 NL_DBG(2, "Cache manager %p, recvmsgs read %d messages\n", 550 mngr, err); 551 nread += err; 552 } 553 554 nl_cb_put(cb); 555 if (err < 0 && err != -NLE_AGAIN) 556 return err; 557 558 return nread; 559 } 560 561 /** 562 * Print information about cache manager 563 * @arg mngr Cache manager 564 * @arg p Dumping parameters 565 * 566 * Prints information about the cache manager including all managed caches. 567 * 568 * @note This is a debugging function. 569 */ nl_cache_mngr_info(struct nl_cache_mngr * mngr,struct nl_dump_params * p)570 void nl_cache_mngr_info(struct nl_cache_mngr *mngr, struct nl_dump_params *p) 571 { 572 char buf[128]; 573 int i; 574 575 nl_dump_line(p, "cache-manager <%p>\n", mngr); 576 nl_dump_line(p, " .protocol = %s\n", 577 nl_nlfamily2str(mngr->cm_protocol, buf, sizeof(buf))); 578 nl_dump_line(p, " .flags = %#x\n", mngr->cm_flags); 579 nl_dump_line(p, " .nassocs = %u\n", mngr->cm_nassocs); 580 nl_dump_line(p, " .sock = <%p>\n", mngr->cm_sock); 581 582 for (i = 0; i < mngr->cm_nassocs; i++) { 583 struct nl_cache_assoc *assoc = &mngr->cm_assocs[i]; 584 585 if (assoc->ca_cache) { 586 nl_dump_line(p, " .cache[%d] = <%p> {\n", i, assoc->ca_cache); 587 nl_dump_line(p, " .name = %s\n", assoc->ca_cache->c_ops->co_name); 588 nl_dump_line(p, " .change_func = <%p>\n", assoc->ca_change); 589 nl_dump_line(p, " .change_data = <%p>\n", assoc->ca_change_data); 590 nl_dump_line(p, " .nitems = %u\n", nl_cache_nitems(assoc->ca_cache)); 591 nl_dump_line(p, " .objects = {\n"); 592 593 p->dp_prefix += 6; 594 nl_cache_dump(assoc->ca_cache, p); 595 p->dp_prefix -= 6; 596 597 nl_dump_line(p, " }\n"); 598 nl_dump_line(p, " }\n"); 599 } 600 } 601 } 602 603 /** 604 * Free cache manager and all caches. 605 * @arg mngr Cache manager. 606 * 607 * Release all resources held by a cache manager. 608 */ nl_cache_mngr_free(struct nl_cache_mngr * mngr)609 void nl_cache_mngr_free(struct nl_cache_mngr *mngr) 610 { 611 int i; 612 613 if (!mngr) 614 return; 615 616 if (mngr->cm_sock) 617 nl_close(mngr->cm_sock); 618 619 if (mngr->cm_sync_sock) { 620 nl_close(mngr->cm_sync_sock); 621 nl_socket_free(mngr->cm_sync_sock); 622 } 623 624 if (mngr->cm_flags & NL_ALLOCATED_SOCK) 625 nl_socket_free(mngr->cm_sock); 626 627 for (i = 0; i < mngr->cm_nassocs; i++) { 628 if (mngr->cm_assocs[i].ca_cache) { 629 nl_cache_mngt_unprovide(mngr->cm_assocs[i].ca_cache); 630 nl_cache_free(mngr->cm_assocs[i].ca_cache); 631 } 632 } 633 634 free(mngr->cm_assocs); 635 636 NL_DBG(1, "Cache manager %p freed\n", mngr); 637 638 free(mngr); 639 } 640 641 /** @} */ 642