mss_clamp: TCP MSS clamping plugin
[vpp.git] / src / plugins / mss_clamp / mss_clamp.c
1 /*
2  * mss_clamp.c - TCP MSS clamping plug-in
3  *
4  * Copyright (c) 2018 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 <vnet/vnet.h>
19 #include <vnet/plugin/plugin.h>
20 #include <mss_clamp/mss_clamp.h>
21 #include <mss_clamp/mss_clamp.api_types.h>
22
23 mssc_main_t mssc_main;
24
25 /* Action function shared between message handler and debug CLI */
26
27 static void
28 mssc_enable_disable_feat (u32 sw_if_index, u8 dir4, u8 dir6, int enable)
29 {
30   if (dir4 == MSS_CLAMP_DIR_NONE && dir6 == MSS_CLAMP_DIR_NONE)
31     return;
32
33   // ip4
34   if ((dir4 & MSS_CLAMP_DIR_RX) != MSS_CLAMP_DIR_NONE)
35     vnet_feature_enable_disable ("ip4-unicast", "tcp-mss-clamping-ip4-in",
36                                  sw_if_index, enable, 0, 0);
37   if ((dir4 & MSS_CLAMP_DIR_TX) != MSS_CLAMP_DIR_NONE)
38     vnet_feature_enable_disable ("ip4-output", "tcp-mss-clamping-ip4-out",
39                                  sw_if_index, enable, 0, 0);
40   // ip6
41   if ((dir6 & MSS_CLAMP_DIR_RX) != MSS_CLAMP_DIR_NONE)
42     vnet_feature_enable_disable ("ip6-unicast", "tcp-mss-clamping-ip6-in",
43                                  sw_if_index, enable, 0, 0);
44   if ((dir6 & MSS_CLAMP_DIR_TX) != MSS_CLAMP_DIR_NONE)
45     vnet_feature_enable_disable ("ip6-output", "tcp-mss-clamping-ip6-out",
46                                  sw_if_index, enable, 0, 0);
47 }
48
49 int
50 mssc_enable_disable (u32 sw_if_index, u8 dir4, u8 dir6, u16 mss4, u16 mss6)
51 {
52   mssc_main_t *cm = &mssc_main;
53   u8 *dir_enabled4, *dir_enabled6;
54   int rv = 0;
55
56   if (dir4 == MSS_CLAMP_DIR_NONE)
57     mss4 = MSS_CLAMP_UNSET;
58   if (dir6 == MSS_CLAMP_DIR_NONE)
59     mss6 = MSS_CLAMP_UNSET;
60
61   vec_validate_init_empty (cm->dir_enabled4, sw_if_index, MSS_CLAMP_DIR_NONE);
62   vec_validate_init_empty (cm->dir_enabled6, sw_if_index, MSS_CLAMP_DIR_NONE);
63   vec_validate_init_empty (cm->max_mss4, sw_if_index, MSS_CLAMP_UNSET);
64   vec_validate_init_empty (cm->max_mss6, sw_if_index, MSS_CLAMP_UNSET);
65
66   cm->max_mss4[sw_if_index] = mss4;
67   cm->max_mss6[sw_if_index] = mss6;
68   dir_enabled4 = &cm->dir_enabled4[sw_if_index];
69   dir_enabled6 = &cm->dir_enabled6[sw_if_index];
70
71   // Disable the directions that are no longer needed
72   mssc_enable_disable_feat (sw_if_index, (*dir_enabled4) & ~dir4,
73                             (*dir_enabled6) & ~dir6, 0);
74   // Enable the new directions
75   mssc_enable_disable_feat (sw_if_index, ~(*dir_enabled4) & dir4,
76                             ~(*dir_enabled6) & dir6, 1);
77
78   *dir_enabled4 = dir4;
79   *dir_enabled6 = dir6;
80
81   return rv;
82 }
83
84 int
85 mssc_get_mss (u32 sw_if_index, u8 *dir4, u8 *dir6, u16 *mss4, u16 *mss6)
86 {
87   mssc_main_t *cm = &mssc_main;
88   int rv = VNET_API_ERROR_FEATURE_DISABLED;
89
90   if (vec_len (cm->dir_enabled4) > sw_if_index &&
91       MSS_CLAMP_DIR_NONE != cm->dir_enabled4[sw_if_index])
92     {
93       *mss4 = cm->max_mss4[sw_if_index];
94       *dir4 = cm->dir_enabled4[sw_if_index];
95       rv = 0;
96     }
97   else
98     {
99       *mss4 = MSS_CLAMP_DIR_NONE;
100       *dir4 = 0;
101     }
102
103   if (vec_len (cm->dir_enabled6) > sw_if_index &&
104       MSS_CLAMP_DIR_NONE != cm->dir_enabled6[sw_if_index])
105     {
106       *mss6 = cm->max_mss6[sw_if_index];
107       *dir6 = cm->dir_enabled6[sw_if_index];
108       rv = 0;
109     }
110   else
111     {
112       *mss6 = MSS_CLAMP_DIR_NONE;
113       *dir6 = 0;
114     }
115   return rv;
116 }
117
118 static uword
119 unformat_mssc_dir (unformat_input_t *input, va_list *args)
120 {
121   u8 *result = va_arg (*args, u8 *);
122   u8 dir = MSS_CLAMP_DIR_RX | MSS_CLAMP_DIR_TX;
123
124   if (unformat (input, "disable"))
125     dir = MSS_CLAMP_DIR_NONE;
126   else if (unformat (input, "enable"))
127     dir = MSS_CLAMP_DIR_RX | MSS_CLAMP_DIR_TX;
128   else if (unformat (input, "rx"))
129     dir = MSS_CLAMP_DIR_RX;
130   else if (unformat (input, "tx"))
131     dir = MSS_CLAMP_DIR_TX;
132   else
133     return 0;
134
135   *result = dir;
136   return 1;
137 }
138
139 static clib_error_t *
140 mssc_enable_command_fn (vlib_main_t *vm, unformat_input_t *input,
141                         vlib_cli_command_t *cmd)
142 {
143   u32 sw_if_index = ~0;
144   u8 dir4 = ~0, dir6 = ~0;
145   u32 mss4 = ~0, mss6 = ~0;
146   int rv;
147
148   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
149     {
150       if (unformat (input, "ip4 %U", unformat_mssc_dir, &dir4))
151         ;
152       else if (unformat (input, "ip6 %U", unformat_mssc_dir, &dir6))
153         ;
154       else if (unformat (input, "ip4-mss %d", &mss4))
155         ;
156       else if (unformat (input, "ip6-mss %d", &mss6))
157         ;
158       else if (unformat (input, "%U", unformat_vnet_sw_interface,
159                          vnet_get_main (), &sw_if_index))
160         ;
161       else
162         break;
163     }
164
165   if (sw_if_index == ~0)
166     return clib_error_return (0, "Please specify an interface");
167
168   if (dir4 == (u8) ~0 || dir6 == (u8) ~0)
169     return clib_error_return (
170       0, "Please specify the MSS clamping direction for ip4 and ip6");
171
172   if (dir4 != MSS_CLAMP_DIR_NONE)
173     {
174       if (mss4 == ~0)
175         return clib_error_return (
176           0, "Please specify the Max Segment Size for ip4");
177       if (mss4 >= MSS_CLAMP_UNSET)
178         return clib_error_return (0, "Invalid Max Segment Size");
179     }
180   if (dir6 != MSS_CLAMP_DIR_NONE)
181     {
182       if (mss6 == ~0)
183         return clib_error_return (
184           0, "Please specify the Max Segment Size for ip6");
185       if (mss6 >= MSS_CLAMP_UNSET)
186         return clib_error_return (0, "Invalid Max Segment Size");
187     }
188
189   rv = mssc_enable_disable (sw_if_index, dir4, dir6, mss4, mss6);
190
191   if (rv)
192     return clib_error_return (0, "Failed: %d = %U", rv, format_vnet_api_errno,
193                               rv);
194
195   return (NULL);
196 }
197
198 VLIB_CLI_COMMAND (mssc_enable_disable_command, static) = {
199   .path = "set interface tcp-mss-clamp",
200   .short_help = "set interface tcp-mss-clamp <interface-name> "
201                 "ip4 [enable|disable|rx|tx] ip4-mss <size> "
202                 "ip6 [enable|disable|rx|tx] ip6-mss <size>",
203   .function = mssc_enable_command_fn,
204 };
205
206 static u8 *
207 format_mssc_clamping (u8 *s, va_list *args)
208 {
209   u8 dir = va_arg (*args, u32);
210   u16 mss = va_arg (*args, u32);
211 #define DIR2S(d)                                                              \
212   (((d) == (MSS_CLAMP_DIR_RX | MSS_CLAMP_DIR_TX)) ?                           \
213      "" :                                                                     \
214      (((d) == MSS_CLAMP_DIR_RX) ? " [RX]" : " [TX]"))
215
216   if (MSS_CLAMP_DIR_NONE == dir)
217     {
218       return format (s, "disabled");
219     }
220   u32 mss_u32 = mss;
221   return format (s, "%d%s", mss_u32, DIR2S (dir));
222 }
223
224 static clib_error_t *
225 mssc_show_command_fn (vlib_main_t *vm, unformat_input_t *input,
226                       vlib_cli_command_t *cmd)
227 {
228   mssc_main_t *cm = &mssc_main;
229   u32 sw_if_index = ~0;
230   u32 ii;
231
232   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
233     {
234       if (unformat (input, "%U", unformat_vnet_sw_interface, vnet_get_main (),
235                     &sw_if_index))
236         ;
237       else
238         break;
239     }
240
241   if (sw_if_index == ~0)
242     {
243       vec_foreach_index (ii, cm->dir_enabled4)
244         {
245           u8 dir4 = cm->dir_enabled4[ii];
246           u8 dir6 = cm->dir_enabled6[ii];
247           if (MSS_CLAMP_DIR_NONE != dir4 || MSS_CLAMP_DIR_NONE != dir6)
248             {
249               u16 mss4 = cm->max_mss4[ii];
250               u16 mss6 = cm->max_mss6[ii];
251               vlib_cli_output (vm, "%U: ip4: %U ip6: %U",
252                                format_vnet_sw_if_index_name, vnet_get_main (),
253                                ii, format_mssc_clamping, dir4, mss4,
254                                format_mssc_clamping, dir6, mss6);
255             }
256         }
257     }
258   else
259     {
260       u16 mss4, mss6;
261       u8 dir4, dir6;
262       mssc_get_mss (sw_if_index, &dir4, &dir6, &mss4, &mss6);
263       vlib_cli_output (vm, "%U: ip4: %U ip6: %U", format_vnet_sw_if_index_name,
264                        vnet_get_main (), sw_if_index, format_mssc_clamping,
265                        dir4, mss4, format_mssc_clamping, dir6, mss6);
266     }
267
268   return (NULL);
269 }
270
271 VLIB_CLI_COMMAND (mssc_show_command, static) = {
272   .path = "show interface tcp-mss-clamp",
273   .short_help = "show interface tcp-mss-clamp [interface-name]",
274   .long_help = "show TCP MSS clamping configurations",
275   .function = mssc_show_command_fn,
276 };
277
278 static clib_error_t *
279 mssc_init (vlib_main_t *vm)
280 {
281   return NULL;
282 }
283
284 VLIB_INIT_FUNCTION (mssc_init);
285
286 /*
287  * fd.io coding-style-patch-verification: ON
288  *
289  * Local Variables:
290  * eval: (c-set-style "gnu")
291  * End:
292  */