ba7f354e9faebe8340caa6a7392aa347d02f8554
[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);
33     else
34         mfib_table_lock(fib_index, proto);
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);
43     else
44         mfib_table_unlock(fib_index, proto);
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);
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   };
233
234   vec_add1(proxy->dhcp_servers, server);
235
236   return (new);
237 }
238
239 typedef struct dhcp4_proxy_dump_walk_ctx_t_
240 {
241     fib_protocol_t proto;
242     void *opaque;
243     u32 context;
244 } dhcp_proxy_dump_walk_cxt_t;
245
246 static int
247 dhcp_proxy_dump_walk (dhcp_proxy_t *proxy,
248                       void *arg)
249 {
250   dhcp_proxy_dump_walk_cxt_t *ctx = arg;
251
252   dhcp_send_details(ctx->proto,
253                     ctx->opaque,
254                     ctx->context,
255                     proxy);
256
257   return (1);
258 }
259
260 void
261 dhcp_proxy_dump (fib_protocol_t proto,
262                  void *opaque,
263                  u32 context)
264 {
265     dhcp_proxy_dump_walk_cxt_t ctx =  {
266         .proto = proto,
267         .opaque = opaque,
268         .context = context,
269     };
270     dhcp_proxy_walk(proto, dhcp_proxy_dump_walk, &ctx);
271 }
272
273 int
274 dhcp_vss_show_walk (dhcp_vss_t *vss,
275                     u32 rx_table_id,
276                     void *ctx)
277 {
278     vlib_main_t * vm = ctx;
279
280     vlib_cli_output (vm, "%=6d%=6d%=12d",
281                      rx_table_id,
282                      vss->oui,
283                      vss->fib_id);
284
285     return (1);
286 }
287
288 int dhcp_proxy_set_vss (fib_protocol_t proto,
289                         u32 tbl_id,
290                         u32 oui,
291                         u32 fib_id, 
292                         int is_del)
293 {
294   dhcp_proxy_main_t *dm = &dhcp_proxy_main;
295   dhcp_vss_t *v = NULL;
296   u32  rx_fib_index;
297   int rc = 0;
298   
299   if (proto == FIB_PROTOCOL_IP4)
300       rx_fib_index = fib_table_find_or_create_and_lock(proto, tbl_id);
301   else
302       rx_fib_index = mfib_table_find_or_create_and_lock(proto, tbl_id);
303   v = dhcp_get_vss_info(dm, rx_fib_index, proto);
304
305   if (NULL != v)
306   {
307       if (is_del)
308       {
309           /* release the lock held on the table when the VSS
310            * info was created */
311           dhcp_proxy_rx_table_unlock (proto, rx_fib_index);
312
313           pool_put (dm->vss[proto], v);
314           dm->vss_index_by_rx_fib_index[proto][rx_fib_index] = ~0;
315       }
316       else
317       {
318           /* this is a modify */
319           v->fib_id = fib_id;
320           v->oui = oui;
321       }
322   }
323   else
324   {
325       if (is_del)
326           rc = VNET_API_ERROR_NO_SUCH_ENTRY;
327       else
328       {
329           /* create a new entry */
330           vec_validate_init_empty(dm->vss_index_by_rx_fib_index[proto],
331                                   rx_fib_index, ~0);
332
333           /* hold a lock on the table whilst the VSS info exist */
334           pool_get (dm->vss[proto], v);
335           v->fib_id = fib_id;
336           v->oui = oui;
337
338           dm->vss_index_by_rx_fib_index[proto][rx_fib_index] =
339               v - dm->vss[proto];
340           dhcp_proxy_rx_table_lock (proto, rx_fib_index);
341       }
342   }
343
344   /* Release the lock taken during the create_or_lock at the start */
345   dhcp_proxy_rx_table_unlock (proto, rx_fib_index);
346
347   return (rc);
348 }