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