ip: Protocol Independent IP Neighbors
[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/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  */
32 typedef struct ip_neighbor_watch_db_t_
33 {
34   mhash_t ipnwdb_hash;
35 } ip_neighbor_watch_db_t;
36
37 static ip_neighbor_watch_db_t ipnw_db;
38
39 static uword
40 ip_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* */
69 VLIB_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
77 static clib_error_t *
78 want_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
107 VL_MSG_API_REAPER_FUNCTION (want_ip_neighbor_events_reaper);
108
109 static int
110 ip_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
116 void
117 ip_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
147 void
148 ip_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
180 static void
181 ip_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
196 void
197 ip_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
229 static clib_error_t *
230 ip_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* */
254 VLIB_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
261 static clib_error_t *
262 ip_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* */
270 VLIB_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  */