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