mactime: add a "top" command to watch device stats
[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                       tr = vlib_add_trace (vm, node, c0, sizeof (*tr));
389                       tr->which = 0;    /* to server */
390                       tr->error = error0;
391                       tr->original_sw_if_index = original_sw_if_index;
392                       tr->sw_if_index = sw_if_index;
393                       if (next0 == DHCP_PROXY_TO_SERVER_INPUT_NEXT_LOOKUP)
394                         tr->trace_ip4_address.as_u32 =
395                           server->dhcp_server.ip4.as_u32;
396                     }
397
398                   if (PREDICT_FALSE (0 == n_left_to_next))
399                     {
400                       vlib_put_next_frame (vm, node, next_index,
401                                            n_left_to_next);
402                       vlib_get_next_frame (vm, node, next_index,
403                                            to_next, n_left_to_next);
404                     }
405                 }
406             }
407         do_trace:
408           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
409             {
410               dhcp_proxy_trace_t *tr = vlib_add_trace (vm, node,
411                                                        b0, sizeof (*tr));
412               tr->which = 0;    /* to server */
413               tr->error = error0;
414               tr->original_sw_if_index = original_sw_if_index;
415               tr->sw_if_index = sw_if_index;
416               if (next0 == DHCP_PROXY_TO_SERVER_INPUT_NEXT_LOOKUP)
417                 tr->trace_ip4_address.as_u32 =
418                   proxy->dhcp_servers[0].dhcp_server.ip4.as_u32;
419             }
420
421         do_enqueue:
422           to_next[0] = bi0;
423           to_next += 1;
424           n_left_to_next -= 1;
425
426           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
427                                            to_next, n_left_to_next,
428                                            bi0, next0);
429         }
430
431       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
432     }
433   vlib_node_increment_counter (vm, dhcp_proxy_to_server_node.index,
434                                DHCP_PROXY_ERROR_RELAY_TO_CLIENT,
435                                pkts_to_client);
436   vlib_node_increment_counter (vm, dhcp_proxy_to_server_node.index,
437                                DHCP_PROXY_ERROR_RELAY_TO_SERVER,
438                                pkts_to_server);
439   vlib_node_increment_counter (vm, dhcp_proxy_to_server_node.index,
440                                DHCP_PROXY_ERROR_NO_SERVER, pkts_no_server);
441   vlib_node_increment_counter (vm, dhcp_proxy_to_server_node.index,
442                                DHCP_PROXY_ERROR_NO_INTERFACE_ADDRESS,
443                                pkts_no_interface_address);
444   vlib_node_increment_counter (vm, dhcp_proxy_to_server_node.index,
445                                DHCP_PROXY_ERROR_PKT_TOO_BIG, pkts_too_big);
446   return from_frame->n_vectors;
447 }
448
449 /* *INDENT-OFF* */
450 VLIB_REGISTER_NODE (dhcp_proxy_to_server_node, static) = {
451   .function = dhcp_proxy_to_server_input,
452   .name = "dhcp-proxy-to-server",
453   /* Takes a vector of packets. */
454   .vector_size = sizeof (u32),
455
456   .n_errors = DHCP_PROXY_N_ERROR,
457   .error_strings = dhcp_proxy_error_strings,
458
459   .n_next_nodes = DHCP_PROXY_TO_SERVER_INPUT_N_NEXT,
460   .next_nodes = {
461 #define _(s,n) [DHCP_PROXY_TO_SERVER_INPUT_NEXT_##s] = n,
462     foreach_dhcp_proxy_to_server_input_next
463 #undef _
464   },
465
466   .format_buffer = format_dhcp_proxy_header_with_length,
467   .format_trace = format_dhcp_proxy_trace,
468 #if 0
469   .unformat_buffer = unformat_dhcp_proxy_header,
470 #endif
471 };
472 /* *INDENT-ON* */
473
474 static uword
475 dhcp_proxy_to_client_input (vlib_main_t * vm,
476                             vlib_node_runtime_t * node,
477                             vlib_frame_t * from_frame)
478 {
479   u32 n_left_from, *from;
480   ethernet_main_t *em = vnet_get_ethernet_main ();
481   dhcp_proxy_main_t *dpm = &dhcp_proxy_main;
482   vnet_main_t *vnm = vnet_get_main ();
483   ip4_main_t *im = &ip4_main;
484
485   from = vlib_frame_vector_args (from_frame);
486   n_left_from = from_frame->n_vectors;
487
488   while (n_left_from > 0)
489     {
490       u32 bi0;
491       vlib_buffer_t *b0;
492       udp_header_t *u0;
493       dhcp_header_t *h0;
494       ip4_header_t *ip0 = 0;
495       ip4_address_t *ia0 = 0;
496       u32 old0, new0;
497       ip_csum_t sum0;
498       ethernet_interface_t *ei0;
499       ethernet_header_t *mac0;
500       vnet_hw_interface_t *hi0;
501       vlib_frame_t *f0;
502       u32 *to_next0;
503       u32 sw_if_index = ~0;
504       vnet_sw_interface_t *si0;
505       u32 error0 = (u32) ~ 0;
506       vnet_sw_interface_t *swif;
507       u32 fib_index;
508       dhcp_proxy_t *proxy;
509       dhcp_server_t *server;
510       u32 original_sw_if_index = (u32) ~ 0;
511       ip4_address_t relay_addr = {
512         .as_u32 = 0,
513       };
514
515       bi0 = from[0];
516       from += 1;
517       n_left_from -= 1;
518
519       b0 = vlib_get_buffer (vm, bi0);
520       h0 = vlib_buffer_get_current (b0);
521
522       /*
523        * udp_local hands us the DHCP header, need udp hdr,
524        * ip hdr to relay to client
525        */
526       vlib_buffer_advance (b0, -(sizeof (*u0)));
527       u0 = vlib_buffer_get_current (b0);
528
529       vlib_buffer_advance (b0, -(sizeof (*ip0)));
530       ip0 = vlib_buffer_get_current (b0);
531
532       /* Consumed by dhcp client code? */
533       if (dhcp_client_for_us (bi0, b0, ip0, u0, h0))
534         continue;
535
536       if (1 /* dpm->insert_option_82 */ )
537         {
538           /* linearize needed to "unclone" and scan options */
539           int rv = vlib_buffer_chain_linearize (vm, b0);
540           if ((b0->flags & VLIB_BUFFER_NEXT_PRESENT) != 0 || !rv)
541             {
542               error0 = DHCP_PROXY_ERROR_PKT_TOO_BIG;
543               goto drop_packet;
544             }
545
546           dhcp_option_t *o = h0->options, *end =
547             (void *) vlib_buffer_get_tail (b0);
548
549           /* Parse through TLVs looking for option 82.
550              The circuit-ID is the FIB number we need
551              to track down the client-facing interface */
552
553           while (o->option != DHCP_PACKET_OPTION_END && o < end)
554             {
555               if (o->option == 82)
556                 {
557                   u32 vss_exist = 0;
558                   u32 vss_ctrl = 0;
559                   dhcp_option_t *sub = (dhcp_option_t *) & o->data[0];
560                   dhcp_option_t *subend =
561                     (dhcp_option_t *) (o->data + o->length);
562                   while (sub->option != DHCP_PACKET_OPTION_END
563                          && sub < subend)
564                     {
565                       /* If this is one of ours, it will have
566                          total length 12, circuit-id suboption type,
567                          and the sw_if_index */
568                       if (sub->option == 1 && sub->length == 4)
569                         {
570                           sw_if_index = ((sub->data[0] << 24) |
571                                          (sub->data[1] << 16) |
572                                          (sub->data[2] << 8) |
573                                          (sub->data[3]));
574                         }
575                       else if (sub->option == 5 && sub->length == 4)
576                         {
577                           relay_addr.as_u8[0] = sub->data[0];
578                           relay_addr.as_u8[1] = sub->data[1];
579                           relay_addr.as_u8[2] = sub->data[2];
580                           relay_addr.as_u8[3] = sub->data[3];
581                         }
582                       else if (sub->option == 151 &&
583                                sub->length == 7 && sub->data[0] == 1)
584                         vss_exist = 1;
585                       else if (sub->option == 152 && sub->length == 0)
586                         vss_ctrl = 1;
587                       sub = (dhcp_option_t *) (sub->data + sub->length);
588                     }
589                   if (vss_ctrl && vss_exist)
590                     vlib_node_increment_counter
591                       (vm, dhcp_proxy_to_client_node.index,
592                        DHCP_PROXY_ERROR_OPTION_82_VSS_NOT_PROCESSED, 1);
593
594                 }
595               o = (dhcp_option_t *) (o->data + o->length);
596             }
597         }
598
599       if (sw_if_index == (u32) ~ 0)
600         {
601           error0 = DHCP_PROXY_ERROR_NO_OPTION_82;
602
603         drop_packet:
604           vlib_node_increment_counter (vm, dhcp_proxy_to_client_node.index,
605                                        error0, 1);
606           f0 = vlib_get_frame_to_node (vm, dpm->error_drop_node_index);
607           to_next0 = vlib_frame_vector_args (f0);
608           to_next0[0] = bi0;
609           f0->n_vectors = 1;
610           vlib_put_frame_to_node (vm, dpm->error_drop_node_index, f0);
611           goto do_trace;
612         }
613
614       if (relay_addr.as_u32 == 0)
615         {
616           error0 = DHCP_PROXY_ERROR_BAD_OPTION_82_ADDR;
617           goto drop_packet;
618         }
619
620       if (sw_if_index >= vec_len (im->fib_index_by_sw_if_index))
621         {
622           error0 = DHCP_PROXY_ERROR_BAD_OPTION_82_ITF;
623           goto drop_packet;
624         }
625
626       fib_index = im->fib_index_by_sw_if_index[sw_if_index];
627       proxy = dhcp_get_proxy (dpm, fib_index, FIB_PROTOCOL_IP4);
628
629       if (PREDICT_FALSE (NULL == proxy))
630         {
631           error0 = DHCP_PROXY_ERROR_NO_SERVER;
632           goto drop_packet;
633         }
634
635       vec_foreach (server, proxy->dhcp_servers)
636       {
637         if (ip0->src_address.as_u32 == server->dhcp_server.ip4.as_u32)
638           {
639             goto server_found;
640           }
641       }
642
643       error0 = DHCP_PROXY_ERROR_BAD_SVR_FIB_OR_ADDRESS;
644       goto drop_packet;
645
646     server_found:
647       vnet_buffer (b0)->sw_if_index[VLIB_TX] = sw_if_index;
648
649       swif = vnet_get_sw_interface (vnm, sw_if_index);
650       original_sw_if_index = sw_if_index;
651       if (swif->flags & VNET_SW_INTERFACE_FLAG_UNNUMBERED)
652         sw_if_index = swif->unnumbered_sw_if_index;
653
654       ia0 = ip4_interface_first_address (&ip4_main, sw_if_index, 0);
655       if (ia0 == 0)
656         {
657           error0 = DHCP_PROXY_ERROR_NO_INTERFACE_ADDRESS;
658           goto drop_packet;
659         }
660
661       if (relay_addr.as_u32 != ia0->as_u32)
662         {
663           error0 = DHCP_PROXY_ERROR_BAD_YIADDR;
664           goto drop_packet;
665         }
666
667       u0->checksum = 0;
668       u0->dst_port = clib_net_to_host_u16 (UDP_DST_PORT_dhcp_to_client);
669       sum0 = ip0->checksum;
670       old0 = ip0->dst_address.as_u32;
671       new0 = 0xFFFFFFFF;
672       ip0->dst_address.as_u32 = new0;
673       sum0 = ip_csum_update (sum0, old0, new0, ip4_header_t /* structure */ ,
674                              dst_address /* offset of changed member */ );
675       ip0->checksum = ip_csum_fold (sum0);
676
677       sum0 = ip0->checksum;
678       old0 = ip0->src_address.as_u32;
679       new0 = ia0->as_u32;
680       ip0->src_address.as_u32 = new0;
681       sum0 = ip_csum_update (sum0, old0, new0, ip4_header_t /* structure */ ,
682                              src_address /* offset of changed member */ );
683       ip0->checksum = ip_csum_fold (sum0);
684
685       vlib_buffer_advance (b0, -(sizeof (ethernet_header_t)));
686       si0 = vnet_get_sw_interface (vnm, original_sw_if_index);
687       if (si0->type == VNET_SW_INTERFACE_TYPE_SUB)
688         vlib_buffer_advance (b0, -4 /* space for VLAN tag */ );
689
690       mac0 = vlib_buffer_get_current (b0);
691
692       hi0 = vnet_get_sup_hw_interface (vnm, original_sw_if_index);
693       ei0 = pool_elt_at_index (em->interfaces, hi0->hw_instance);
694       clib_memcpy (mac0->src_address, ei0->address, sizeof (ei0->address));
695       clib_memset (mac0->dst_address, 0xff, sizeof (mac0->dst_address));
696       mac0->type = (si0->type == VNET_SW_INTERFACE_TYPE_SUB) ?
697         clib_net_to_host_u16 (0x8100) : clib_net_to_host_u16 (0x0800);
698
699       if (si0->type == VNET_SW_INTERFACE_TYPE_SUB)
700         {
701           u32 *vlan_tag = (u32 *) (mac0 + 1);
702           u32 tmp;
703           tmp = (si0->sub.id << 16) | 0x0800;
704           *vlan_tag = clib_host_to_net_u32 (tmp);
705         }
706
707       /* $$$ This needs to be rewritten, for sure */
708       f0 = vlib_get_frame_to_node (vm, hi0->output_node_index);
709       to_next0 = vlib_frame_vector_args (f0);
710       to_next0[0] = bi0;
711       f0->n_vectors = 1;
712       vlib_put_frame_to_node (vm, hi0->output_node_index, f0);
713
714     do_trace:
715       if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
716         {
717           dhcp_proxy_trace_t *tr = vlib_add_trace (vm, node,
718                                                    b0, sizeof (*tr));
719           tr->which = 1;        /* to client */
720           tr->trace_ip4_address.as_u32 = ia0 ? ia0->as_u32 : 0;
721           tr->error = error0;
722           tr->original_sw_if_index = original_sw_if_index;
723           tr->sw_if_index = sw_if_index;
724         }
725     }
726
727   return from_frame->n_vectors;
728 }
729
730 /* *INDENT-OFF* */
731 VLIB_REGISTER_NODE (dhcp_proxy_to_client_node, static) = {
732   .function = dhcp_proxy_to_client_input,
733   .name = "dhcp-proxy-to-client",
734   /* Takes a vector of packets. */
735   .vector_size = sizeof (u32),
736
737   .n_errors = DHCP_PROXY_N_ERROR,
738   .error_strings = dhcp_proxy_error_strings,
739   .format_buffer = format_dhcp_proxy_header_with_length,
740   .format_trace = format_dhcp_proxy_trace,
741 #if 0
742   .unformat_buffer = unformat_dhcp_proxy_header,
743 #endif
744 };
745 /* *INDENT-ON* */
746
747 void
748 dhcp_maybe_register_udp_ports (dhcp_port_reg_flags_t ports)
749 {
750   dhcp_proxy_main_t *dm = &dhcp_proxy_main;
751   vlib_main_t *vm = dm->vlib_main;
752   int port_regs_diff = dm->udp_ports_registered ^ ports;
753
754   if (!port_regs_diff)
755     return;
756
757   if ((port_regs_diff & DHCP_PORT_REG_CLIENT) & ports)
758     udp_register_dst_port (vm, UDP_DST_PORT_dhcp_to_client,
759                            dhcp_proxy_to_client_node.index, 1 /* is_ip4 */ );
760
761   if ((port_regs_diff & DHCP_PORT_REG_SERVER) & ports)
762     udp_register_dst_port (vm, UDP_DST_PORT_dhcp_to_server,
763                            dhcp_proxy_to_server_node.index, 1 /* is_ip4 */ );
764
765   dm->udp_ports_registered |= ports;
766 }
767
768 static clib_error_t *
769 dhcp4_proxy_init (vlib_main_t * vm)
770 {
771   dhcp_proxy_main_t *dm = &dhcp_proxy_main;
772   vlib_node_t *error_drop_node;
773
774   error_drop_node = vlib_get_node_by_name (vm, (u8 *) "error-drop");
775   dm->error_drop_node_index = error_drop_node->index;
776   dm->vlib_main = vm;
777
778   return 0;
779 }
780
781
782 VLIB_INIT_FUNCTION (dhcp4_proxy_init);
783
784 int
785 dhcp4_proxy_set_server (ip46_address_t * addr,
786                         ip46_address_t * src_addr,
787                         u32 rx_table_id, u32 server_table_id, int is_del)
788 {
789   u32 rx_fib_index = 0;
790   int rc = 0;
791
792   const fib_prefix_t all_1s = {
793     .fp_len = 32,
794     .fp_addr.ip4.as_u32 = 0xffffffff,
795     .fp_proto = FIB_PROTOCOL_IP4,
796   };
797
798   if (ip46_address_is_zero (addr))
799     return VNET_API_ERROR_INVALID_DST_ADDRESS;
800
801   if (ip46_address_is_zero (src_addr))
802     return VNET_API_ERROR_INVALID_SRC_ADDRESS;
803
804   dhcp_maybe_register_udp_ports (DHCP_PORT_REG_CLIENT | DHCP_PORT_REG_SERVER);
805
806   rx_fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
807                                                     rx_table_id,
808                                                     FIB_SOURCE_DHCP);
809
810   if (is_del)
811     {
812       if (dhcp_proxy_server_del (FIB_PROTOCOL_IP4, rx_fib_index,
813                                  addr, server_table_id))
814         {
815           fib_table_entry_special_remove (rx_fib_index,
816                                           &all_1s, FIB_SOURCE_DHCP);
817           fib_table_unlock (rx_fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_DHCP);
818         }
819     }
820   else
821     {
822       if (dhcp_proxy_server_add (FIB_PROTOCOL_IP4,
823                                  addr, src_addr,
824                                  rx_fib_index, server_table_id))
825         {
826           fib_table_entry_special_add (rx_fib_index,
827                                        &all_1s,
828                                        FIB_SOURCE_DHCP, FIB_ENTRY_FLAG_LOCAL);
829           fib_table_lock (rx_fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_DHCP);
830         }
831     }
832   fib_table_unlock (rx_fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_DHCP);
833
834   return (rc);
835 }
836
837 static clib_error_t *
838 dhcp4_proxy_set_command_fn (vlib_main_t * vm,
839                             unformat_input_t * input,
840                             vlib_cli_command_t * cmd)
841 {
842   ip46_address_t server_addr, src_addr;
843   u32 server_table_id = 0, rx_table_id = 0;
844   int is_del = 0;
845   int set_src = 0, set_server = 0;
846
847   clib_memset (&server_addr, 0, sizeof (server_addr));
848   clib_memset (&src_addr, 0, sizeof (src_addr));
849
850   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
851     {
852       if (unformat (input, "server %U",
853                     unformat_ip4_address, &server_addr.ip4))
854         set_server = 1;
855       else if (unformat (input, "server-fib-id %d", &server_table_id))
856         ;
857       else if (unformat (input, "rx-fib-id %d", &rx_table_id))
858         ;
859       else if (unformat (input, "src-address %U",
860                          unformat_ip4_address, &src_addr.ip4))
861         set_src = 1;
862       else if (unformat (input, "delete") || unformat (input, "del"))
863         is_del = 1;
864       else
865         break;
866     }
867
868   if (is_del || (set_server && set_src))
869     {
870       int rv;
871
872       rv = dhcp4_proxy_set_server (&server_addr, &src_addr, rx_table_id,
873                                    server_table_id, is_del);
874       switch (rv)
875         {
876         case 0:
877           return 0;
878
879         case VNET_API_ERROR_INVALID_DST_ADDRESS:
880           return clib_error_return (0, "Invalid server address");
881
882         case VNET_API_ERROR_INVALID_SRC_ADDRESS:
883           return clib_error_return (0, "Invalid src address");
884
885         case VNET_API_ERROR_NO_SUCH_ENTRY:
886           return clib_error_return
887             (0, "Fib id %d: no per-fib DHCP server configured", rx_table_id);
888
889         default:
890           return clib_error_return (0, "BUG: rv %d", rv);
891         }
892     }
893   else
894     return clib_error_return (0, "parse error`%U'",
895                               format_unformat_error, input);
896 }
897
898 /* *INDENT-OFF* */
899 VLIB_CLI_COMMAND (dhcp_proxy_set_command, static) = {
900   .path = "set dhcp proxy",
901   .short_help = "set dhcp proxy [del] server <ip-addr> src-address <ip-addr> [server-fib-id <n>] [rx-fib-id <n>]",
902   .function = dhcp4_proxy_set_command_fn,
903 };
904 /* *INDENT-ON* */
905
906 static u8 *
907 format_dhcp4_proxy_server (u8 * s, va_list * args)
908 {
909   dhcp_proxy_t *proxy = va_arg (*args, dhcp_proxy_t *);
910   ip4_fib_t *rx_fib, *server_fib;
911   dhcp_server_t *server;
912
913   if (proxy == 0)
914     {
915       s = format (s, "%=14s%=16s%s", "RX FIB", "Src Address",
916                   "Servers FIB,Address");
917       return s;
918     }
919
920   rx_fib = ip4_fib_get (proxy->rx_fib_index);
921
922   s = format (s, "%=14u%=16U",
923               rx_fib->table_id,
924               format_ip46_address, &proxy->dhcp_src_address, IP46_TYPE_ANY);
925
926   vec_foreach (server, proxy->dhcp_servers)
927   {
928     server_fib = ip4_fib_get (server->server_fib_index);
929     s = format (s, "%u,%U  ",
930                 server_fib->table_id,
931                 format_ip46_address, &server->dhcp_server, IP46_TYPE_ANY);
932   }
933   return s;
934 }
935
936 static int
937 dhcp4_proxy_show_walk (dhcp_proxy_t * server, void *ctx)
938 {
939   vlib_main_t *vm = ctx;
940
941   vlib_cli_output (vm, "%U", format_dhcp4_proxy_server, server);
942
943   return (1);
944 }
945
946 static clib_error_t *
947 dhcp4_proxy_show_command_fn (vlib_main_t * vm,
948                              unformat_input_t * input,
949                              vlib_cli_command_t * cmd)
950 {
951   vlib_cli_output (vm, "%U", format_dhcp4_proxy_server,
952                    NULL /* header line */ );
953
954   dhcp_proxy_walk (FIB_PROTOCOL_IP4, dhcp4_proxy_show_walk, vm);
955
956   return (NULL);
957 }
958
959 /* *INDENT-OFF* */
960 VLIB_CLI_COMMAND (dhcp_proxy_show_command, static) = {
961   .path = "show dhcp proxy",
962   .short_help = "Display dhcp proxy server info",
963   .function = dhcp4_proxy_show_command_fn,
964 };
965 /* *INDENT-ON* */
966
967 static clib_error_t *
968 dhcp_option_82_vss_fn (vlib_main_t * vm,
969                        unformat_input_t * input, vlib_cli_command_t * cmd)
970 {
971   u8 is_del = 0, vss_type = VSS_TYPE_DEFAULT;
972   u32 oui = 0, fib_id = 0, tbl_id = ~0;
973   u8 *vpn_ascii_id = 0;
974
975   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
976     {
977       if (unformat (input, "table %d", &tbl_id))
978         ;
979       else if (unformat (input, "oui %d", &oui))
980         vss_type = VSS_TYPE_VPN_ID;
981       else if (unformat (input, "vpn-id %d", &fib_id))
982         vss_type = VSS_TYPE_VPN_ID;
983       else if (unformat (input, "vpn-ascii-id %s", &vpn_ascii_id))
984         vss_type = VSS_TYPE_ASCII;
985       else if (unformat (input, "delete") || unformat (input, "del"))
986         is_del = 1;
987       else
988         break;
989     }
990
991   if (tbl_id == ~0)
992     return clib_error_return (0, "no table ID specified.");
993
994   int rv = dhcp_proxy_set_vss (FIB_PROTOCOL_IP4, tbl_id, vss_type,
995                                vpn_ascii_id, oui, fib_id, is_del);
996   switch (rv)
997     {
998     case 0:
999       return 0;
1000     case VNET_API_ERROR_NO_SUCH_ENTRY:
1001       return clib_error_return (0,
1002                                 "option 82 vss for table %d not found in in pool.",
1003                                 tbl_id);
1004     default:
1005       return clib_error_return (0, "BUG: rv %d", rv);
1006
1007     }
1008 }
1009
1010 /* *INDENT-OFF* */
1011 VLIB_CLI_COMMAND (dhcp_proxy_vss_command,static) = {
1012   .path = "set dhcp option-82 vss",
1013   .short_help = "set dhcp option-82 vss [del] table <table id> [oui <n> vpn-id <n> | vpn-ascii-id <text>]",
1014   .function = dhcp_option_82_vss_fn,
1015 };
1016 /* *INDENT-ON* */
1017
1018 static clib_error_t *
1019 dhcp_vss_show_command_fn (vlib_main_t * vm,
1020                           unformat_input_t * input, vlib_cli_command_t * cmd)
1021 {
1022   dhcp_vss_walk (FIB_PROTOCOL_IP4, dhcp_vss_show_walk, vm);
1023
1024   return (NULL);
1025 }
1026
1027 /* *INDENT-OFF* */
1028 VLIB_CLI_COMMAND (dhcp_proxy_vss_show_command, static) = {
1029   .path = "show dhcp vss",
1030   .short_help = "show dhcp VSS",
1031   .function = dhcp_vss_show_command_fn,
1032 };
1033 /* *INDENT-ON* */
1034
1035 static clib_error_t *
1036 dhcp_option_82_address_show_command_fn (vlib_main_t * vm,
1037                                         unformat_input_t * input,
1038                                         vlib_cli_command_t * cmd)
1039 {
1040   vnet_main_t *vnm = vnet_get_main ();
1041   u32 sw_if_index0 = 0, sw_if_index;
1042   vnet_sw_interface_t *swif;
1043   ip4_address_t *ia0;
1044
1045   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1046     {
1047
1048       if (unformat (input, "%U",
1049                     unformat_vnet_sw_interface, vnm, &sw_if_index0))
1050         {
1051           swif = vnet_get_sw_interface (vnm, sw_if_index0);
1052           sw_if_index = (swif->flags & VNET_SW_INTERFACE_FLAG_UNNUMBERED) ?
1053             swif->unnumbered_sw_if_index : sw_if_index0;
1054           ia0 = ip4_interface_first_address (&ip4_main, sw_if_index, 0);
1055           if (ia0)
1056             {
1057               vlib_cli_output (vm, "%=20s%=20s", "interface",
1058                                "source IP address");
1059
1060               vlib_cli_output (vm, "%=20U%=20U",
1061                                format_vnet_sw_if_index_name,
1062                                vnm, sw_if_index0, format_ip4_address, ia0);
1063             }
1064           else
1065             vlib_cli_output (vm, "%=34s %=20U",
1066                              "No IPv4 address configured on",
1067                              format_vnet_sw_if_index_name, vnm, sw_if_index);
1068         }
1069       else
1070         break;
1071     }
1072
1073   return 0;
1074 }
1075
1076 /* *INDENT-OFF* */
1077 VLIB_CLI_COMMAND (dhcp_proxy_address_show_command,static) = {
1078   .path = "show dhcp option-82-address interface",
1079   .short_help = "show dhcp option-82-address interface <interface>",
1080   .function = dhcp_option_82_address_show_command_fn,
1081 };
1082 /* *INDENT-ON* */
1083
1084 /*
1085  * fd.io coding-style-patch-verification: ON
1086  *
1087  * Local Variables:
1088  * eval: (c-set-style "gnu")
1089  * End:
1090  */