/* * ip_neighboor_watch.c; IP neighbor watching * * Copyright (c) 2019 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include /** * Database of registered watchers * The key for a watcher is {type, sw_if_index, addreess} * interface=~0 / address=all-zeros imples any. */ typedef struct ip_neighbor_watch_db_t_ { mhash_t ipnwdb_hash; } ip_neighbor_watch_db_t; static ip_neighbor_watch_db_t ipnw_db; static uword ip_neighbor_event_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f) { ip_neighbor_event_t *ipne, *ipnes = NULL; uword event_type = ~0; while (1) { vlib_process_wait_for_event (vm); ipnes = vlib_process_get_event_data (vm, &event_type); switch (event_type) { default: vec_foreach (ipne, ipnes) ip_neighbor_handle_event (ipne); break; case ~0: /* timeout - */ break; } vec_reset_length (ipnes); } return 0; } VLIB_REGISTER_NODE (ip_neighbor_event_process_node) = { .function = ip_neighbor_event_process, .type = VLIB_NODE_TYPE_PROCESS, .name = "ip-neighbor-event", }; static clib_error_t * want_ip_neighbor_events_reaper (u32 client_index) { ip_neighbor_key_t *key, *empty_keys = NULL; ip_neighbor_watcher_t *watchers; uword *v; i32 pos; /* walk the entire IP neighbour DB and removes the client's registrations */ mhash_foreach(key, v, &ipnw_db.ipnwdb_hash, ({ watchers = (ip_neighbor_watcher_t*) *v; vec_foreach_index_backwards (pos, watchers) { if (watchers[pos].ipw_client == client_index) vec_del1(watchers, pos); } if (vec_len(watchers) == 0) vec_add1 (empty_keys, *key); })); vec_foreach (key, empty_keys) mhash_unset (&ipnw_db.ipnwdb_hash, key, NULL); vec_free (empty_keys); return (NULL); } VL_MSG_API_REAPER_FUNCTION (want_ip_neighbor_events_reaper); static int ip_neighbor_watch_cmp (const ip_neighbor_watcher_t * w1, const ip_neighbor_watcher_t * w2) { return (0 == clib_memcmp (w1, w2, sizeof(*w1))); } void ip_neighbor_watch (const ip_address_t * ip, u32 sw_if_index, const ip_neighbor_watcher_t * watch) { ip_neighbor_key_t key = { .ipnk_ip = *ip, .ipnk_sw_if_index = (sw_if_index == 0 ? ~0 : sw_if_index), }; ip_neighbor_watcher_t *ipws = NULL; uword *p; p = mhash_get (&ipnw_db.ipnwdb_hash, &key); if (p) { ipws = (ip_neighbor_watcher_t*) p[0]; if (~0 != vec_search_with_function (ipws, watch, ip_neighbor_watch_cmp)) /* duplicate */ return; } vec_add1 (ipws, *watch); mhash_set (&ipnw_db.ipnwdb_hash, &key, (uword) ipws, NULL); } void ip_neighbor_unwatch (const ip_address_t * ip, u32 sw_if_index, const ip_neighbor_watcher_t * watch) { ip_neighbor_key_t key = { .ipnk_ip = *ip, .ipnk_sw_if_index = (sw_if_index == 0 ? ~0 : sw_if_index), }; ip_neighbor_watcher_t *ipws = NULL; uword *p; u32 pos; p = mhash_get (&ipnw_db.ipnwdb_hash, &key); if (!p) return; ipws = (ip_neighbor_watcher_t*) p[0]; pos = vec_search_with_function (ipws, watch, ip_neighbor_watch_cmp); if (~0 == pos) return; vec_del1 (ipws, pos); if (vec_len(ipws) == 0) mhash_unset (&ipnw_db.ipnwdb_hash, &key, NULL); } static void ip_neighbor_signal (ip_neighbor_watcher_t *watchers, index_t ipni, ip_neighbor_event_flags_t flags) { ip_neighbor_watcher_t *watcher; vec_foreach (watcher, watchers) { ip_neighbor_event_t *ipne; ipne = vlib_process_signal_event_data (vlib_get_main(), ip_neighbor_event_process_node.index, 0, 1, sizeof(*ipne)); ipne->ipne_watch = *watcher; ipne->ipne_flags = flags; ip_neighbor_clone(ip_neighbor_get(ipni), &ipne->ipne_nbr); } } void ip_neighbor_publish (index_t ipni, ip_neighbor_event_flags_t flags) { const ip_neighbor_t *ipn; ip_neighbor_key_t key; uword *p; ipn = ip_neighbor_get (ipni); clib_memcpy (&key, ipn->ipn_key, sizeof (key)); /* Search the DB from longest to shortest key */ p = mhash_get (&ipnw_db.ipnwdb_hash, &key); if (p) { ip_neighbor_signal ((ip_neighbor_watcher_t*) p[0], ipni, flags); } ip_address_reset (&key.ipnk_ip); p = mhash_get (&ipnw_db.ipnwdb_hash, &key); if (p) { ip_neighbor_signal ((ip_neighbor_watcher_t*) p[0], ipni, flags); } key.ipnk_sw_if_index = ~0; p = mhash_get (&ipnw_db.ipnwdb_hash, &key); if (p) { ip_neighbor_signal ((ip_neighbor_watcher_t*) p[0], ipni, flags); } } static clib_error_t * ip_neighbor_watchers_show (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { ip_neighbor_watcher_t *watchers, *watcher; ip_neighbor_key_t *key; uword *v; mhash_foreach(key, v, &ipnw_db.ipnwdb_hash, ({ watchers = (ip_neighbor_watcher_t*) *v; ASSERT(vec_len(watchers)); vlib_cli_output (vm, "Key: %U", format_ip_neighbor_key, key); vec_foreach (watcher, watchers) vlib_cli_output (vm, " %U", format_ip_neighbor_watcher, watcher); })); return (NULL); } VLIB_CLI_COMMAND (show_ip_neighbor_watchers_cmd_node, static) = { .path = "show ip neighbor-watcher", .function = ip_neighbor_watchers_show, .short_help = "show ip neighbors-watcher", }; static clib_error_t * ip_neighbor_watch_init (vlib_main_t * vm) { mhash_init (&ipnw_db.ipnwdb_hash, sizeof (ip_neighbor_watcher_t *), sizeof (ip_neighbor_key_t)); return (NULL); } VLIB_INIT_FUNCTION (ip_neighbor_watch_init) = { .runs_after = VLIB_INITS("ip_neighbor_init"), }; /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */