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