6b15c51b38f54fde5b98953c2599696877607520
[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           u8 *end = NULL;
138           u32 fib_index;
139           dhcp_proxy_t *proxy;
140           dhcp_server_t *server;
141           u32 rx_sw_if_index;
142           dhcp_option_t *o;
143           u32 len = 0;
144           vlib_buffer_free_list_t *fl;
145           u8 is_discover = 0;
146
147           bi0 = from[0];
148           from += 1;
149           n_left_from -= 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 ==
164               clib_net_to_host_u16 (UDP_DST_PORT_dhcp_to_server))
165             {
166               vlib_buffer_advance (b0, sizeof (*u0));
167               next0 = DHCP_PROXY_TO_SERVER_INPUT_NEXT_SEND_TO_CLIENT;
168               error0 = 0;
169               pkts_to_client++;
170               goto do_enqueue;
171             }
172
173           rx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
174
175           fib_index = im->fib_index_by_sw_if_index[rx_sw_if_index];
176           proxy = dhcp_get_proxy (dpm, fib_index, FIB_PROTOCOL_IP4);
177
178           if (PREDICT_FALSE (NULL == proxy))
179             {
180               error0 = DHCP_PROXY_ERROR_NO_SERVER;
181               next0 = DHCP_PROXY_TO_SERVER_INPUT_NEXT_DROP;
182               pkts_no_server++;
183               goto do_trace;
184             }
185
186           server = &proxy->dhcp_servers[0];
187           vlib_buffer_advance (b0, -(sizeof (*ip0)));
188           ip0 = vlib_buffer_get_current (b0);
189
190           /* disable UDP checksum */
191           u0->checksum = 0;
192           sum0 = ip0->checksum;
193           old0 = ip0->dst_address.as_u32;
194           new0 = server->dhcp_server.ip4.as_u32;
195           ip0->dst_address.as_u32 = server->dhcp_server.ip4.as_u32;
196           sum0 = ip_csum_update (sum0, old0, new0,
197                                  ip4_header_t /* structure */ ,
198                                  dst_address /* changed member */ );
199           ip0->checksum = ip_csum_fold (sum0);
200
201           sum0 = ip0->checksum;
202           old0 = ip0->src_address.as_u32;
203           new0 = proxy->dhcp_src_address.ip4.as_u32;
204           ip0->src_address.as_u32 = new0;
205           sum0 = ip_csum_update (sum0, old0, new0,
206                                  ip4_header_t /* structure */ ,
207                                  src_address /* changed member */ );
208           ip0->checksum = ip_csum_fold (sum0);
209
210           /* Send to DHCP server via the configured FIB */
211           vnet_buffer (b0)->sw_if_index[VLIB_TX] = server->server_fib_index;
212
213           h0->gateway_ip_address.as_u32 = proxy->dhcp_src_address.ip4.as_u32;
214           pkts_to_server++;
215
216           o = (dhcp_option_t *) h0->options;
217
218           fib_index = im->fib_index_by_sw_if_index
219             [vnet_buffer (b0)->sw_if_index[VLIB_RX]];
220
221           end = b0->data + b0->current_data + b0->current_length;
222           /* TLVs are not performance-friendly... */
223           while (o->option != 0xFF /* end of options */  && (u8 *) o < end)
224             {
225               if (DHCP_PACKET_OPTION_MSG_TYPE == o->option)
226                 {
227                   if (DHCP_PACKET_DISCOVER == o->data[0])
228                     {
229                       is_discover = 1;
230                     }
231                 }
232               o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
233             }
234
235           fl =
236             vlib_buffer_get_free_list (vm,
237                                        vlib_buffer_get_free_list_index (b0));
238           // start write at (option*)o, some packets have padding
239           if (((u8 *) o - (u8 *) b0->data + VPP_DHCP_OPTION82_SIZE) >
240               fl->n_data_bytes)
241             {
242               next0 = DHCP_PROXY_TO_SERVER_INPUT_NEXT_DROP;
243               pkts_too_big++;
244               goto do_trace;
245             }
246
247           if ((o->option == 0xFF) && ((u8 *) o <= end))
248             {
249               vnet_main_t *vnm = vnet_get_main ();
250               u16 old_l0, new_l0;
251               ip4_address_t _ia0, *ia0 = &_ia0;
252               dhcp_vss_t *vss;
253               vnet_sw_interface_t *swif;
254               sw_if_index = 0;
255               original_sw_if_index = 0;
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] = 0xFF;
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] = 0xFF;  /* "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                   ci0 = vlib_get_buffer_index (vm, c0);
362                   server = &proxy->dhcp_servers[ii];
363
364                   ip0 = vlib_buffer_get_current (c0);
365
366                   sum0 = ip0->checksum;
367                   old0 = ip0->dst_address.as_u32;
368                   new0 = server->dhcp_server.ip4.as_u32;
369                   ip0->dst_address.as_u32 = server->dhcp_server.ip4.as_u32;
370                   sum0 = ip_csum_update (sum0, old0, new0,
371                                          ip4_header_t /* structure */ ,
372                                          dst_address /* changed member */ );
373                   ip0->checksum = ip_csum_fold (sum0);
374
375                   to_next[0] = ci0;
376                   to_next += 1;
377                   n_left_to_next -= 1;
378
379                   vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
380                                                    to_next, n_left_to_next,
381                                                    ci0, next0);
382
383                   if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
384                     {
385                       dhcp_proxy_trace_t *tr;
386
387                       tr = vlib_add_trace (vm, node, c0, sizeof (*tr));
388                       tr->which = 0;    /* to server */
389                       tr->error = error0;
390                       tr->original_sw_if_index = original_sw_if_index;
391                       tr->sw_if_index = sw_if_index;
392                       if (next0 == DHCP_PROXY_TO_SERVER_INPUT_NEXT_LOOKUP)
393                         tr->trace_ip4_address.as_u32 =
394                           server->dhcp_server.ip4.as_u32;
395                     }
396
397                   if (PREDICT_FALSE (0 == n_left_to_next))
398                     {
399                       vlib_put_next_frame (vm, node, next_index,
400                                            n_left_to_next);
401                       vlib_get_next_frame (vm, node, next_index,
402                                            to_next, n_left_to_next);
403                     }
404                 }
405             }
406         do_trace:
407           if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
408             {
409               dhcp_proxy_trace_t *tr = vlib_add_trace (vm, node,
410                                                        b0, sizeof (*tr));
411               tr->which = 0;    /* to server */
412               tr->error = error0;
413               tr->original_sw_if_index = original_sw_if_index;
414               tr->sw_if_index = sw_if_index;
415               if (next0 == DHCP_PROXY_TO_SERVER_INPUT_NEXT_LOOKUP)
416                 tr->trace_ip4_address.as_u32 =
417                   proxy->dhcp_servers[0].dhcp_server.ip4.as_u32;
418             }
419
420         do_enqueue:
421           to_next[0] = bi0;
422           to_next += 1;
423           n_left_to_next -= 1;
424
425           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
426                                            to_next, n_left_to_next,
427                                            bi0, next0);
428         }
429
430       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
431     }
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           dhcp_option_t *o = (dhcp_option_t *) h0->options;
539           dhcp_option_t *sub;
540
541           /* Parse through TLVs looking for option 82.
542              The circuit-ID is the FIB number we need
543              to track down the client-facing interface */
544
545           while (o->option != 0xFF /* end of options */  &&
546                  (u8 *) o <
547                  (b0->data + b0->current_data + b0->current_length))
548             {
549               if (o->option == 82)
550                 {
551                   u32 vss_exist = 0;
552                   u32 vss_ctrl = 0;
553                   sub = (dhcp_option_t *) & o->data[0];
554                   while (sub->option != 0xFF /* end of options */  &&
555                          (u8 *) sub < (u8 *) (o + o->length))
556                     {
557                       /* If this is one of ours, it will have
558                          total length 12, circuit-id suboption type,
559                          and the sw_if_index */
560                       if (sub->option == 1 && sub->length == 4)
561                         {
562                           sw_if_index = ((sub->data[0] << 24) |
563                                          (sub->data[1] << 16) |
564                                          (sub->data[2] << 8) |
565                                          (sub->data[3]));
566                         }
567                       else if (sub->option == 5 && sub->length == 4)
568                         {
569                           relay_addr.as_u8[0] = sub->data[0];
570                           relay_addr.as_u8[1] = sub->data[1];
571                           relay_addr.as_u8[2] = sub->data[2];
572                           relay_addr.as_u8[3] = sub->data[3];
573                         }
574                       else if (sub->option == 151 &&
575                                sub->length == 7 && sub->data[0] == 1)
576                         vss_exist = 1;
577                       else if (sub->option == 152 && sub->length == 0)
578                         vss_ctrl = 1;
579                       sub = (dhcp_option_t *)
580                         (((uword) sub) + (sub->length + 2));
581                     }
582                   if (vss_ctrl && vss_exist)
583                     vlib_node_increment_counter
584                       (vm, dhcp_proxy_to_client_node.index,
585                        DHCP_PROXY_ERROR_OPTION_82_VSS_NOT_PROCESSED, 1);
586
587                 }
588               o = (dhcp_option_t *) (((uword) o) + (o->length + 2));
589             }
590         }
591
592       if (sw_if_index == (u32) ~ 0)
593         {
594           error0 = DHCP_PROXY_ERROR_NO_OPTION_82;
595
596         drop_packet:
597           vlib_node_increment_counter (vm, dhcp_proxy_to_client_node.index,
598                                        error0, 1);
599           f0 = vlib_get_frame_to_node (vm, dpm->error_drop_node_index);
600           to_next0 = vlib_frame_vector_args (f0);
601           to_next0[0] = bi0;
602           f0->n_vectors = 1;
603           vlib_put_frame_to_node (vm, dpm->error_drop_node_index, f0);
604           goto do_trace;
605         }
606
607       if (relay_addr.as_u32 == 0)
608         {
609           error0 = DHCP_PROXY_ERROR_BAD_OPTION_82_ADDR;
610           goto drop_packet;
611         }
612
613       if (sw_if_index >= vec_len (im->fib_index_by_sw_if_index))
614         {
615           error0 = DHCP_PROXY_ERROR_BAD_OPTION_82_ITF;
616           goto drop_packet;
617         }
618
619       fib_index = im->fib_index_by_sw_if_index[sw_if_index];
620       proxy = dhcp_get_proxy (dpm, fib_index, FIB_PROTOCOL_IP4);
621
622       if (PREDICT_FALSE (NULL == proxy))
623         {
624           error0 = DHCP_PROXY_ERROR_NO_SERVER;
625           goto drop_packet;
626         }
627
628       vec_foreach (server, proxy->dhcp_servers)
629       {
630         if (ip0->src_address.as_u32 == server->dhcp_server.ip4.as_u32)
631           {
632             goto server_found;
633           }
634       }
635
636       error0 = DHCP_PROXY_ERROR_BAD_SVR_FIB_OR_ADDRESS;
637       goto drop_packet;
638
639     server_found:
640       vnet_buffer (b0)->sw_if_index[VLIB_TX] = sw_if_index;
641
642       swif = vnet_get_sw_interface (vnm, sw_if_index);
643       original_sw_if_index = sw_if_index;
644       if (swif->flags & VNET_SW_INTERFACE_FLAG_UNNUMBERED)
645         sw_if_index = swif->unnumbered_sw_if_index;
646
647       ia0 = ip4_interface_first_address (&ip4_main, sw_if_index, 0);
648       if (ia0 == 0)
649         {
650           error0 = DHCP_PROXY_ERROR_NO_INTERFACE_ADDRESS;
651           goto drop_packet;
652         }
653
654       if (relay_addr.as_u32 != ia0->as_u32)
655         {
656           error0 = DHCP_PROXY_ERROR_BAD_YIADDR;
657           goto drop_packet;
658         }
659
660       u0->checksum = 0;
661       u0->dst_port = clib_net_to_host_u16 (UDP_DST_PORT_dhcp_to_client);
662       sum0 = ip0->checksum;
663       old0 = ip0->dst_address.as_u32;
664       new0 = 0xFFFFFFFF;
665       ip0->dst_address.as_u32 = new0;
666       sum0 = ip_csum_update (sum0, old0, new0, ip4_header_t /* structure */ ,
667                              dst_address /* offset of changed member */ );
668       ip0->checksum = ip_csum_fold (sum0);
669
670       sum0 = ip0->checksum;
671       old0 = ip0->src_address.as_u32;
672       new0 = ia0->as_u32;
673       ip0->src_address.as_u32 = new0;
674       sum0 = ip_csum_update (sum0, old0, new0, ip4_header_t /* structure */ ,
675                              src_address /* offset of changed member */ );
676       ip0->checksum = ip_csum_fold (sum0);
677
678       vlib_buffer_advance (b0, -(sizeof (ethernet_header_t)));
679       si0 = vnet_get_sw_interface (vnm, original_sw_if_index);
680       if (si0->type == VNET_SW_INTERFACE_TYPE_SUB)
681         vlib_buffer_advance (b0, -4 /* space for VLAN tag */ );
682
683       mac0 = vlib_buffer_get_current (b0);
684
685       hi0 = vnet_get_sup_hw_interface (vnm, original_sw_if_index);
686       ei0 = pool_elt_at_index (em->interfaces, hi0->hw_instance);
687       clib_memcpy (mac0->src_address, ei0->address, sizeof (ei0->address));
688       memset (mac0->dst_address, 0xff, sizeof (mac0->dst_address));
689       mac0->type = (si0->type == VNET_SW_INTERFACE_TYPE_SUB) ?
690         clib_net_to_host_u16 (0x8100) : clib_net_to_host_u16 (0x0800);
691
692       if (si0->type == VNET_SW_INTERFACE_TYPE_SUB)
693         {
694           u32 *vlan_tag = (u32 *) (mac0 + 1);
695           u32 tmp;
696           tmp = (si0->sub.id << 16) | 0x0800;
697           *vlan_tag = clib_host_to_net_u32 (tmp);
698         }
699
700       /* $$$ This needs to be rewritten, for sure */
701       f0 = vlib_get_frame_to_node (vm, hi0->output_node_index);
702       to_next0 = vlib_frame_vector_args (f0);
703       to_next0[0] = bi0;
704       f0->n_vectors = 1;
705       vlib_put_frame_to_node (vm, hi0->output_node_index, f0);
706
707     do_trace:
708       if (PREDICT_FALSE (b0->flags & VLIB_BUFFER_IS_TRACED))
709         {
710           dhcp_proxy_trace_t *tr = vlib_add_trace (vm, node,
711                                                    b0, sizeof (*tr));
712           tr->which = 1;        /* to client */
713           tr->trace_ip4_address.as_u32 = ia0 ? ia0->as_u32 : 0;
714           tr->error = error0;
715           tr->original_sw_if_index = original_sw_if_index;
716           tr->sw_if_index = sw_if_index;
717         }
718     }
719   return from_frame->n_vectors;
720 }
721
722 /* *INDENT-OFF* */
723 VLIB_REGISTER_NODE (dhcp_proxy_to_client_node, static) = {
724   .function = dhcp_proxy_to_client_input,
725   .name = "dhcp-proxy-to-client",
726   /* Takes a vector of packets. */
727   .vector_size = sizeof (u32),
728
729   .n_errors = DHCP_PROXY_N_ERROR,
730   .error_strings = dhcp_proxy_error_strings,
731   .format_buffer = format_dhcp_proxy_header_with_length,
732   .format_trace = format_dhcp_proxy_trace,
733 #if 0
734   .unformat_buffer = unformat_dhcp_proxy_header,
735 #endif
736 };
737 /* *INDENT-ON* */
738
739 static clib_error_t *
740 dhcp4_proxy_init (vlib_main_t * vm)
741 {
742   dhcp_proxy_main_t *dm = &dhcp_proxy_main;
743   vlib_node_t *error_drop_node;
744
745   error_drop_node = vlib_get_node_by_name (vm, (u8 *) "error-drop");
746   dm->error_drop_node_index = error_drop_node->index;
747
748   udp_register_dst_port (vm, UDP_DST_PORT_dhcp_to_client,
749                          dhcp_proxy_to_client_node.index, 1 /* is_ip4 */ );
750
751   udp_register_dst_port (vm, UDP_DST_PORT_dhcp_to_server,
752                          dhcp_proxy_to_server_node.index, 1 /* is_ip4 */ );
753
754   return 0;
755 }
756
757
758 VLIB_INIT_FUNCTION (dhcp4_proxy_init);
759
760 int
761 dhcp4_proxy_set_server (ip46_address_t * addr,
762                         ip46_address_t * src_addr,
763                         u32 rx_table_id, u32 server_table_id, int is_del)
764 {
765   u32 rx_fib_index = 0;
766   int rc = 0;
767
768   const fib_prefix_t all_1s = {
769     .fp_len = 32,
770     .fp_addr.ip4.as_u32 = 0xffffffff,
771     .fp_proto = FIB_PROTOCOL_IP4,
772   };
773
774   if (ip46_address_is_zero (addr))
775     return VNET_API_ERROR_INVALID_DST_ADDRESS;
776
777   if (ip46_address_is_zero (src_addr))
778     return VNET_API_ERROR_INVALID_SRC_ADDRESS;
779
780   rx_fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP4,
781                                                     rx_table_id,
782                                                     FIB_SOURCE_DHCP);
783
784   if (is_del)
785     {
786       if (dhcp_proxy_server_del (FIB_PROTOCOL_IP4, rx_fib_index,
787                                  addr, server_table_id))
788         {
789           fib_table_entry_special_remove (rx_fib_index,
790                                           &all_1s, FIB_SOURCE_DHCP);
791           fib_table_unlock (rx_fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_DHCP);
792         }
793     }
794   else
795     {
796       if (dhcp_proxy_server_add (FIB_PROTOCOL_IP4,
797                                  addr, src_addr,
798                                  rx_fib_index, server_table_id))
799         {
800           fib_table_entry_special_add (rx_fib_index,
801                                        &all_1s,
802                                        FIB_SOURCE_DHCP, FIB_ENTRY_FLAG_LOCAL);
803           fib_table_lock (rx_fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_DHCP);
804         }
805     }
806   fib_table_unlock (rx_fib_index, FIB_PROTOCOL_IP4, FIB_SOURCE_DHCP);
807
808   return (rc);
809 }
810
811 static clib_error_t *
812 dhcp4_proxy_set_command_fn (vlib_main_t * vm,
813                             unformat_input_t * input,
814                             vlib_cli_command_t * cmd)
815 {
816   ip46_address_t server_addr, src_addr;
817   u32 server_table_id = 0, rx_table_id = 0;
818   int is_del = 0;
819   int set_src = 0, set_server = 0;
820
821   memset (&server_addr, 0, sizeof (server_addr));
822   memset (&src_addr, 0, sizeof (src_addr));
823
824   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
825     {
826       if (unformat (input, "server %U",
827                     unformat_ip4_address, &server_addr.ip4))
828         set_server = 1;
829       else if (unformat (input, "server-fib-id %d", &server_table_id))
830         ;
831       else if (unformat (input, "rx-fib-id %d", &rx_table_id))
832         ;
833       else if (unformat (input, "src-address %U",
834                          unformat_ip4_address, &src_addr.ip4))
835         set_src = 1;
836       else if (unformat (input, "delete") || unformat (input, "del"))
837         is_del = 1;
838       else
839         break;
840     }
841
842   if (is_del || (set_server && set_src))
843     {
844       int rv;
845
846       rv = dhcp4_proxy_set_server (&server_addr, &src_addr, rx_table_id,
847                                    server_table_id, is_del);
848       switch (rv)
849         {
850         case 0:
851           return 0;
852
853         case VNET_API_ERROR_INVALID_DST_ADDRESS:
854           return clib_error_return (0, "Invalid server address");
855
856         case VNET_API_ERROR_INVALID_SRC_ADDRESS:
857           return clib_error_return (0, "Invalid src address");
858
859         case VNET_API_ERROR_NO_SUCH_ENTRY:
860           return clib_error_return
861             (0, "Fib id %d: no per-fib DHCP server configured", rx_table_id);
862
863         default:
864           return clib_error_return (0, "BUG: rv %d", rv);
865         }
866     }
867   else
868     return clib_error_return (0, "parse error`%U'",
869                               format_unformat_error, input);
870 }
871
872 /* *INDENT-OFF* */
873 VLIB_CLI_COMMAND (dhcp_proxy_set_command, static) = {
874   .path = "set dhcp proxy",
875   .short_help = "set dhcp proxy [del] server <ip-addr> src-address <ip-addr> [server-fib-id <n>] [rx-fib-id <n>]",
876   .function = dhcp4_proxy_set_command_fn,
877 };
878 /* *INDENT-ON* */
879
880 static u8 *
881 format_dhcp4_proxy_server (u8 * s, va_list * args)
882 {
883   dhcp_proxy_t *proxy = va_arg (*args, dhcp_proxy_t *);
884   ip4_fib_t *rx_fib, *server_fib;
885   dhcp_server_t *server;
886
887   if (proxy == 0)
888     {
889       s = format (s, "%=14s%=16s%s", "RX FIB", "Src Address",
890                   "Servers FIB,Address");
891       return s;
892     }
893
894   rx_fib = ip4_fib_get (proxy->rx_fib_index);
895
896   s = format (s, "%=14u%=16U",
897               rx_fib->table_id,
898               format_ip46_address, &proxy->dhcp_src_address, IP46_TYPE_ANY);
899
900   vec_foreach (server, proxy->dhcp_servers)
901   {
902     server_fib = ip4_fib_get (server->server_fib_index);
903     s = format (s, "%u,%U  ",
904                 server_fib->table_id,
905                 format_ip46_address, &server->dhcp_server, IP46_TYPE_ANY);
906   }
907   return s;
908 }
909
910 static int
911 dhcp4_proxy_show_walk (dhcp_proxy_t * server, void *ctx)
912 {
913   vlib_main_t *vm = ctx;
914
915   vlib_cli_output (vm, "%U", format_dhcp4_proxy_server, server);
916
917   return (1);
918 }
919
920 static clib_error_t *
921 dhcp4_proxy_show_command_fn (vlib_main_t * vm,
922                              unformat_input_t * input,
923                              vlib_cli_command_t * cmd)
924 {
925   vlib_cli_output (vm, "%U", format_dhcp4_proxy_server,
926                    NULL /* header line */ );
927
928   dhcp_proxy_walk (FIB_PROTOCOL_IP4, dhcp4_proxy_show_walk, vm);
929
930   return (NULL);
931 }
932
933 /* *INDENT-OFF* */
934 VLIB_CLI_COMMAND (dhcp_proxy_show_command, static) = {
935   .path = "show dhcp proxy",
936   .short_help = "Display dhcp proxy server info",
937   .function = dhcp4_proxy_show_command_fn,
938 };
939 /* *INDENT-ON* */
940
941 static clib_error_t *
942 dhcp_option_82_vss_fn (vlib_main_t * vm,
943                        unformat_input_t * input, vlib_cli_command_t * cmd)
944 {
945   u8 is_del = 0, vss_type = VSS_TYPE_DEFAULT;
946   u32 oui = 0, fib_id = 0, tbl_id = ~0;
947   u8 *vpn_ascii_id = 0;
948
949   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
950     {
951       if (unformat (input, "table %d", &tbl_id))
952         ;
953       else if (unformat (input, "oui %d", &oui))
954         vss_type = VSS_TYPE_VPN_ID;
955       else if (unformat (input, "vpn-id %d", &fib_id))
956         vss_type = VSS_TYPE_VPN_ID;
957       else if (unformat (input, "vpn-ascii-id %s", &vpn_ascii_id))
958         vss_type = VSS_TYPE_ASCII;
959       else if (unformat (input, "delete") || unformat (input, "del"))
960         is_del = 1;
961       else
962         break;
963     }
964
965   if (tbl_id == ~0)
966     return clib_error_return (0, "no table ID specified.");
967
968   int rv = dhcp_proxy_set_vss (FIB_PROTOCOL_IP4, tbl_id, vss_type,
969                                vpn_ascii_id, oui, fib_id, is_del);
970   switch (rv)
971     {
972     case 0:
973       return 0;
974     case VNET_API_ERROR_NO_SUCH_ENTRY:
975       return clib_error_return (0,
976                                 "option 82 vss for table %d not found in in pool.",
977                                 tbl_id);
978     default:
979       return clib_error_return (0, "BUG: rv %d", rv);
980
981     }
982 }
983
984 /* *INDENT-OFF* */
985 VLIB_CLI_COMMAND (dhcp_proxy_vss_command,static) = {
986   .path = "set dhcp option-82 vss",
987   .short_help = "set dhcp option-82 vss [del] table <table id> [oui <n> vpn-id <n> | vpn-ascii-id <text>]",
988   .function = dhcp_option_82_vss_fn,
989 };
990 /* *INDENT-ON* */
991
992 static clib_error_t *
993 dhcp_vss_show_command_fn (vlib_main_t * vm,
994                           unformat_input_t * input, vlib_cli_command_t * cmd)
995 {
996   dhcp_vss_walk (FIB_PROTOCOL_IP4, dhcp_vss_show_walk, vm);
997
998   return (NULL);
999 }
1000
1001 /* *INDENT-OFF* */
1002 VLIB_CLI_COMMAND (dhcp_proxy_vss_show_command, static) = {
1003   .path = "show dhcp vss",
1004   .short_help = "show dhcp VSS",
1005   .function = dhcp_vss_show_command_fn,
1006 };
1007 /* *INDENT-ON* */
1008
1009 static clib_error_t *
1010 dhcp_option_82_address_show_command_fn (vlib_main_t * vm,
1011                                         unformat_input_t * input,
1012                                         vlib_cli_command_t * cmd)
1013 {
1014   vnet_main_t *vnm = vnet_get_main ();
1015   u32 sw_if_index0 = 0, sw_if_index;
1016   vnet_sw_interface_t *swif;
1017   ip4_address_t *ia0;
1018
1019   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1020     {
1021
1022       if (unformat (input, "%U",
1023                     unformat_vnet_sw_interface, vnm, &sw_if_index0))
1024         {
1025           swif = vnet_get_sw_interface (vnm, sw_if_index0);
1026           sw_if_index = (swif->flags & VNET_SW_INTERFACE_FLAG_UNNUMBERED) ?
1027             swif->unnumbered_sw_if_index : sw_if_index0;
1028           ia0 = ip4_interface_first_address (&ip4_main, sw_if_index, 0);
1029           if (ia0)
1030             {
1031               vlib_cli_output (vm, "%=20s%=20s", "interface",
1032                                "source IP address");
1033
1034               vlib_cli_output (vm, "%=20U%=20U",
1035                                format_vnet_sw_if_index_name,
1036                                vnm, sw_if_index0, format_ip4_address, ia0);
1037             }
1038           else
1039             vlib_cli_output (vm, "%=34s %=20U",
1040                              "No IPv4 address configured on",
1041                              format_vnet_sw_if_index_name, vnm, sw_if_index);
1042         }
1043       else
1044         break;
1045     }
1046
1047   return 0;
1048 }
1049
1050 /* *INDENT-OFF* */
1051 VLIB_CLI_COMMAND (dhcp_proxy_address_show_command,static) = {
1052   .path = "show dhcp option-82-address interface",
1053   .short_help = "show dhcp option-82-address interface <interface>",
1054   .function = dhcp_option_82_address_show_command_fn,
1055 };
1056 /* *INDENT-ON* */
1057
1058 /*
1059  * fd.io coding-style-patch-verification: ON
1060  *
1061  * Local Variables:
1062  * eval: (c-set-style "gnu")
1063  * End:
1064  */