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