Consolidate DHCP v4 and V6 implementation. No functional change intended
[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
21 /**
22  * @brief Shard 4/6 instance of DHCP main
23  */
24 dhcp_proxy_main_t dhcp_proxy_main;
25
26 void
27 dhcp_proxy_walk (fib_protocol_t proto,
28                  dhcp_proxy_walk_fn_t fn,
29                  void *ctx)
30 {
31   dhcp_proxy_main_t * dpm = &dhcp_proxy_main;
32   dhcp_server_t * server;
33   u32 server_index, i;
34
35   vec_foreach_index (i, dpm->dhcp_server_index_by_rx_fib_index[proto])
36   {
37       server_index = dpm->dhcp_server_index_by_rx_fib_index[proto][i];
38       if (~0 == server_index)
39           continue;
40
41       server = pool_elt_at_index (dpm->dhcp_servers[proto], server_index);
42
43       if (!fn(server, ctx))
44           break;
45     }
46 }
47
48 void
49 dhcp_vss_walk (fib_protocol_t proto,
50                dhcp_vss_walk_fn_t fn,
51                void *ctx)
52 {
53   dhcp_proxy_main_t * dpm = &dhcp_proxy_main;
54   dhcp_vss_t * vss;
55   u32 vss_index, i;
56   fib_table_t *fib;
57
58
59   vec_foreach_index (i, dpm->vss_index_by_rx_fib_index[proto])
60   {
61       vss_index = dpm->vss_index_by_rx_fib_index[proto][i];
62       if (~0 == vss_index)
63           continue;
64
65       vss = pool_elt_at_index (dpm->vss[proto], vss_index);
66
67       fib = fib_table_get(i, proto);
68
69       if (!fn(vss, fib->ft_table_id, ctx))
70           break;
71     }
72 }
73
74 int
75 dhcp_proxy_server_del (fib_protocol_t proto,
76                        u32 rx_fib_index)
77 {
78   dhcp_proxy_main_t * dpm = &dhcp_proxy_main;
79   dhcp_server_t * server = 0;
80   int rc = 0;
81
82   server = dhcp_get_server(dpm, rx_fib_index, proto);
83
84   if (NULL == server)
85   {
86       rc = VNET_API_ERROR_NO_SUCH_ENTRY;
87   }
88   else
89   {
90       /* Use the default server again.  */
91       dpm->dhcp_server_index_by_rx_fib_index[proto][rx_fib_index] = ~0;
92
93       fib_table_unlock (server->server_fib_index, proto);
94
95       pool_put (dpm->dhcp_servers[proto], server);
96   }
97
98   return (rc);
99 }
100
101 int
102 dhcp_proxy_server_add (fib_protocol_t proto,
103                        ip46_address_t *addr,
104                        ip46_address_t *src_address,
105                        u32 rx_fib_index,
106                        u32 server_table_id)
107 {
108   dhcp_proxy_main_t * dpm = &dhcp_proxy_main;
109   dhcp_server_t * server = 0;
110   int new = 0;
111
112   server = dhcp_get_server(dpm, rx_fib_index, proto);
113
114   if (NULL == server)
115   {
116       vec_validate_init_empty(dpm->dhcp_server_index_by_rx_fib_index[proto],
117                               rx_fib_index,
118                               ~0);
119
120       pool_get (dpm->dhcp_servers[proto], server);
121       memset (server, 0, sizeof (*server));
122       new = 1;
123
124       dpm->dhcp_server_index_by_rx_fib_index[proto][rx_fib_index] =
125           server - dpm->dhcp_servers[proto];
126
127       server->rx_fib_index = rx_fib_index;
128       server->server_fib_index = 
129           fib_table_find_or_create_and_lock(proto, server_table_id);
130   }
131   else
132   {
133       /* modify, may need to swap server FIBs */
134       u32 tmp_index;
135
136       tmp_index = fib_table_find(proto, server_table_id);
137
138       if (tmp_index != server->server_fib_index)
139       {
140           tmp_index = server->server_fib_index;
141
142           /* certainly swapping if the fib doesn't exist */
143           server->server_fib_index = 
144               fib_table_find_or_create_and_lock(proto, server_table_id);
145           fib_table_unlock (tmp_index, proto);
146       }
147   }
148
149   server->dhcp_server = *addr;
150   server->dhcp_src_address = *src_address;
151
152   return (new);
153 }
154
155 typedef struct dhcp4_proxy_dump_walk_ctx_t_
156 {
157     fib_protocol_t proto;
158     void *opaque;
159     u32 context;
160 } dhcp_proxy_dump_walk_cxt_t;
161
162 static int
163 dhcp_proxy_dump_walk (dhcp_server_t *server,
164                       void *arg)
165 {
166   dhcp_proxy_dump_walk_cxt_t *ctx = arg;
167   fib_table_t *s_fib, *r_fib;
168   dhcp_vss_t *v;
169
170   v = dhcp_get_vss_info(&dhcp_proxy_main,
171                         server->rx_fib_index,
172                         ctx->proto);
173
174   s_fib = fib_table_get(server->server_fib_index, ctx->proto);
175   r_fib = fib_table_get(server->rx_fib_index, ctx->proto);
176
177   dhcp_send_details(ctx->proto,
178                     ctx->opaque,
179                     ctx->context,
180                     &server->dhcp_server,
181                     &server->dhcp_src_address,
182                     s_fib->ft_table_id,
183                     r_fib->ft_table_id,
184                     (v ? v->fib_id : 0),
185                     (v ? v->oui : 0));
186
187   return (1);
188 }
189
190 void
191 dhcp_proxy_dump (fib_protocol_t proto,
192                  void *opaque,
193                  u32 context)
194 {
195     dhcp_proxy_dump_walk_cxt_t ctx =  {
196         .proto = proto,
197         .opaque = opaque,
198         .context = context,
199     };
200     dhcp_proxy_walk(proto, dhcp_proxy_dump_walk, &ctx);
201 }
202
203 int
204 dhcp_vss_show_walk (dhcp_vss_t *vss,
205                     u32 rx_table_id,
206                     void *ctx)
207 {
208     vlib_main_t * vm = ctx;
209
210     vlib_cli_output (vm, "%=6d%=6d%=12d",
211                      rx_table_id,
212                      vss->oui,
213                      vss->fib_id);
214
215     return (1);
216 }
217
218 int dhcp_proxy_set_vss (fib_protocol_t proto,
219                         u32 tbl_id,
220                         u32 oui,
221                         u32 fib_id, 
222                         int is_del)
223 {
224   dhcp_proxy_main_t *dm = &dhcp_proxy_main;
225   dhcp_vss_t *v = NULL;
226   u32  rx_fib_index;
227   int rc = 0;
228   
229   rx_fib_index = fib_table_find_or_create_and_lock(proto, tbl_id);
230   v = dhcp_get_vss_info(dm, rx_fib_index, proto);
231
232   if (NULL != v)
233   {
234       if (is_del)
235       {
236           /* release the lock held on the table when the VSS
237            * info was created */
238           fib_table_unlock (rx_fib_index, proto);
239
240           pool_put (dm->vss[proto], v);
241           dm->vss_index_by_rx_fib_index[proto][rx_fib_index] = ~0;
242       }
243       else
244       {
245           /* this is a modify */
246           v->fib_id = fib_id;
247           v->oui = oui;
248       }
249   }
250   else
251   {
252       if (is_del)
253           rc = VNET_API_ERROR_NO_SUCH_ENTRY;
254       else
255       {
256           /* create a new entry */
257           vec_validate_init_empty(dm->vss_index_by_rx_fib_index[proto],
258                                   rx_fib_index, ~0);
259
260           /* hold a lock on the table whilst the VSS info exist */
261           fib_table_lock (rx_fib_index, proto);
262
263           pool_get (dm->vss[proto], v);
264           v->fib_id = fib_id;
265           v->oui = oui;
266           dm->vss_index_by_rx_fib_index[proto][rx_fib_index] =
267               v - dm->vss[proto];
268       }
269   }
270
271   /* Release the lock taken during the create_or_lock at the start */
272   fib_table_unlock (rx_fib_index, proto);
273   
274   return (rc);
275 }