dhcp: multiple additions
[vpp.git] / src / vnet / dhcp / proxy_node.c
1 /*
2  * proxy_node.c: dhcp 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 <vlib/vlib.h>
19 #include <vnet/pg/pg.h>
20 #include <vnet/dhcp/proxy.h>
21 #include <vnet/dhcp/client.h>
22 #include <vnet/fib/ip4_fib.h>
23
24 static char * dhcp_proxy_error_strings[] = {
25 #define dhcp_proxy_error(n,s) s,
26 #include "proxy_error.def"
27 #undef dhcp_proxy_error
28 };
29
30 #define foreach_dhcp_proxy_to_server_input_next \
31   _ (DROP, "error-drop")                        \
32   _ (LOOKUP, "ip4-lookup")                      \
33   _ (SEND_TO_CLIENT, "dhcp-proxy-to-client")
34
35 typedef enum {
36 #define _(s,n) DHCP_PROXY_TO_SERVER_INPUT_NEXT_##s,
37   foreach_dhcp_proxy_to_server_input_next
38 #undef _
39   DHCP_PROXY_TO_SERVER_INPUT_N_NEXT,
40 } dhcp_proxy_to_server_input_next_t;
41
42 typedef struct {
43   /* 0 => to server, 1 => to client */
44   int which; 
45   ip4_address_t trace_ip4_address;
46   u32 error;
47   u32 sw_if_index;
48   u32 original_sw_if_index;
49 } dhcp_proxy_trace_t;
50
51 #define VPP_DHCP_OPTION82_SUB1_SIZE   6
52 #define VPP_DHCP_OPTION82_SUB5_SIZE   6
53 #define VPP_DHCP_OPTION82_VSS_SIZE    12
54 #define VPP_DHCP_OPTION82_SIZE (VPP_DHCP_OPTION82_SUB1_SIZE + \
55                                 VPP_DHCP_OPTION82_SUB5_SIZE + \
56                                 VPP_DHCP_OPTION82_VSS_SIZE +3)
57
58 vlib_node_registration_t dhcp_proxy_to_server_node;
59 vlib_node_registration_t dhcp_proxy_to_client_node;
60
61 dhcp_proxy_main_t dhcp_proxy_main;
62
63 u8 * format_dhcp_proxy_trace (u8 * s, va_list * args)
64 {
65   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
66   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
67   dhcp_proxy_trace_t * t = va_arg (*args, dhcp_proxy_trace_t *);
68     
69   if (t->which == 0)
70     s = format (s, "DHCP proxy: sent to server %U\n",
71                 format_ip4_address, &t->trace_ip4_address, t->error);
72   else
73     s = format (s, "DHCP proxy: broadcast to client from %U\n",
74                 format_ip4_address, &t->trace_ip4_address);
75       
76   if (t->error != (u32)~0)
77     s = format (s, "  error: %s\n", dhcp_proxy_error_strings[t->error]);
78
79   s = format (s, "  original_sw_if_index: %d, sw_if_index: %d\n",
80               t->original_sw_if_index, t->sw_if_index);
81   
82   return s;
83 }
84
85 u8 * format_dhcp_proxy_header_with_length (u8 * s, va_list * args)
86 {
87   dhcp_header_t * h = va_arg (*args, dhcp_header_t *);
88   u32 max_header_bytes = va_arg (*args, u32);
89   u32 header_bytes;
90
91   header_bytes = sizeof (h[0]);
92   if (max_header_bytes != 0 && header_bytes > max_header_bytes)
93     return format (s, "dhcp header truncated");
94
95   s = format (s, "DHCP Proxy");
96
97   return s;
98 }
99
100 static inline vss_info *
101 dhcp_get_vss_info (dhcp_proxy_main_t *dm,
102                    u32 rx_fib_index)
103 {
104   vss_info *v;
105
106   if (vec_len(dm->vss_index_by_rx_fib_index) <= rx_fib_index ||
107       dm->vss_index_by_rx_fib_index[rx_fib_index] == ~0)
108   {
109       v = NULL;
110   }
111   else
112   {
113       v = pool_elt_at_index (dm->vss,
114                              dm->vss_index_by_rx_fib_index[rx_fib_index]);
115   }
116
117   return (v);
118 }
119
120 static inline dhcp_server_t *
121 dhcp_get_server (dhcp_proxy_main_t *dm,
122                  u32 rx_fib_index)
123 {
124   dhcp_server_t *s = NULL;
125
126   if (vec_len(dm->dhcp_server_index_by_rx_fib_index) > rx_fib_index &&
127       dm->dhcp_server_index_by_rx_fib_index[rx_fib_index] != ~0)
128   {
129       s = pool_elt_at_index (dm->dhcp_servers,
130                              dm->dhcp_server_index_by_rx_fib_index[rx_fib_index]);
131   }
132
133   return (s);
134 }
135
136 static uword
137 dhcp_proxy_to_server_input (vlib_main_t * vm,
138                             vlib_node_runtime_t * node,
139                             vlib_frame_t * from_frame)
140 {
141   u32 n_left_from, next_index, * from, * to_next;
142   dhcp_proxy_main_t * dpm = &dhcp_proxy_main;
143   from = vlib_frame_vector_args (from_frame);
144   n_left_from = from_frame->n_vectors;
145   u32 pkts_to_server=0, pkts_to_client=0, pkts_no_server=0;
146   u32 pkts_no_interface_address=0;
147   u32 pkts_too_big=0;
148   ip4_main_t * im = &ip4_main;
149
150   next_index = node->cached_next_index;
151
152   while (n_left_from > 0)
153     {
154       u32 n_left_to_next;
155
156       vlib_get_next_frame (vm, node, next_index,
157                            to_next, n_left_to_next);
158
159       while (n_left_from > 0 && n_left_to_next > 0)
160         {
161           u32 bi0;
162           vlib_buffer_t * b0;
163           udp_header_t * u0;
164           dhcp_header_t * h0;
165           ip4_header_t * ip0;
166           u32 next0;
167           u32 old0, new0;
168           ip_csum_t sum0;
169           u32 error0 = (u32) ~0;
170           u32 sw_if_index = 0;
171           u32 original_sw_if_index = 0;
172           u8  *end = NULL;
173           u32 fib_index;
174           dhcp_server_t * server;
175           u32 rx_sw_if_index;
176           dhcp_option_t *o;
177           u32 len = 0;
178           vlib_buffer_free_list_t *fl;
179
180           bi0 = from[0];
181           to_next[0] = bi0;
182           from += 1;
183           to_next += 1;
184           n_left_from -= 1;
185           n_left_to_next -= 1;
186
187           b0 = vlib_get_buffer (vm, bi0);
188
189           h0 = vlib_buffer_get_current (b0);
190
191           /* 
192            * udp_local hands us the DHCP header, need udp hdr, 
193            * ip hdr to relay to server
194            */
195           vlib_buffer_advance (b0, -(sizeof(*u0)));
196           u0 = vlib_buffer_get_current (b0);
197
198           /* This blows. Return traffic has src_port = 67, dst_port = 67 */
199           if (u0->src_port == clib_net_to_host_u16(UDP_DST_PORT_dhcp_to_server))
200             {
201               vlib_buffer_advance (b0, sizeof(*u0));
202               next0 = DHCP_PROXY_TO_SERVER_INPUT_NEXT_SEND_TO_CLIENT;
203               error0 = 0;
204               pkts_to_client++;
205               goto do_enqueue;
206             }
207
208           rx_sw_if_index = vnet_buffer(b0)->sw_if_index[VLIB_RX];
209
210           fib_index = im->fib_index_by_sw_if_index [rx_sw_if_index];
211           server = dhcp_get_server(dpm, fib_index);
212           
213           if (PREDICT_FALSE (NULL == server))
214             {
215               error0 = DHCP_PROXY_ERROR_NO_SERVER;
216               next0 = DHCP_PROXY_TO_SERVER_INPUT_NEXT_DROP;
217               pkts_no_server++;
218               goto do_trace;
219             }
220           
221           vlib_buffer_advance (b0, -(sizeof(*ip0)));
222           ip0 = vlib_buffer_get_current (b0);
223
224           /* disable UDP checksum */
225           u0->checksum = 0;
226           sum0 = ip0->checksum;
227           old0 = ip0->dst_address.as_u32;
228           new0 = server->dhcp_server.as_u32;
229           ip0->dst_address.as_u32 = server->dhcp_server.as_u32;
230           sum0 = ip_csum_update (sum0, old0, new0, 
231                                 ip4_header_t /* structure */, 
232                                 dst_address /* changed member */);
233           ip0->checksum = ip_csum_fold (sum0);
234
235           sum0 = ip0->checksum;
236           old0 = ip0->src_address.as_u32;
237           new0 = server->dhcp_src_address.as_u32;
238           ip0->src_address.as_u32 = new0;
239           sum0 = ip_csum_update (sum0, old0, new0, 
240                                 ip4_header_t /* structure */, 
241                                 src_address /* changed member */);
242           ip0->checksum = ip_csum_fold (sum0);
243
244           /* Send to DHCP server via the configured FIB */
245           vnet_buffer(b0)->sw_if_index[VLIB_TX] =
246             server->server_fib_index;
247
248           h0->gateway_ip_address.as_u32 = server->dhcp_src_address.as_u32;
249           pkts_to_server++;
250
251           o = (dhcp_option_t *) h0->options;
252               
253           fib_index = im->fib_index_by_sw_if_index 
254               [vnet_buffer(b0)->sw_if_index[VLIB_RX]];
255
256           end = b0->data + b0->current_data + b0->current_length;
257           /* TLVs are not performance-friendly... */
258           while  (o->option != 0xFF /* end of options */ && (u8 *)o < end) 
259               o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
260
261           fl = vlib_buffer_get_free_list (vm, b0->free_list_index);
262           // start write at (option*)o, some packets have padding
263           if (((u8 *)o - (u8 *)b0->data + VPP_DHCP_OPTION82_SIZE) > fl->n_data_bytes)
264           {
265               next0 = DHCP_PROXY_TO_SERVER_INPUT_NEXT_DROP;
266               pkts_too_big++;
267               goto do_trace;
268           }
269
270           if ((o->option == 0xFF)  && ((u8 *)o <= end))
271           {  
272               vnet_main_t *vnm = vnet_get_main();   
273               u16 old_l0, new_l0;
274               ip4_address_t _ia0, * ia0 = &_ia0;
275               vss_info *vss;
276               vnet_sw_interface_t *swif;
277               sw_if_index = 0;
278               original_sw_if_index = 0;
279                   
280               original_sw_if_index = sw_if_index = 
281                   vnet_buffer(b0)->sw_if_index[VLIB_RX];
282               swif = vnet_get_sw_interface (vnm, sw_if_index);
283               if (swif->flags & VNET_SW_INTERFACE_FLAG_UNNUMBERED)
284                   sw_if_index = swif->unnumbered_sw_if_index;
285                   
286               /* 
287                * Get the first ip4 address on the [client-side] 
288                * RX interface, if not unnumbered. otherwise use
289                * the loopback interface's ip address.
290                */
291               ia0 = ip4_interface_first_address(&ip4_main, sw_if_index, 0);
292                   
293               if (ia0 == 0)
294               {
295                   error0 = DHCP_PROXY_ERROR_NO_INTERFACE_ADDRESS;
296                   next0 = DHCP_PROXY_TO_SERVER_INPUT_NEXT_DROP;
297                   pkts_no_interface_address++;
298                   goto do_trace;
299               }
300
301               /* Add option 82 */
302               o->option = 82;   /* option 82 */
303               o->length = 12;   /* 12 octets to follow */
304               o->data[0] = 1;   /* suboption 1, circuit ID (=FIB id) */
305               o->data[1] = 4;   /* length of suboption */
306               o->data[2] = (original_sw_if_index >> 24) & 0xFF;
307               o->data[3] = (original_sw_if_index >> 16) & 0xFF;
308               o->data[4] = (original_sw_if_index >> 8)  & 0xFF;
309               o->data[5] = (original_sw_if_index >> 0)  & 0xFF;
310               o->data[6] = 5; /* suboption 5 (client RX intfc address) */
311               o->data[7] = 4; /* length 4 */
312               o->data[8] = ia0->as_u8[0];
313               o->data[9] = ia0->as_u8[1];
314               o->data[10] = ia0->as_u8[2];
315               o->data[11] = ia0->as_u8[3];
316               o->data[12] = 0xFF;
317
318               vss = dhcp_get_vss_info (dpm, fib_index);
319               if (NULL != vss)
320               {
321                   u32 opt82_fib_id=0, opt82_oui=0;
322
323                   opt82_oui =  vss->vpn_id.oui;
324                   opt82_fib_id =  vss->vpn_id.fib_id;
325
326                   o->data[12] = 151; /* vss suboption */
327                   if (255 == opt82_fib_id) {
328                       o->data[13] = 1;   /* length */
329                       o->data[14] = 255;   /* vss option type */
330                       o->data[15] = 152; /* vss control suboption */
331                       o->data[16] = 0;   /* length */
332                       /* and a new "end-of-options" option (0xff) */
333                       o->data[17] = 0xFF;
334                       o->length += 5;
335                   } else {
336                       o->data[13] = 8;   /* length */
337                       o->data[14] = 1;   /* vss option type */
338                       o->data[15] = (opt82_oui >> 16) & 0xff;
339                       o->data[16] = (opt82_oui >> 8) & 0xff;
340                       o->data[17] = (opt82_oui ) & 0xff;
341                       o->data[18] = (opt82_fib_id >> 24) & 0xff;
342                       o->data[19] = (opt82_fib_id >> 16) & 0xff;
343                       o->data[20] = (opt82_fib_id >> 8) & 0xff;
344                       o->data[21] = (opt82_fib_id) & 0xff;
345                       o->data[22] = 152; /* vss control suboption */
346                       o->data[23] = 0;   /* length */
347                           
348                       /* and a new "end-of-options" option (0xff) */
349                       o->data[24] = 0xFF;
350                       o->length += 12;
351                   }
352               }
353
354               len = o->length + 3;
355               b0->current_length += len;
356               /* Fix IP header length and checksum */
357               old_l0 = ip0->length;
358               new_l0 = clib_net_to_host_u16 (old_l0);
359               new_l0 += len;
360               new_l0 = clib_host_to_net_u16 (new_l0);
361               ip0->length = new_l0;
362               sum0 = ip0->checksum;
363               sum0 = ip_csum_update (sum0, old_l0, new_l0, ip4_header_t,
364                                      length /* changed member */);
365               ip0->checksum = ip_csum_fold (sum0);
366
367               /* Fix UDP length */
368               new_l0 = clib_net_to_host_u16 (u0->length);
369               new_l0 += len;
370               u0->length = clib_host_to_net_u16 (new_l0);
371           } else {
372               vlib_node_increment_counter 
373                   (vm, dhcp_proxy_to_server_node.index,
374                    DHCP_PROXY_ERROR_OPTION_82_ERROR, 1);
375           }
376           
377           next0 = DHCP_PROXY_TO_SERVER_INPUT_NEXT_LOOKUP;
378
379         do_trace:
380           if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) 
381             {
382                dhcp_proxy_trace_t *tr = vlib_add_trace (vm, node, 
383                                                         b0, sizeof (*tr));
384                tr->which = 0; /* to server */
385                tr->error = error0;
386                tr->original_sw_if_index = original_sw_if_index;
387                tr->sw_if_index = sw_if_index;
388                if (next0 == DHCP_PROXY_TO_SERVER_INPUT_NEXT_LOOKUP)
389                  tr->trace_ip4_address.as_u32 = server->dhcp_server.as_u32;
390             }
391
392         do_enqueue:
393           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
394                                            to_next, n_left_to_next,
395                                            bi0, next0);
396         }
397
398       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
399     }
400
401   vlib_node_increment_counter (vm, dhcp_proxy_to_server_node.index,
402                                DHCP_PROXY_ERROR_RELAY_TO_CLIENT,
403                                pkts_to_client);
404   vlib_node_increment_counter (vm, dhcp_proxy_to_server_node.index,
405                                DHCP_PROXY_ERROR_RELAY_TO_SERVER,
406                                pkts_to_server);
407   vlib_node_increment_counter (vm, dhcp_proxy_to_server_node.index,
408                                DHCP_PROXY_ERROR_NO_SERVER,
409                                pkts_no_server);
410   vlib_node_increment_counter (vm, dhcp_proxy_to_server_node.index,
411                                DHCP_PROXY_ERROR_NO_INTERFACE_ADDRESS,
412                                pkts_no_interface_address);
413  vlib_node_increment_counter (vm, dhcp_proxy_to_server_node.index,
414                               DHCP_PROXY_ERROR_PKT_TOO_BIG,
415                               pkts_too_big);
416   return from_frame->n_vectors;
417 }
418
419 VLIB_REGISTER_NODE (dhcp_proxy_to_server_node) = {
420   .function = dhcp_proxy_to_server_input,
421   .name = "dhcp-proxy-to-server",
422   /* Takes a vector of packets. */
423   .vector_size = sizeof (u32),
424
425   .n_errors = DHCP_PROXY_N_ERROR,
426   .error_strings = dhcp_proxy_error_strings,
427
428   .n_next_nodes = DHCP_PROXY_TO_SERVER_INPUT_N_NEXT,
429   .next_nodes = {
430 #define _(s,n) [DHCP_PROXY_TO_SERVER_INPUT_NEXT_##s] = n,
431     foreach_dhcp_proxy_to_server_input_next
432 #undef _
433   },
434
435   .format_buffer = format_dhcp_proxy_header_with_length,
436   .format_trace = format_dhcp_proxy_trace,
437 #if 0
438   .unformat_buffer = unformat_dhcp_proxy_header,
439 #endif
440 };
441
442 static uword
443 dhcp_proxy_to_client_input (vlib_main_t * vm,
444                             vlib_node_runtime_t * node,
445                             vlib_frame_t * from_frame)
446 {
447   u32 n_left_from, * from;
448   ethernet_main_t *em = ethernet_get_main (vm);
449   dhcp_proxy_main_t * dpm = &dhcp_proxy_main;
450   vnet_main_t * vnm = vnet_get_main();
451   ip4_main_t * im = &ip4_main;
452
453   from = vlib_frame_vector_args (from_frame);
454   n_left_from = from_frame->n_vectors;
455
456   while (n_left_from > 0)
457     {
458       u32 bi0;
459       vlib_buffer_t * b0;
460       udp_header_t * u0;
461       dhcp_header_t * h0;
462       ip4_header_t * ip0 = 0;
463       ip4_address_t * ia0 = 0;
464       u32 old0, new0;
465       ip_csum_t sum0;
466       ethernet_interface_t *ei0;
467       ethernet_header_t *mac0;
468       vnet_hw_interface_t *hi0;
469       vlib_frame_t *f0;
470       u32 * to_next0;
471       u32 sw_if_index = ~0;
472       vnet_sw_interface_t *si0;
473       u32 error0 = (u32)~0;
474       vnet_sw_interface_t *swif;
475       u32 fib_index;
476       dhcp_server_t * server;
477       u32 original_sw_if_index = (u32) ~0;
478       ip4_address_t relay_addr = {
479           .as_u32 = 0,
480       };
481
482       bi0 = from[0];
483       from += 1;
484       n_left_from -= 1;
485
486       b0 = vlib_get_buffer (vm, bi0);
487       h0 = vlib_buffer_get_current (b0);
488
489       /* 
490        * udp_local hands us the DHCP header, need udp hdr, 
491        * ip hdr to relay to client
492        */
493       vlib_buffer_advance (b0, -(sizeof(*u0)));
494       u0 = vlib_buffer_get_current (b0);
495
496       vlib_buffer_advance (b0, -(sizeof(*ip0)));
497       ip0 = vlib_buffer_get_current (b0);
498
499       /* Consumed by dhcp client code? */
500       if (dhcp_client_for_us (bi0, b0, ip0, u0, h0))
501           continue;
502
503       if (1 /* dpm->insert_option_82 */)
504         {
505           dhcp_option_t *o = (dhcp_option_t *) h0->options;
506           dhcp_option_t *sub;
507               
508           /* Parse through TLVs looking for option 82.
509              The circuit-ID is the FIB number we need
510              to track down the client-facing interface */
511
512           while (o->option != 0xFF /* end of options */ &&
513                  (u8 *) o < (b0->data + b0->current_data + b0->current_length))
514             {
515               if (o->option == 82)
516                 {
517                     u32 vss_exist = 0;
518                     u32 vss_ctrl = 0;
519                     sub = (dhcp_option_t *) &o->data[0];
520                     while (sub->option != 0xFF /* end of options */ &&
521                            (u8 *) sub < (u8 *)(o + o->length)) {
522                         /* If this is one of ours, it will have
523                            total length 12, circuit-id suboption type,
524                            and the sw_if_index */
525                         if (sub->option == 1 && sub->length == 4)
526                           {
527                             sw_if_index = ((sub->data[0] << 24) |
528                                            (sub->data[1] << 16) |
529                                            (sub->data[2] << 8)  |
530                                            (sub->data[3]));
531                           }
532                         else if (sub->option == 5 && sub->length == 4)
533                           {
534                               relay_addr.as_u8[0] = sub->data[0];
535                               relay_addr.as_u8[1] = sub->data[1];
536                               relay_addr.as_u8[2] = sub->data[2];
537                               relay_addr.as_u8[3] = sub->data[3];
538                           }
539                         else if (sub->option == 151 &&
540                                  sub->length == 7 &&
541                                  sub->data[0] == 1)
542                             vss_exist = 1;
543                         else if (sub->option == 152 && sub->length == 0)
544                             vss_ctrl = 1;
545                         sub = (dhcp_option_t *) 
546                           (((uword) sub) + (sub->length + 2));
547                     }
548                     if (vss_ctrl && vss_exist)
549                       vlib_node_increment_counter 
550                         (vm, dhcp_proxy_to_client_node.index,
551                          DHCP_PROXY_ERROR_OPTION_82_VSS_NOT_PROCESSED, 1);
552
553                 }
554               o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
555             }
556         }
557
558       if (sw_if_index == (u32)~0)
559         {
560           error0 = DHCP_PROXY_ERROR_NO_OPTION_82;
561           
562         drop_packet:
563           vlib_node_increment_counter (vm, dhcp_proxy_to_client_node.index,
564                                        error0, 1);
565           f0 = vlib_get_frame_to_node (vm, dpm->error_drop_node_index);
566           to_next0 = vlib_frame_vector_args (f0);
567           to_next0[0] = bi0;
568           f0->n_vectors = 1;
569           vlib_put_frame_to_node (vm, dpm->error_drop_node_index, f0);
570           goto do_trace;
571         }
572       
573       if (relay_addr.as_u32 == 0)
574         {
575           error0 = DHCP_PROXY_ERROR_BAD_OPTION_82_ADDR;
576           goto drop_packet;
577         }
578
579       if (sw_if_index >= vec_len (im->fib_index_by_sw_if_index))
580         {
581           error0 = DHCP_PROXY_ERROR_BAD_OPTION_82_ITF;
582           goto drop_packet;
583         }
584
585       fib_index = im->fib_index_by_sw_if_index [sw_if_index];
586       server = dhcp_get_server(dpm, fib_index);
587
588       if (PREDICT_FALSE (NULL == server))
589         {
590           error0 = DHCP_PROXY_ERROR_NO_SERVER;
591           goto drop_packet;
592         }
593       
594       if (ip0->src_address.as_u32 != server->dhcp_server.as_u32)
595         {             
596           error0 = DHCP_PROXY_ERROR_BAD_SVR_FIB_OR_ADDRESS;
597           goto drop_packet;
598         }
599
600       vnet_buffer (b0)->sw_if_index[VLIB_TX] = sw_if_index;
601
602       swif = vnet_get_sw_interface (vnm, sw_if_index);
603       original_sw_if_index = sw_if_index;
604       if (swif->flags & VNET_SW_INTERFACE_FLAG_UNNUMBERED)
605           sw_if_index = swif->unnumbered_sw_if_index;
606
607       ia0 = ip4_interface_first_address (&ip4_main, sw_if_index, 0);
608       if (ia0 == 0)
609         {
610           error0 = DHCP_PROXY_ERROR_NO_INTERFACE_ADDRESS;
611           goto drop_packet;
612         }
613
614       if (relay_addr.as_u32 != ia0->as_u32)
615         {             
616           error0 = DHCP_PROXY_ERROR_BAD_YIADDR;
617           goto drop_packet;
618         }
619
620       u0->checksum = 0;
621       u0->dst_port = clib_net_to_host_u16 (UDP_DST_PORT_dhcp_to_client);
622       sum0 = ip0->checksum;
623       old0 = ip0->dst_address.as_u32;
624       new0 = 0xFFFFFFFF;
625       ip0->dst_address.as_u32 = new0;
626       sum0 = ip_csum_update (sum0, old0, new0, 
627                             ip4_header_t /* structure */, 
628                             dst_address /* offset of changed member */);
629       ip0->checksum = ip_csum_fold (sum0);
630
631       sum0 = ip0->checksum;
632       old0 = ip0->src_address.as_u32;
633       new0 = ia0->as_u32;
634       ip0->src_address.as_u32 = new0;
635       sum0 = ip_csum_update (sum0, old0, new0, 
636                             ip4_header_t /* structure */, 
637                             src_address /* offset of changed member */);
638       ip0->checksum = ip_csum_fold (sum0);
639
640       vlib_buffer_advance (b0, -(sizeof(ethernet_header_t)));
641       si0 = vnet_get_sw_interface (vnm, original_sw_if_index);
642       if (si0->type == VNET_SW_INTERFACE_TYPE_SUB)
643           vlib_buffer_advance (b0, -4 /* space for VLAN tag */);
644
645       mac0 = vlib_buffer_get_current (b0);
646
647       hi0 = vnet_get_sup_hw_interface (vnm, original_sw_if_index);
648       ei0 = pool_elt_at_index (em->interfaces, hi0->hw_instance);
649       clib_memcpy (mac0->src_address, ei0->address, sizeof (ei0->address));
650       memset (mac0->dst_address, 0xff, sizeof (mac0->dst_address));
651       mac0->type = (si0->type == VNET_SW_INTERFACE_TYPE_SUB) ?
652         clib_net_to_host_u16(0x8100) : clib_net_to_host_u16 (0x0800);
653
654       if (si0->type == VNET_SW_INTERFACE_TYPE_SUB)
655         {
656           u32 * vlan_tag = (u32 *)(mac0+1);
657           u32 tmp;
658           tmp = (si0->sub.id << 16) | 0x0800;
659           *vlan_tag = clib_host_to_net_u32 (tmp);
660         }
661
662       /* $$$ This needs to be rewritten, for sure */
663       f0 = vlib_get_frame_to_node (vm, hi0->output_node_index);
664       to_next0 = vlib_frame_vector_args (f0);
665       to_next0[0] = bi0;
666       f0->n_vectors = 1;
667       vlib_put_frame_to_node (vm, hi0->output_node_index, f0);
668
669     do_trace:
670       if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED)) 
671         {
672           dhcp_proxy_trace_t *tr = vlib_add_trace (vm, node, 
673                                                    b0, sizeof (*tr));
674           tr->which = 1; /* to client */
675           tr->trace_ip4_address.as_u32 = ia0 ? ia0->as_u32 : 0;
676           tr->error = error0;
677           tr->original_sw_if_index = original_sw_if_index;
678           tr->sw_if_index = sw_if_index;
679         }
680     }
681   return from_frame->n_vectors;
682 }
683
684 VLIB_REGISTER_NODE (dhcp_proxy_to_client_node) = {
685   .function = dhcp_proxy_to_client_input,
686   .name = "dhcp-proxy-to-client",
687   /* Takes a vector of packets. */
688   .vector_size = sizeof (u32),
689
690   .n_errors = DHCP_PROXY_N_ERROR,
691   .error_strings = dhcp_proxy_error_strings,
692   .format_buffer = format_dhcp_proxy_header_with_length,
693   .format_trace = format_dhcp_proxy_trace,
694 #if 0
695   .unformat_buffer = unformat_dhcp_proxy_header,
696 #endif
697 };
698
699 clib_error_t * dhcp_proxy_init (vlib_main_t * vm)
700 {
701   dhcp_proxy_main_t * dm = &dhcp_proxy_main;
702   vlib_node_t * error_drop_node;
703   dhcp_server_t * server;
704
705   dm->vlib_main = vm;
706   dm->vnet_main = vnet_get_main();
707   error_drop_node = vlib_get_node_by_name (vm, (u8 *) "error-drop");
708   dm->error_drop_node_index = error_drop_node->index;
709
710   dm->vss_index_by_rx_fib_index = NULL;
711
712   udp_register_dst_port (vm, UDP_DST_PORT_dhcp_to_client, 
713                          dhcp_proxy_to_client_node.index, 1 /* is_ip4 */);
714
715   udp_register_dst_port (vm, UDP_DST_PORT_dhcp_to_server, 
716                          dhcp_proxy_to_server_node.index, 1 /* is_ip4 */);
717
718   /* Create the default server, don't mark it valid */
719   pool_get (dm->dhcp_servers, server);
720   memset (server, 0, sizeof (*server));
721
722   return 0;
723 }
724
725 VLIB_INIT_FUNCTION (dhcp_proxy_init);
726
727 int dhcp_proxy_set_server (ip4_address_t *addr,
728                            ip4_address_t *src_address,
729                            u32 rx_fib_id,
730                            u32 server_fib_id, 
731                            int is_del)
732 {
733   dhcp_proxy_main_t * dpm = &dhcp_proxy_main;
734   dhcp_server_t * server = 0;
735   u32 server_index = 0;
736   u32 rx_fib_index = 0;
737
738   const fib_prefix_t all_1s =
739   {
740       .fp_len = 32,
741       .fp_addr.ip4.as_u32 = 0xffffffff,
742       .fp_proto = FIB_PROTOCOL_IP4,
743   };
744
745   if (addr->as_u32 == 0)
746     return VNET_API_ERROR_INVALID_DST_ADDRESS;
747   
748   if (src_address->as_u32 == 0)
749     return VNET_API_ERROR_INVALID_SRC_ADDRESS;
750
751   rx_fib_index = fib_table_find_or_create_and_lock(FIB_PROTOCOL_IP4,
752                                                    rx_fib_id);
753
754   if (is_del)
755     {
756       if (rx_fib_index >= vec_len(dpm->dhcp_server_index_by_rx_fib_index))
757         return VNET_API_ERROR_NO_SUCH_ENTRY;
758       
759       server_index = dpm->dhcp_server_index_by_rx_fib_index[rx_fib_index];
760
761       if (server_index == ~0)
762         return VNET_API_ERROR_NO_SUCH_ENTRY;
763
764       /* Use the default server again.  */
765       dpm->dhcp_server_index_by_rx_fib_index[rx_fib_index] = ~0;
766       server = pool_elt_at_index (dpm->dhcp_servers, server_index);
767
768       fib_table_entry_special_remove(rx_fib_index,
769                                      &all_1s,
770                                      FIB_SOURCE_DHCP);
771       fib_table_unlock (rx_fib_index,
772                         FIB_PROTOCOL_IP4);
773       fib_table_unlock (server->server_fib_index,
774                         FIB_PROTOCOL_IP4);
775
776       memset (server, 0, sizeof (*server));
777       pool_put (dpm->dhcp_servers, server);
778       return 0;
779     }
780   else
781   {
782       vec_validate_init_empty(dpm->dhcp_server_index_by_rx_fib_index,
783                               rx_fib_index,
784                               ~0);
785
786       pool_get (dpm->dhcp_servers, server);
787
788       server->dhcp_server.as_u32 = addr->as_u32;
789       server->dhcp_src_address.as_u32 = src_address->as_u32;
790
791       fib_table_entry_special_add(rx_fib_index,
792                                   &all_1s,
793                                   FIB_SOURCE_DHCP,
794                                   FIB_ENTRY_FLAG_LOCAL,
795                                   ADJ_INDEX_INVALID);
796       fib_table_lock (rx_fib_index,
797                       FIB_PROTOCOL_IP4);
798
799       server->server_fib_index = 
800           fib_table_find_or_create_and_lock(FIB_PROTOCOL_IP4,
801                                             server_fib_id);
802
803       vec_validate_init_empty (dpm->dhcp_server_index_by_rx_fib_index,
804                                rx_fib_index,
805                                ~0);
806       dpm->dhcp_server_index_by_rx_fib_index[rx_fib_index] = 
807           server - dpm->dhcp_servers;
808   }
809
810   fib_table_unlock (rx_fib_index,
811                     FIB_PROTOCOL_IP4);
812
813   return 0;
814 }
815
816 static clib_error_t *
817 dhcp_proxy_set_command_fn (vlib_main_t * vm,
818                            unformat_input_t * input,
819                            vlib_cli_command_t * cmd)
820 {
821   ip4_address_t server_addr, src_addr;
822   u32 server_fib_id = 0, rx_fib_id = 0;
823   int is_del = 0;
824   int set_src = 0, set_server = 0;
825   
826   while (unformat_check_input(input) != UNFORMAT_END_OF_INPUT) 
827     {
828       if (unformat (input, "server %U", 
829                     unformat_ip4_address, &server_addr)) 
830         set_server = 1;
831       else if (unformat (input, "server-fib-id %d", &server_fib_id))
832         ;
833       else if (unformat (input, "rx-fib-id %d", &rx_fib_id))
834         ;
835       else if (unformat(input, "src-address %U", 
836                         unformat_ip4_address, &src_addr))
837         set_src = 1;
838       else if (unformat (input, "delete") ||
839                unformat (input, "del"))
840         is_del = 1;
841       else
842         break;
843     }
844
845   if (is_del || (set_server && set_src))
846     {
847       int rv;
848
849       rv = dhcp_proxy_set_server (&server_addr, &src_addr, rx_fib_id, 
850                                   server_fib_id, is_del);
851       switch (rv)
852         {
853         case 0:
854           return 0;
855
856         case VNET_API_ERROR_INVALID_DST_ADDRESS:
857           return clib_error_return (0, "Invalid server address");
858           
859         case VNET_API_ERROR_INVALID_SRC_ADDRESS:
860           return clib_error_return (0, "Invalid src address");
861           
862         case VNET_API_ERROR_NO_SUCH_INNER_FIB:
863           return clib_error_return (0, "No such rx fib id %d", rx_fib_id);
864           
865         case VNET_API_ERROR_NO_SUCH_FIB:
866           return clib_error_return (0, "No such server fib id %d", 
867                                     server_fib_id);
868
869         case VNET_API_ERROR_NO_SUCH_ENTRY:
870           return clib_error_return 
871             (0, "Fib id %d: no per-fib DHCP server configured", rx_fib_id);
872
873         default:
874           return clib_error_return (0, "BUG: rv %d", rv);
875         }
876     }
877   else
878     return clib_error_return (0, "parse error`%U'",
879                               format_unformat_error, input);
880 }
881
882 VLIB_CLI_COMMAND (dhcp_proxy_set_command, static) = {
883   .path = "set dhcp proxy",
884   .short_help = "set dhcp proxy [del] server <ip-addr> src-address <ip-addr> [server-fib-id <n>] [rx-fib-id <n>]",
885   .function = dhcp_proxy_set_command_fn,
886 };
887
888 u8 * format_dhcp_proxy_server (u8 * s, va_list * args)
889 {
890   dhcp_proxy_main_t * dm = va_arg (*args, dhcp_proxy_main_t *);
891   dhcp_server_t * server = va_arg (*args, dhcp_server_t *);
892   u32 rx_fib_index = va_arg (*args, u32);
893   ip4_fib_t * rx_fib, * server_fib;
894   u32 server_fib_id = ~0, rx_fib_id = ~0;
895
896   if (dm == 0)
897     {
898       s = format (s, "%=16s%=16s%=14s%=14s", "Server", "Src Address", 
899                   "Server FIB", "RX FIB");
900       return s;
901     }
902
903   server_fib = ip4_fib_get(server->server_fib_index);
904
905   if (server_fib)
906     server_fib_id = server_fib->table_id;
907
908   rx_fib = ip4_fib_get(rx_fib_index);
909
910   if (rx_fib)
911     rx_fib_id = rx_fib->table_id;
912
913   s = format (s, "%=16U%=16U%=14u%=14u",
914               format_ip4_address, &server->dhcp_server,
915               format_ip4_address, &server->dhcp_src_address,
916               server_fib_id, rx_fib_id);
917   return s;
918 }
919
920 static clib_error_t *
921 dhcp_proxy_show_command_fn (vlib_main_t * vm,
922                             unformat_input_t * input,
923                             vlib_cli_command_t * cmd)
924 {
925   dhcp_proxy_main_t * dpm = &dhcp_proxy_main;
926   dhcp_server_t * server;
927   u32 server_index, i;
928
929   vlib_cli_output (vm, "%U", format_dhcp_proxy_server, 0 /* header line */,
930                    0, 0);
931
932   vec_foreach_index (i, dpm->dhcp_server_index_by_rx_fib_index)
933   {
934       server_index = dpm->dhcp_server_index_by_rx_fib_index[i];
935       if (~0 == server_index)
936           continue;
937
938       server = pool_elt_at_index (dpm->dhcp_servers, server_index);
939
940       vlib_cli_output (vm, "%U", format_dhcp_proxy_server, dpm, 
941                        server, i);
942     }
943
944   return 0;
945 }
946
947 VLIB_CLI_COMMAND (dhcp_proxy_show_command, static) = {
948   .path = "show dhcp proxy",
949   .short_help = "Display dhcp proxy server info",
950   .function = dhcp_proxy_show_command_fn,
951 };
952
953 void
954 dhcp_proxy_dump (void *opaque,
955                  u32 context)
956 {
957   dhcp_proxy_main_t * dpm = &dhcp_proxy_main;
958   ip4_fib_t *s_fib, *r_fib;
959   dhcp_server_t * server;
960   u32 server_index, i;
961   vss_info *v;
962
963   vec_foreach_index (i, dpm->dhcp_server_index_by_rx_fib_index)
964   {
965       server_index = dpm->dhcp_server_index_by_rx_fib_index[i];
966       if (~0 == server_index)
967           continue;
968
969       server = pool_elt_at_index (dpm->dhcp_servers, server_index);
970       v = dhcp_get_vss_info(dpm, i);
971
972       ip46_address_t src_addr = {
973           .ip4 = server->dhcp_src_address,
974       };
975       ip46_address_t server_addr = {
976           .ip4 = server->dhcp_server,
977       };
978
979       s_fib = ip4_fib_get(server->server_fib_index);
980       r_fib = ip4_fib_get(i);
981
982       dhcp_send_details(opaque,
983                         context,
984                         &server_addr,
985                         &src_addr,
986                         s_fib->table_id,
987                         r_fib->table_id,
988                         (v ? v->vpn_id.fib_id : 0),
989                         (v ? v->vpn_id.oui : 0));
990   }
991 }
992
993 int dhcp_proxy_set_option82_vss(u32 tbl_id,
994                                 u32 oui,
995                                 u32 fib_id, 
996                                 int is_del)
997 {
998   dhcp_proxy_main_t *dm = &dhcp_proxy_main;
999   vss_info *v = NULL;
1000   u32  rx_fib_index;
1001   int rc = 0;
1002   
1003   rx_fib_index = ip4_fib_table_find_or_create_and_lock(tbl_id);
1004   v = dhcp_get_vss_info(dm, rx_fib_index);
1005
1006   if (NULL != v)
1007   {
1008       if (is_del)
1009       {
1010           /* release the lock held on the table when the VSS
1011            * info was created */
1012           fib_table_unlock (rx_fib_index,
1013                             FIB_PROTOCOL_IP4);
1014
1015           pool_put (dm->vss, v);
1016           dm->vss_index_by_rx_fib_index[rx_fib_index] = ~0;
1017       }
1018       else
1019       {
1020           /* this is a modify */
1021           v->vpn_id.fib_id = fib_id;
1022           v->vpn_id.oui = oui;
1023       }
1024   }
1025   else
1026   {
1027       if (is_del)
1028           rc = VNET_API_ERROR_NO_SUCH_ENTRY;
1029       else
1030       {
1031           /* create a new entry */
1032           vec_validate_init_empty(dm->vss_index_by_rx_fib_index,
1033                                   rx_fib_index, ~0);
1034
1035           /* hold a lock on the table whilst the VSS info exist */
1036           fib_table_lock (rx_fib_index,
1037                           FIB_PROTOCOL_IP4);
1038
1039           pool_get (dm->vss, v);
1040           v->vpn_id.fib_id = fib_id;
1041           v->vpn_id.oui = oui;
1042           dm->vss_index_by_rx_fib_index[rx_fib_index] = v - dm->vss;
1043       }
1044   }
1045
1046   /* Release the lock taken during the create_or_lock at the start */
1047   fib_table_unlock (rx_fib_index,
1048                     FIB_PROTOCOL_IP4);
1049   
1050   return (rc);
1051 }
1052
1053 static clib_error_t *
1054 dhcp_option_82_vss_fn (vlib_main_t * vm,
1055                         unformat_input_t * input,
1056                         vlib_cli_command_t * cmd)
1057 {
1058   int is_del = 0, got_new_vpn_id=0;
1059   u32 oui=0, fib_id=0, tbl_id=~0;
1060  
1061
1062   while (unformat_check_input(input) != UNFORMAT_END_OF_INPUT) 
1063     {
1064
1065       if (unformat(input, "delete") || unformat(input, "del"))
1066           is_del = 1;    
1067       else if (unformat (input, "oui %d", &oui))
1068           got_new_vpn_id = 1;
1069       else if (unformat (input, "vpn-id %d", &fib_id))
1070           got_new_vpn_id = 1;
1071       else if (unformat (input, "table %d", &tbl_id))
1072           got_new_vpn_id = 1;
1073       else
1074           break;
1075   }
1076   if (tbl_id == ~0)
1077       return clib_error_return (0, "no table ID specified.");
1078   
1079   if (is_del || got_new_vpn_id)
1080     {
1081       int rv;
1082       rv = dhcp_proxy_set_option82_vss(tbl_id, oui, fib_id, is_del);
1083       switch (rv)
1084         {
1085         case 0:
1086             return 0;
1087             
1088         case VNET_API_ERROR_NO_SUCH_FIB:
1089             return clib_error_return (0, "option 82 vss(oui:%d, vpn-id:%d) not found in table %d",
1090                                       oui, fib_id, tbl_id);
1091             
1092         case VNET_API_ERROR_NO_SUCH_ENTRY:
1093             return clib_error_return (0, "option 82 vss for table %d not found in in pool.",
1094                                       tbl_id);
1095         default:
1096           return clib_error_return (0, "BUG: rv %d", rv);
1097         }
1098     }
1099   else
1100       return clib_error_return (0, "parse error`%U'",
1101                                 format_unformat_error, input);
1102 }
1103
1104 VLIB_CLI_COMMAND (dhcp_proxy_vss_command,static) = {
1105   .path = "set dhcp option-82 vss",
1106   .short_help = "set dhcp option-82 vss [del] table <table id> oui <oui> vpn-id <vpn-id>",
1107   .function = dhcp_option_82_vss_fn,
1108 };
1109
1110
1111 static clib_error_t *
1112 dhcp_vss_show_command_fn (vlib_main_t * vm,
1113                           unformat_input_t * input,
1114                           vlib_cli_command_t * cmd)
1115   
1116 {
1117   dhcp_proxy_main_t * dm = &dhcp_proxy_main;
1118   ip4_fib_t *fib;
1119   u32 *fib_index;
1120   vss_info *v;
1121   
1122   vlib_cli_output (vm, "%=9s%=11s%=12s","Table", "OUI", "VPN-ID");
1123   pool_foreach (fib_index, dm->vss_index_by_rx_fib_index,
1124   ({
1125       fib = ip4_fib_get (*fib_index);
1126       v = pool_elt_at_index (dm->vss, *fib_index);
1127
1128       vlib_cli_output (vm, "%=6d%=6d%=12d",
1129                        fib->table_id,
1130                        v->vpn_id.oui,
1131                        v->vpn_id.fib_id);
1132   }));
1133   
1134   return 0;
1135 }
1136
1137 VLIB_CLI_COMMAND (dhcp_proxy_vss_show_command, static) = {
1138   .path = "show dhcp vss",
1139   .short_help = "show dhcp VSS",
1140   .function = dhcp_vss_show_command_fn,
1141 };
1142
1143 static clib_error_t *
1144 dhcp_option_82_address_show_command_fn (vlib_main_t * vm,
1145                                 unformat_input_t * input,
1146                                 vlib_cli_command_t * cmd)
1147   
1148 {
1149   dhcp_proxy_main_t *dm = &dhcp_proxy_main;
1150   vnet_main_t *vnm = vnet_get_main();                                     
1151   u32 sw_if_index0=0, sw_if_index;
1152   ip4_address_t *ia0;
1153   vnet_sw_interface_t *swif;
1154   
1155   while (unformat_check_input(input) != UNFORMAT_END_OF_INPUT) 
1156     {
1157       
1158       if (unformat(input, "%U",
1159                    unformat_vnet_sw_interface, dm->vnet_main, &sw_if_index0))
1160         {
1161           swif = vnet_get_sw_interface (vnm, sw_if_index0);
1162           sw_if_index = (swif->flags & VNET_SW_INTERFACE_FLAG_UNNUMBERED) ?
1163             swif->unnumbered_sw_if_index : sw_if_index0;
1164           ia0 = ip4_interface_first_address(&ip4_main, sw_if_index, 0);
1165           if (ia0)
1166             {
1167               vlib_cli_output (vm, "%=20s%=20s", "interface", 
1168                                "source IP address");
1169               
1170               vlib_cli_output (vm, "%=20U%=20U",
1171                                format_vnet_sw_if_index_name, 
1172                                dm->vnet_main, sw_if_index0,
1173                                format_ip4_address, ia0);
1174             }
1175           else
1176             vlib_cli_output (vm, "%=34s %=20U", 
1177                              "No IPv4 address configured on",
1178                              format_vnet_sw_if_index_name, 
1179                              dm->vnet_main, sw_if_index);
1180         }
1181       else
1182         break;
1183     }
1184   
1185   return 0;
1186 }
1187
1188 VLIB_CLI_COMMAND (dhcp_proxy_address_show_command,static) = {
1189   .path = "show dhcp option-82-address interface",
1190   .short_help = "show dhcp option-82-address interface <interface>",
1191   .function = dhcp_option_82_address_show_command_fn,
1192 };