1/*
2 * ip_neighboor_watch.c; IP neighbor watching
3 *
4 * Copyright (c) 2019 Cisco and/or its affiliates.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at:
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include <vnet/ip-neighbor/ip_neighbor.h>
19#include <vnet/ip/ip_types_api.h>
20#include <vnet/ethernet/ethernet_types_api.h>
21
22#include <vnet/ip-neighbor/ip_neighbor.api_enum.h>
23#include <vnet/ip-neighbor/ip_neighbor.api_types.h>
24
25#include <vlibmemory/api.h>
26
27/**
28 * Database of registered watchers
29 * The key for a watcher is {type, sw_if_index, addreess}
30 * interface=~0 / address=all-zeros imples any.
31 */
32typedef struct ip_neighbor_watch_db_t_
33{
34  mhash_t ipnwdb_hash;
35} ip_neighbor_watch_db_t;
36
37static ip_neighbor_watch_db_t ipnw_db;
38
39static uword
40ip_neighbor_event_process (vlib_main_t * vm,
41			   vlib_node_runtime_t * rt, vlib_frame_t * f)
42{
43  ip_neighbor_event_t *ipne, *ipnes = NULL;
44  uword event_type = ~0;
45
46  while (1)
47    {
48      vlib_process_wait_for_event (vm);
49
50      ipnes = vlib_process_get_event_data (vm, &event_type);
51
52      switch (event_type)
53	{
54	default:
55	  vec_foreach (ipne, ipnes) ip_neighbor_handle_event (ipne);
56	  break;
57
58	case ~0:
59	  /* timeout - */
60	  break;
61	}
62
63      vec_reset_length (ipnes);
64    }
65  return 0;
66}
67
68/* *INDENT-OFF* */
69VLIB_REGISTER_NODE (ip_neighbor_event_process_node) = {
70  .function = ip_neighbor_event_process,
71  .type = VLIB_NODE_TYPE_PROCESS,
72  .name = "ip-neighbor-event",
73};
74/* *INDENT-ON* */
75
76
77static clib_error_t *
78want_ip_neighbor_events_reaper (u32 client_index)
79{
80  ip_neighbor_key_t *key, *empty_keys = NULL;
81  ip_neighbor_watcher_t *watchers;
82  uword *v;
83  i32 pos;
84
85  /* walk the entire IP neighbour DB and removes the client's registrations */
86  /* *INDENT-OFF* */
87  mhash_foreach(key, v, &ipnw_db.ipnwdb_hash,
88  ({
89    watchers = (ip_neighbor_watcher_t*) *v;
90
91    vec_foreach_index_backwards (pos, watchers) {
92      if (watchers[pos].ipw_client == client_index)
93        vec_del1(watchers, pos);
94    }
95
96    if (vec_len(watchers) == 0)
97      vec_add1 (empty_keys, *key);
98  }));
99  /* *INDENT-OFF* */
100
101  vec_foreach (key, empty_keys)
102    mhash_unset (&ipnw_db.ipnwdb_hash, key, NULL);
103  vec_free (empty_keys);
104  return (NULL);
105}
106
107VL_MSG_API_REAPER_FUNCTION (want_ip_neighbor_events_reaper);
108
109static int
110ip_neighbor_watch_cmp (const ip_neighbor_watcher_t * w1,
111                       const ip_neighbor_watcher_t * w2)
112{
113  return (0 == clib_memcmp (w1, w2, sizeof(*w1)));
114}
115
116void
117ip_neighbor_watch (const ip46_address_t * ip,
118		   ip46_type_t type,
119		   u32 sw_if_index,
120                   const ip_neighbor_watcher_t * watch)
121{
122  ip_neighbor_key_t key = {
123    .ipnk_ip = *ip,
124    .ipnk_sw_if_index = (sw_if_index == 0 ? ~0 : sw_if_index),
125    .ipnk_type = type,
126  };
127  ip_neighbor_watcher_t *ipws = NULL;
128  uword *p;
129
130  p = mhash_get (&ipnw_db.ipnwdb_hash, &key);
131
132  if (p)
133    {
134      ipws = (ip_neighbor_watcher_t*) p[0];
135
136      if (~0 != vec_search_with_function (ipws, watch,
137                                          ip_neighbor_watch_cmp))
138        /* duplicate */
139        return;
140    }
141
142  vec_add1 (ipws, *watch);
143
144  mhash_set (&ipnw_db.ipnwdb_hash, &key, (uword) ipws, NULL);
145}
146
147void
148ip_neighbor_unwatch (const ip46_address_t * ip,
149		     ip46_type_t type,
150		     u32 sw_if_index,
151                     const ip_neighbor_watcher_t * watch)
152{
153  ip_neighbor_key_t key = {
154    .ipnk_ip = *ip,
155    .ipnk_sw_if_index = (sw_if_index == 0 ? ~0 : sw_if_index),
156    .ipnk_type = type,
157  };
158  ip_neighbor_watcher_t *ipws = NULL;
159  uword *p;
160  u32 pos;
161
162  p = mhash_get (&ipnw_db.ipnwdb_hash, &key);
163
164  if (!p)
165    return;
166
167  ipws = (ip_neighbor_watcher_t*) p[0];
168
169  pos = vec_search_with_function (ipws, watch, ip_neighbor_watch_cmp);
170
171  if (~0 == pos)
172    return;
173
174  vec_del1 (ipws, pos);
175
176  if (vec_len(ipws) == 0)
177    mhash_unset (&ipnw_db.ipnwdb_hash, &key, NULL);
178}
179
180static void
181ip_neighbor_signal (ip_neighbor_watcher_t *watchers, index_t ipni)
182{
183  ip_neighbor_watcher_t *watcher;
184
185  vec_foreach (watcher, watchers) {
186    ip_neighbor_event_t *ipne;
187
188    ipne = vlib_process_signal_event_data (vlib_get_main(),
189                                           ip_neighbor_event_process_node.index,
190                                           0, 1, sizeof(*ipne));
191    ipne->ipne_watch = *watcher;
192    ipne->ipne_index = ipni;
193  }
194}
195
196void
197ip_neighbor_publish (index_t ipni)
198{
199  const ip_neighbor_t *ipn;
200  ip_neighbor_key_t key;
201  uword *p;
202
203  ipn = ip_neighbor_get (ipni);
204
205  clib_memcpy (&key, ipn->ipn_key, sizeof (key));
206
207  /* Search the DB from longest to shortest key */
208  p = mhash_get (&ipnw_db.ipnwdb_hash, &key);
209
210  if (p) {
211    ip_neighbor_signal ((ip_neighbor_watcher_t*) p[0], ipni);
212  }
213
214  ip46_address_reset (&key.ipnk_ip);
215  p = mhash_get (&ipnw_db.ipnwdb_hash, &key);
216
217  if (p) {
218    ip_neighbor_signal ((ip_neighbor_watcher_t*) p[0], ipni);
219  }
220
221  key.ipnk_sw_if_index = ~0;
222  p = mhash_get (&ipnw_db.ipnwdb_hash, &key);
223
224  if (p) {
225    ip_neighbor_signal ((ip_neighbor_watcher_t*) p[0], ipni);
226  }
227}
228
229static clib_error_t *
230ip_neighbor_watchers_show (vlib_main_t * vm,
231                           unformat_input_t * input,
232                           vlib_cli_command_t * cmd)
233{
234  ip_neighbor_watcher_t *watchers, *watcher;
235  ip_neighbor_key_t *key;
236  uword *v;
237
238  /* *INDENT-OFF* */
239  mhash_foreach(key, v, &ipnw_db.ipnwdb_hash,
240  ({
241    watchers = (ip_neighbor_watcher_t*) *v;
242
243    ASSERT(vec_len(watchers));
244    vlib_cli_output (vm, "Key: %U", format_ip_neighbor_key, key);
245
246    vec_foreach (watcher, watchers)
247      vlib_cli_output (vm, "  %U", format_ip_neighbor_watcher, watcher);
248  }));
249  /* *INDENT-ON* */
250  return (NULL);
251}
252
253/* *INDENT-OFF* */
254VLIB_CLI_COMMAND (show_ip_neighbor_watchers_cmd_node, static) = {
255  .path = "show ip neighbor-watcher",
256  .function = ip_neighbor_watchers_show,
257  .short_help = "show ip neighbors-watcher",
258};
259/* *INDENT-ON* */
260
261static clib_error_t *
262ip_neighbor_watch_init (vlib_main_t * vm)
263{
264  mhash_init (&ipnw_db.ipnwdb_hash,
265	      sizeof (ip_neighbor_watcher_t *), sizeof (ip_neighbor_key_t));
266  return (NULL);
267}
268
269/* *INDENT-OFF* */
270VLIB_INIT_FUNCTION (ip_neighbor_watch_init) =
271{
272  .runs_after = VLIB_INITS("ip_neighbor_init"),
273};
274/* *INDENT-ON* */
275
276
277/*
278 * fd.io coding-style-patch-verification: ON
279 *
280 * Local Variables:
281 * eval: (c-set-style "gnu")
282 * End:
283 */
284