mactime: add a "top" command to watch device stats
[vpp.git] / src / vnet / dhcp / dhcp_proxy.c
1 /*
2  * proxy_node.c: common dhcp v4 and v6 proxy node processing
3  *
4  * Copyright (c) 2013 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/dhcp/dhcp_proxy.h>
19 #include <vnet/fib/fib_table.h>
20 #include <vnet/mfib/mfib_table.h>
21
22 /**
23  * @brief Shard 4/6 instance of DHCP main
24  */
25 dhcp_proxy_main_t dhcp_proxy_main;
26
27 static void
28 dhcp_proxy_rx_table_lock (fib_protocol_t proto, u32 fib_index)
29 {
30   if (FIB_PROTOCOL_IP4 == proto)
31     fib_table_lock (fib_index, proto, FIB_SOURCE_DHCP);
32   else
33     mfib_table_lock (fib_index, proto, MFIB_SOURCE_DHCP);
34 }
35
36 static void
37 dhcp_proxy_rx_table_unlock (fib_protocol_t proto, u32 fib_index)
38 {
39   if (FIB_PROTOCOL_IP4 == proto)
40     fib_table_unlock (fib_index, proto, FIB_SOURCE_DHCP);
41   else
42     mfib_table_unlock (fib_index, proto, MFIB_SOURCE_DHCP);
43 }
44
45 u32
46 dhcp_proxy_rx_table_get_table_id (fib_protocol_t proto, u32 fib_index)
47 {
48   if (FIB_PROTOCOL_IP4 == proto)
49     {
50       fib_table_t *fib;
51
52       fib = fib_table_get (fib_index, proto);
53
54       return (fib->ft_table_id);
55     }
56   else
57     {
58       mfib_table_t *mfib;
59
60       mfib = mfib_table_get (fib_index, proto);
61
62       return (mfib->mft_table_id);
63     }
64 }
65
66 void
67 dhcp_proxy_walk (fib_protocol_t proto, dhcp_proxy_walk_fn_t fn, void *ctx)
68 {
69   dhcp_proxy_main_t *dpm = &dhcp_proxy_main;
70   dhcp_proxy_t *server;
71   u32 server_index, i;
72
73   vec_foreach_index (i, dpm->dhcp_server_index_by_rx_fib_index[proto])
74   {
75     server_index = dpm->dhcp_server_index_by_rx_fib_index[proto][i];
76     if (~0 == server_index)
77       continue;
78
79     server = pool_elt_at_index (dpm->dhcp_servers[proto], server_index);
80
81     if (!fn (server, ctx))
82       break;
83   }
84 }
85
86 void
87 dhcp_vss_walk (fib_protocol_t proto, dhcp_vss_walk_fn_t fn, void *ctx)
88 {
89   dhcp_proxy_main_t *dpm = &dhcp_proxy_main;
90   mfib_table_t *mfib;
91   dhcp_vss_t *vss;
92   u32 vss_index, i;
93   fib_table_t *fib;
94
95   vec_foreach_index (i, dpm->vss_index_by_rx_fib_index[proto])
96   {
97     vss_index = dpm->vss_index_by_rx_fib_index[proto][i];
98     if (~0 == vss_index)
99       continue;
100
101     vss = pool_elt_at_index (dpm->vss[proto], vss_index);
102
103     if (FIB_PROTOCOL_IP4 == proto)
104       {
105         fib = fib_table_get (i, proto);
106
107         if (!fn (vss, fib->ft_table_id, ctx))
108           break;
109       }
110     else
111       {
112         mfib = mfib_table_get (i, proto);
113
114         if (!fn (vss, mfib->mft_table_id, ctx))
115           break;
116       }
117   }
118 }
119
120 static u32
121 dhcp_proxy_server_find (dhcp_proxy_t * proxy,
122                         fib_protocol_t proto,
123                         ip46_address_t * addr, u32 server_table_id)
124 {
125   dhcp_server_t *server;
126   u32 ii, fib_index;
127
128   vec_foreach_index (ii, proxy->dhcp_servers)
129   {
130     server = &proxy->dhcp_servers[ii];
131     fib_index = fib_table_find (proto, server_table_id);
132
133     if (ip46_address_is_equal (&server->dhcp_server,
134                                addr) &&
135         (server->server_fib_index == fib_index))
136       {
137         return (ii);
138       }
139   }
140   return (~0);
141 }
142
143 int
144 dhcp_proxy_server_del (fib_protocol_t proto,
145                        u32 rx_fib_index,
146                        ip46_address_t * addr, u32 server_table_id)
147 {
148   dhcp_proxy_main_t *dpm = &dhcp_proxy_main;
149   dhcp_proxy_t *proxy = 0;
150
151   proxy = dhcp_get_proxy (dpm, rx_fib_index, proto);
152
153   if (NULL != proxy)
154     {
155       dhcp_server_t *server;
156       u32 index;
157
158       index = dhcp_proxy_server_find (proxy, proto, addr, server_table_id);
159
160       if (~0 != index)
161         {
162           server = &proxy->dhcp_servers[index];
163           fib_table_unlock (server->server_fib_index, proto, FIB_SOURCE_DHCP);
164
165           vec_del1 (proxy->dhcp_servers, index);
166
167           if (0 == vec_len (proxy->dhcp_servers))
168             {
169               /* no servers left, delete the proxy config */
170               dpm->dhcp_server_index_by_rx_fib_index[proto][rx_fib_index] =
171                 ~0;
172               vec_free (proxy->dhcp_servers);
173               pool_put (dpm->dhcp_servers[proto], proxy);
174               return (1);
175             }
176         }
177     }
178
179   /* the proxy still exists */
180   return (0);
181 }
182
183 int
184 dhcp_proxy_server_add (fib_protocol_t proto,
185                        ip46_address_t * addr,
186                        ip46_address_t * src_address,
187                        u32 rx_fib_index, u32 server_table_id)
188 {
189   dhcp_proxy_main_t *dpm = &dhcp_proxy_main;
190   dhcp_proxy_t *proxy = 0;
191   int new = 0;
192
193   proxy = dhcp_get_proxy (dpm, rx_fib_index, proto);
194
195   if (NULL == proxy)
196     {
197       vec_validate_init_empty (dpm->dhcp_server_index_by_rx_fib_index[proto],
198                                rx_fib_index, ~0);
199
200       pool_get (dpm->dhcp_servers[proto], proxy);
201       clib_memset (proxy, 0, sizeof (*proxy));
202       new = 1;
203
204       dpm->dhcp_server_index_by_rx_fib_index[proto][rx_fib_index] =
205         proxy - dpm->dhcp_servers[proto];
206
207       proxy->dhcp_src_address = *src_address;
208       proxy->rx_fib_index = rx_fib_index;
209     }
210   else
211     {
212       if (~0 != dhcp_proxy_server_find (proxy, proto, addr, server_table_id))
213         {
214           return (new);
215         }
216     }
217
218   dhcp_server_t server = {
219     .dhcp_server = *addr,
220     .server_fib_index = fib_table_find_or_create_and_lock (proto,
221                                                            server_table_id,
222                                                            FIB_SOURCE_DHCP),
223   };
224
225   vec_add1 (proxy->dhcp_servers, server);
226
227   return (new);
228 }
229
230 typedef struct dhcp4_proxy_dump_walk_ctx_t_
231 {
232   fib_protocol_t proto;
233   void *opaque;
234   u32 context;
235 } dhcp_proxy_dump_walk_cxt_t;
236
237 static int
238 dhcp_proxy_dump_walk (dhcp_proxy_t * proxy, void *arg)
239 {
240   dhcp_proxy_dump_walk_cxt_t *ctx = arg;
241
242   dhcp_send_details (ctx->proto, ctx->opaque, ctx->context, proxy);
243
244   return (1);
245 }
246
247 void
248 dhcp_proxy_dump (fib_protocol_t proto, void *opaque, u32 context)
249 {
250   dhcp_proxy_dump_walk_cxt_t ctx = {
251     .proto = proto,
252     .opaque = opaque,
253     .context = context,
254   };
255   dhcp_proxy_walk (proto, dhcp_proxy_dump_walk, &ctx);
256 }
257
258 int
259 dhcp_vss_show_walk (dhcp_vss_t * vss, u32 rx_table_id, void *ctx)
260 {
261   vlib_main_t *vm = ctx;
262
263   if (vss->vss_type == VSS_TYPE_VPN_ID)
264     {
265       u32 oui = ((u32) vss->vpn_id[0] << 16) + ((u32) vss->vpn_id[1] << 8)
266         + ((u32) vss->vpn_id[2]);
267       u32 fib_id = ((u32) vss->vpn_id[3] << 24) + ((u32) vss->vpn_id[4] << 16)
268         + ((u32) vss->vpn_id[5] << 8) + ((u32) vss->vpn_id[6]);
269       vlib_cli_output (vm, " fib_table: %d  oui: %d vpn_index: %d",
270                        rx_table_id, oui, fib_id);
271     }
272   else if (vss->vss_type == VSS_TYPE_ASCII)
273     vlib_cli_output (vm, " fib_table: %d  vpn_id: %s",
274                      rx_table_id, vss->vpn_ascii_id);
275   else
276     vlib_cli_output (vm, " fib_table: %d  default global vpn", rx_table_id);
277
278   return (1);
279 }
280
281 void
282 update_vss (dhcp_vss_t * v,
283             u8 vss_type, u8 * vpn_ascii_id, u32 oui, u32 vpn_index)
284 {
285   v->vss_type = vss_type;
286   if (v->vpn_ascii_id)
287     {
288       if (v->vpn_ascii_id == (u8 *) ~ 0)
289         v->vpn_ascii_id = 0;
290       else
291         vec_free (v->vpn_ascii_id);
292     }
293
294   if (vss_type == VSS_TYPE_ASCII)
295     v->vpn_ascii_id = vpn_ascii_id;
296   else if (vss_type == VSS_TYPE_VPN_ID)
297     {
298       v->vpn_id[0] = (oui >> 16) & 0xff;
299       v->vpn_id[1] = (oui >> 8) & 0xff;
300       v->vpn_id[2] = (oui >> 0) & 0xff;
301       v->vpn_id[3] = (vpn_index >> 24) & 0xff;
302       v->vpn_id[4] = (vpn_index >> 16) & 0xff;
303       v->vpn_id[5] = (vpn_index >> 8) & 0xff;
304       v->vpn_id[6] = (vpn_index >> 0) & 0xff;
305     }
306 }
307
308 int
309 dhcp_proxy_set_vss (fib_protocol_t proto,
310                     u32 tbl_id,
311                     u8 vss_type,
312                     u8 * vpn_ascii_id, u32 oui, u32 vpn_index, u8 is_del)
313 {
314   dhcp_proxy_main_t *dm = &dhcp_proxy_main;
315   dhcp_vss_t *v = NULL;
316   u32 rx_fib_index;
317   int rc = 0;
318
319   if (proto == FIB_PROTOCOL_IP4)
320     rx_fib_index = fib_table_find_or_create_and_lock (proto, tbl_id,
321                                                       FIB_SOURCE_DHCP);
322   else
323     rx_fib_index = mfib_table_find_or_create_and_lock (proto, tbl_id,
324                                                        MFIB_SOURCE_DHCP);
325   v = dhcp_get_vss_info (dm, rx_fib_index, proto);
326
327   if (NULL != v)
328     {
329       if (is_del)
330         {
331           /* release the lock held on the table when the VSS
332            * info was created */
333           dhcp_proxy_rx_table_unlock (proto, rx_fib_index);
334
335           vec_free (v->vpn_ascii_id);
336           pool_put (dm->vss[proto], v);
337           dm->vss_index_by_rx_fib_index[proto][rx_fib_index] = ~0;
338         }
339       else
340         {
341           update_vss (v, vss_type, vpn_ascii_id, oui, vpn_index);
342         }
343     }
344   else
345     {
346       if (is_del)
347         rc = VNET_API_ERROR_NO_SUCH_ENTRY;
348       else
349         {
350           /* create a new entry */
351           vec_validate_init_empty (dm->vss_index_by_rx_fib_index[proto],
352                                    rx_fib_index, ~0);
353
354           /* hold a lock on the table whilst the VSS info exist */
355           pool_get (dm->vss[proto], v);
356           update_vss (v, vss_type, vpn_ascii_id, oui, vpn_index);
357           dm->vss_index_by_rx_fib_index[proto][rx_fib_index] =
358             v - dm->vss[proto];
359           dhcp_proxy_rx_table_lock (proto, rx_fib_index);
360         }
361     }
362
363   /* Release the lock taken during the create_or_lock at the start */
364   dhcp_proxy_rx_table_unlock (proto, rx_fib_index);
365
366   return (rc);
367 }
368
369 /*
370  * fd.io coding-style-patch-verification: ON
371  *
372  * Local Variables:
373  * eval: (c-set-style "gnu")
374  * End:
375  */