nat: 1:1 policy NAT
[vpp.git] / src / plugins / nat / pnat / pnat_cli.c
1 /*
2  * Copyright (c) 2021 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <stdbool.h>
17 #include <vlib/vlib.h>
18 #include <vnet/feature/feature.h>
19 #include <vnet/ip/ip.h>
20 #include <vnet/ip/ip4.h>
21 #include <vnet/ip/ip4_packet.h>
22 #include <vppinfra/clib_error.h>
23 #include <vppinfra/pool.h>
24 #include "pnat.h"
25
26 /*
27  * This file contains the handlers for the (unsupported) VPP debug CLI.
28  */
29 u8 *format_pnat_5tuple(u8 *s, va_list *args) {
30     pnat_5tuple_t *t = va_arg(*args, pnat_5tuple_t *);
31     s = format(s, "{");
32     if (t->mask & PNAT_SA)
33         s = format(s, "%U", format_ip4_address, &t->src);
34     else
35         s = format(s, "*");
36     if (t->mask & PNAT_SPORT)
37         s = format(s, ":%u,", t->sport);
38     else
39         s = format(s, ":*,");
40     if (t->proto > 0)
41         s = format(s, "%U,", format_ip_protocol, t->proto);
42     else
43         s = format(s, "*,");
44     if (t->mask & PNAT_DA)
45         s = format(s, "%U", format_ip4_address, &t->dst);
46     else
47         s = format(s, "*");
48     if (t->mask & PNAT_DPORT)
49         s = format(s, ":%u", t->dport);
50     else
51         s = format(s, ":*");
52     s = format(s, "}");
53     return s;
54 }
55
56 u8 *format_pnat_translation(u8 *s, va_list *args) {
57     u32 index = va_arg(*args, u32);
58     pnat_translation_t *t = va_arg(*args, pnat_translation_t *);
59     s = format(s, "[%d] match: %U rewrite: %U", index, format_pnat_5tuple,
60                &t->match, format_pnat_5tuple, &t->rewrite);
61     return s;
62 }
63
64 static u8 *format_pnat_mask(u8 *s, va_list *args) {
65     pnat_mask_t t = va_arg(*args, pnat_mask_t);
66     if (t & PNAT_SA)
67         s = format(s, "SA ");
68     if (t & PNAT_SPORT)
69         s = format(s, "SP ");
70     if (t & PNAT_DA)
71         s = format(s, "DA ");
72     if (t & PNAT_DPORT)
73         s = format(s, "DP");
74     return s;
75 }
76
77 static u8 *format_pnat_interface(u8 *s, va_list *args) {
78     pnat_interface_t *interface = va_arg(*args, pnat_interface_t *);
79     s = format(s, "sw_if_index: %d", interface->sw_if_index);
80     if (interface->enabled[PNAT_IP4_INPUT]) {
81         s = format(s, " input mask: %U", format_pnat_mask,
82                    interface->lookup_mask[PNAT_IP4_INPUT]);
83     }
84     if (interface->enabled[PNAT_IP4_OUTPUT]) {
85         s = format(s, " output mask: %U", format_pnat_mask,
86                    interface->lookup_mask[PNAT_IP4_OUTPUT]);
87     }
88     return s;
89 }
90
91 uword unformat_pnat_5tuple(unformat_input_t *input, va_list *args) {
92     pnat_5tuple_t *t = va_arg(*args, pnat_5tuple_t *);
93     u32 dport, sport;
94     while (1) {
95         if (unformat(input, "src %U", unformat_ip4_address, &t->src))
96             t->mask |= PNAT_SA;
97         else if (unformat(input, "dst %U", unformat_ip4_address, &t->dst))
98             t->mask |= PNAT_DA;
99         else if (unformat(input, "sport %d", &sport)) {
100             if (sport < 0 || sport > 65535)
101                 return 0;
102             t->mask |= PNAT_SPORT;
103             t->sport = sport;
104         } else if (unformat(input, "dport %d", &dport)) {
105             if (dport < 0 || dport > 65535)
106                 return 0;
107             t->mask |= PNAT_DPORT;
108             t->dport = dport;
109         } else if (unformat(input, "proto %U", unformat_ip_protocol, &t->proto))
110             ;
111         else
112             break;
113     }
114     return 1;
115 }
116
117 static clib_error_t *set_pnat_translation_command_fn(vlib_main_t *vm,
118                                                      unformat_input_t *input,
119                                                      vlib_cli_command_t *cmd) {
120     unformat_input_t _line_input, *line_input = &_line_input;
121     clib_error_t *error = 0;
122     bool in = false, out = false;
123     bool match_set = false, rewrite_set = false;
124     bool add = true;
125     u32 sw_if_index = ~0;
126     pnat_5tuple_t match = {0};
127     pnat_5tuple_t rewrite = {0};
128
129     /* Get a line of input. */
130     if (!unformat_user(input, unformat_line_input, line_input))
131         return 0;
132
133     while (unformat_check_input(line_input) != UNFORMAT_END_OF_INPUT) {
134         if (unformat(line_input, "match %U", unformat_pnat_5tuple, &match))
135             match_set = true;
136         else if (unformat(line_input, "rewrite %U", unformat_pnat_5tuple,
137                           &rewrite))
138             rewrite_set = true;
139         else if (unformat(line_input, "interface %U",
140                           unformat_vnet_sw_interface, vnet_get_main(),
141                           &sw_if_index))
142             ;
143         else if (unformat(line_input, "in")) {
144             in = true;
145         } else if (unformat(line_input, "out")) {
146             out = true;
147         } else if (unformat(line_input, "del")) {
148             add = false;
149         } else {
150             error = clib_error_return(0, "unknown input `%U'",
151                                       format_unformat_error, line_input);
152             goto done;
153         }
154     }
155     if (sw_if_index == ~0) {
156         error = clib_error_return(0, "interface is required `%U'",
157                                   format_unformat_error, line_input);
158         goto done;
159     }
160     if ((in && out) || (!in && !out)) {
161         error = clib_error_return(0, "in or out is required `%U'",
162                                   format_unformat_error, line_input);
163         goto done;
164     }
165     if (!match_set) {
166         error = clib_error_return(0, "missing parameter: match `%U'",
167                                   format_unformat_error, line_input);
168         goto done;
169     }
170     if (!rewrite_set) {
171         error = clib_error_return(0, "missing parameter: rewrite `%U'",
172                                   format_unformat_error, line_input);
173         goto done;
174     }
175
176     if ((match.dport || match.sport) &&
177         (match.proto != 17 && match.proto != 6)) {
178         error = clib_error_return(0, "missing protocol (TCP|UDP): match `%U'",
179                                   format_unformat_error, line_input);
180         goto done;
181     }
182     pnat_attachment_point_t attachment = in ? PNAT_IP4_INPUT : PNAT_IP4_OUTPUT;
183
184     if (add) {
185         u32 binding_index;
186         int rv = pnat_binding_add(&match, &rewrite, &binding_index);
187         if (rv) {
188             error = clib_error_return(0, "Adding binding failed %d", rv);
189             goto done;
190         }
191         rv = pnat_binding_attach(sw_if_index, attachment, binding_index);
192         if (rv) {
193             pnat_binding_del(binding_index);
194             error = clib_error_return(
195                 0, "Attaching binding to interface failed %d", rv);
196             goto done;
197         }
198     } else {
199         /* Lookup binding and lookup interface if both exists proceed with
200          * delete */
201         u32 binding_index = pnat_flow_lookup(sw_if_index, attachment, &match);
202         if (binding_index == ~0) {
203             error = clib_error_return(0, "Binding does not exist");
204             goto done;
205         }
206         pnat_attachment_point_t attachment =
207             in ? PNAT_IP4_INPUT : PNAT_IP4_OUTPUT;
208         int rv = pnat_binding_detach(sw_if_index, attachment, binding_index);
209         if (rv) {
210             error = clib_error_return(0, "Detaching binding failed %d %d",
211                                       binding_index, rv);
212             goto done;
213         }
214         rv = pnat_binding_del(binding_index);
215         if (rv) {
216             error = clib_error_return(0, "Deleting translation failed %d %d",
217                                       binding_index, rv);
218             goto done;
219         }
220     }
221
222 done:
223     unformat_free(line_input);
224
225     return error;
226 }
227
228 VLIB_CLI_COMMAND(set_pnat_translation_command, static) = {
229     .path = "set pnat translation",
230     .short_help = "set pnat translation interface <name> match <5-tuple> "
231                   "rewrite <5-tuple> {in|out} [del]",
232     .function = set_pnat_translation_command_fn,
233 };
234
235 static clib_error_t *
236 show_pnat_translations_command_fn(vlib_main_t *vm, unformat_input_t *input,
237                                   vlib_cli_command_t *cmd) {
238     pnat_main_t *pm = &pnat_main;
239     pnat_translation_t *s;
240     clib_error_t *error = 0;
241
242     /* Get a line of input. */
243     pool_foreach(s, pm->translations) {
244         vlib_cli_output(vm, "%U", format_pnat_translation, s - pm->translations,
245                         s);
246     }
247     return error;
248 }
249
250 VLIB_CLI_COMMAND(show_pnat_translations_command, static) = {
251     .path = "show pnat translations",
252     .short_help = "show pnat translations",
253     .function = show_pnat_translations_command_fn,
254 };
255
256 static clib_error_t *show_pnat_interfaces_command_fn(vlib_main_t *vm,
257                                                      unformat_input_t *input,
258                                                      vlib_cli_command_t *cmd) {
259     pnat_main_t *pm = &pnat_main;
260     pnat_interface_t *interface;
261     clib_error_t *error = 0;
262
263     /* Get a line of input. */
264     pool_foreach(interface, pm->interfaces) {
265         vlib_cli_output(vm, "%U", format_pnat_interface, interface);
266     }
267     return error;
268 }
269
270 VLIB_CLI_COMMAND(show_pnat_interfaces_command, static) = {
271     .path = "show pnat interfaces",
272     .short_help = "show pnat interfaces",
273     .function = show_pnat_interfaces_command_fn,
274 };