DHCPv6 - Be consistent with the use of MFIB index as the RX FIB index for DHCPv6...
[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 static 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_server_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 int
128 dhcp_proxy_server_del (fib_protocol_t proto,
129                        u32 rx_fib_index)
130 {
131   dhcp_proxy_main_t * dpm = &dhcp_proxy_main;
132   dhcp_server_t * server = 0;
133   int rc = 0;
134
135   server = dhcp_get_server(dpm, rx_fib_index, proto);
136
137   if (NULL == server)
138   {
139       rc = VNET_API_ERROR_NO_SUCH_ENTRY;
140   }
141   else
142   {
143       /* Use the default server again.  */
144       dpm->dhcp_server_index_by_rx_fib_index[proto][rx_fib_index] = ~0;
145
146       fib_table_unlock (server->server_fib_index, proto);
147
148       pool_put (dpm->dhcp_servers[proto], server);
149   }
150
151   return (rc);
152 }
153
154 int
155 dhcp_proxy_server_add (fib_protocol_t proto,
156                        ip46_address_t *addr,
157                        ip46_address_t *src_address,
158                        u32 rx_fib_index,
159                        u32 server_table_id)
160 {
161   dhcp_proxy_main_t * dpm = &dhcp_proxy_main;
162   dhcp_server_t * server = 0;
163   int new = 0;
164
165   server = dhcp_get_server(dpm, rx_fib_index, proto);
166
167   if (NULL == server)
168   {
169       vec_validate_init_empty(dpm->dhcp_server_index_by_rx_fib_index[proto],
170                               rx_fib_index,
171                               ~0);
172
173       pool_get (dpm->dhcp_servers[proto], server);
174       memset (server, 0, sizeof (*server));
175       new = 1;
176
177       dpm->dhcp_server_index_by_rx_fib_index[proto][rx_fib_index] =
178           server - dpm->dhcp_servers[proto];
179
180       server->rx_fib_index = rx_fib_index;
181       server->server_fib_index = 
182           fib_table_find_or_create_and_lock(proto, server_table_id);
183   }
184   else
185   {
186       /* modify, may need to swap server FIBs */
187       u32 tmp_index;
188
189       tmp_index = fib_table_find(proto, server_table_id);
190
191       if (tmp_index != server->server_fib_index)
192       {
193           tmp_index = server->server_fib_index;
194
195           /* certainly swapping if the fib doesn't exist */
196           server->server_fib_index = 
197               fib_table_find_or_create_and_lock(proto, server_table_id);
198           fib_table_unlock (tmp_index, proto);
199       }
200   }
201
202   server->dhcp_server = *addr;
203   server->dhcp_src_address = *src_address;
204
205   return (new);
206 }
207
208 typedef struct dhcp4_proxy_dump_walk_ctx_t_
209 {
210     fib_protocol_t proto;
211     void *opaque;
212     u32 context;
213 } dhcp_proxy_dump_walk_cxt_t;
214
215 static int
216 dhcp_proxy_dump_walk (dhcp_server_t *server,
217                       void *arg)
218 {
219   dhcp_proxy_dump_walk_cxt_t *ctx = arg;
220   fib_table_t *s_fib;
221   u32 rx_table_id;
222   dhcp_vss_t *v;
223
224   v = dhcp_get_vss_info(&dhcp_proxy_main,
225                         server->rx_fib_index,
226                         ctx->proto);
227
228   s_fib = fib_table_get(server->server_fib_index, ctx->proto);
229   rx_table_id = dhcp_proxy_rx_table_get_table_id(server->rx_fib_index,
230                                                  ctx->proto);
231
232   dhcp_send_details(ctx->proto,
233                     ctx->opaque,
234                     ctx->context,
235                     &server->dhcp_server,
236                     &server->dhcp_src_address,
237                     s_fib->ft_table_id,
238                     rx_table_id,
239                     (v ? v->fib_id : 0),
240                     (v ? v->oui : 0));
241
242   return (1);
243 }
244
245 void
246 dhcp_proxy_dump (fib_protocol_t proto,
247                  void *opaque,
248                  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,
260                     u32 rx_table_id,
261                     void *ctx)
262 {
263     vlib_main_t * vm = ctx;
264
265     vlib_cli_output (vm, "%=6d%=6d%=12d",
266                      rx_table_id,
267                      vss->oui,
268                      vss->fib_id);
269
270     return (1);
271 }
272
273 int dhcp_proxy_set_vss (fib_protocol_t proto,
274                         u32 tbl_id,
275                         u32 oui,
276                         u32 fib_id, 
277                         int is_del)
278 {
279   dhcp_proxy_main_t *dm = &dhcp_proxy_main;
280   dhcp_vss_t *v = NULL;
281   u32  rx_fib_index;
282   int rc = 0;
283   
284   if (proto == FIB_PROTOCOL_IP4)
285       rx_fib_index = fib_table_find_or_create_and_lock(proto, tbl_id);
286   else
287       rx_fib_index = mfib_table_find_or_create_and_lock(proto, tbl_id);
288   v = dhcp_get_vss_info(dm, rx_fib_index, proto);
289
290   if (NULL != v)
291   {
292       if (is_del)
293       {
294           /* release the lock held on the table when the VSS
295            * info was created */
296           dhcp_proxy_rx_table_unlock (proto, rx_fib_index);
297
298           pool_put (dm->vss[proto], v);
299           dm->vss_index_by_rx_fib_index[proto][rx_fib_index] = ~0;
300       }
301       else
302       {
303           /* this is a modify */
304           v->fib_id = fib_id;
305           v->oui = oui;
306       }
307   }
308   else
309   {
310       if (is_del)
311           rc = VNET_API_ERROR_NO_SUCH_ENTRY;
312       else
313       {
314           /* create a new entry */
315           vec_validate_init_empty(dm->vss_index_by_rx_fib_index[proto],
316                                   rx_fib_index, ~0);
317
318           /* hold a lock on the table whilst the VSS info exist */
319           pool_get (dm->vss[proto], v);
320           v->fib_id = fib_id;
321           v->oui = oui;
322
323           dm->vss_index_by_rx_fib_index[proto][rx_fib_index] =
324               v - dm->vss[proto];
325           dhcp_proxy_rx_table_lock (proto, rx_fib_index);
326       }
327   }
328
329   /* Release the lock taken during the create_or_lock at the start */
330   dhcp_proxy_rx_table_unlock (proto, rx_fib_index);
331
332   return (rc);
333 }