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