Add Support of DHCP VSS Type 0 where VPN-ID is ASCII
[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     if (vss->vss_type == VSS_TYPE_VPN_ID)
282       {
283         u32 oui = ((u32) vss->vpn_id[0] << 16) + ((u32) vss->vpn_id[1] << 8)
284             + ((u32) vss->vpn_id[2]);
285         u32 fib_id = ((u32) vss->vpn_id[3] << 24) + ((u32) vss->vpn_id[4] << 16)
286             + ((u32) vss->vpn_id[5] << 8) + ((u32) vss->vpn_id[6]);
287         vlib_cli_output (vm, " fib_table: %d  oui: %d vpn_index: %d",
288                          rx_table_id, oui, fib_id);
289       }
290     else if (vss->vss_type == VSS_TYPE_ASCII)
291         vlib_cli_output (vm, " fib_table: %d  vpn_id: %s",
292                          rx_table_id, vss->vpn_ascii_id);
293     else
294         vlib_cli_output (vm, " fib_table: %d  default global vpn", rx_table_id);
295
296     return (1);
297 }
298
299 void update_vss (dhcp_vss_t *v,
300                  u8 vss_type,
301                  u8 *vpn_ascii_id,
302                  u32 oui,
303                  u32 vpn_index)
304 {
305   v->vss_type = vss_type;
306   if (v->vpn_ascii_id)
307     {
308         if (v->vpn_ascii_id == (u8 *) ~0)
309         v->vpn_ascii_id = 0;
310       else
311         vec_free (v->vpn_ascii_id);
312     }
313
314   if (vss_type == VSS_TYPE_ASCII)
315       v->vpn_ascii_id = vpn_ascii_id;
316   else if (vss_type == VSS_TYPE_VPN_ID)
317     {
318       v->vpn_id[0] = (oui >> 16) & 0xff;
319       v->vpn_id[1] = (oui >> 8) & 0xff;
320       v->vpn_id[2] = (oui >> 0) & 0xff;
321       v->vpn_id[3] = (vpn_index >> 24) & 0xff;
322       v->vpn_id[4] = (vpn_index >> 16) & 0xff;
323       v->vpn_id[5] = (vpn_index >> 8) & 0xff;
324       v->vpn_id[6] = (vpn_index >> 0) & 0xff;
325     }
326 }
327
328 int dhcp_proxy_set_vss (fib_protocol_t proto,
329                         u32 tbl_id,
330                         u8 vss_type,
331                         u8 *vpn_ascii_id,
332                         u32 oui,
333                         u32 vpn_index,
334                         u8 is_del)
335 {
336   dhcp_proxy_main_t *dm = &dhcp_proxy_main;
337   dhcp_vss_t *v = NULL;
338   u32  rx_fib_index;
339   int rc = 0;
340   
341   if (proto == FIB_PROTOCOL_IP4)
342       rx_fib_index = fib_table_find_or_create_and_lock(proto, tbl_id,
343                                                        FIB_SOURCE_DHCP);
344   else
345       rx_fib_index = mfib_table_find_or_create_and_lock(proto, tbl_id,
346                                                         MFIB_SOURCE_DHCP);
347   v = dhcp_get_vss_info(dm, rx_fib_index, proto);
348
349   if (NULL != v)
350     {
351       if (is_del)
352         {
353           /* release the lock held on the table when the VSS
354            * info was created */
355           dhcp_proxy_rx_table_unlock (proto, rx_fib_index);
356
357           vec_free (v->vpn_ascii_id);
358           pool_put (dm->vss[proto], v);
359           dm->vss_index_by_rx_fib_index[proto][rx_fib_index] = ~0;
360         }
361       else
362         {
363           update_vss (v, vss_type, vpn_ascii_id, oui, vpn_index);
364         }
365     }
366   else
367     {
368       if (is_del)
369         rc = VNET_API_ERROR_NO_SUCH_ENTRY;
370       else
371         {
372           /* create a new entry */
373           vec_validate_init_empty(dm->vss_index_by_rx_fib_index[proto],
374                                   rx_fib_index, ~0);
375
376           /* hold a lock on the table whilst the VSS info exist */
377           pool_get (dm->vss[proto], v);
378           update_vss (v, vss_type, vpn_ascii_id, oui, vpn_index);
379           dm->vss_index_by_rx_fib_index[proto][rx_fib_index] =
380               v - dm->vss[proto];
381           dhcp_proxy_rx_table_lock (proto, rx_fib_index);
382         }
383     }
384
385   /* Release the lock taken during the create_or_lock at the start */
386   dhcp_proxy_rx_table_unlock (proto, rx_fib_index);
387
388   return (rc);
389 }