Line data Source code
1 : /*
2 : * Copyright (c) 2018 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 : * @file
17 : * @brief NAT66 implementation
18 : */
19 :
20 : #include <nat/nat66/nat66.h>
21 : #include <vpp/app/version.h>
22 : #include <vnet/plugin/plugin.h>
23 : #include <vnet/ip/reass/ip6_sv_reass.h>
24 :
25 : nat66_main_t nat66_main;
26 :
27 : /* Hook up input features */
28 30799 : VNET_FEATURE_INIT (nat66_in2out, static) = {
29 : .arc_name = "ip6-unicast",
30 : .node_name = "nat66-in2out",
31 : .runs_before = VNET_FEATURES ("ip6-lookup"),
32 : .runs_after = VNET_FEATURES ("ip6-sv-reassembly-feature"),
33 : };
34 30799 : VNET_FEATURE_INIT (nat66_out2in, static) = {
35 : .arc_name = "ip6-unicast",
36 : .node_name = "nat66-out2in",
37 : .runs_before = VNET_FEATURES ("ip6-lookup"),
38 : .runs_after = VNET_FEATURES ("ip6-sv-reassembly-feature"),
39 : };
40 :
41 : clib_error_t *nat66_plugin_api_hookup (vlib_main_t * vm);
42 :
43 : #define fail_if_enabled() \
44 : do \
45 : { \
46 : nat66_main_t *nm = &nat66_main; \
47 : if (PREDICT_FALSE (nm->enabled)) \
48 : { \
49 : nat66_elog_warn ("plugin enabled"); \
50 : return 1; \
51 : } \
52 : } \
53 : while (0)
54 :
55 : #define fail_if_disabled() \
56 : do \
57 : { \
58 : nat66_main_t *nm = &nat66_main; \
59 : if (PREDICT_FALSE (!nm->enabled)) \
60 : { \
61 : nat66_elog_warn ("plugin disabled"); \
62 : return 1; \
63 : } \
64 : } \
65 : while (0)
66 :
67 : static clib_error_t *
68 559 : nat66_init (vlib_main_t * vm)
69 : {
70 559 : nat66_main_t *nm = &nat66_main;
71 :
72 559 : clib_memset (nm, 0, sizeof (*nm));
73 :
74 559 : nm->session_counters.name = "session counters";
75 559 : nm->in2out_packets.name = "in2out";
76 559 : nm->in2out_packets.stat_segment_name = "/nat64/in2out";
77 559 : nm->out2in_packets.name = "out2in";
78 559 : nm->out2in_packets.stat_segment_name = "/nat64/out2in";
79 :
80 559 : nm->nat_fib_src_hi = fib_source_allocate ("nat66-hi", FIB_SOURCE_PRIORITY_HI,
81 : FIB_SOURCE_BH_SIMPLE);
82 559 : return nat66_plugin_api_hookup (vm);
83 : }
84 :
85 : int
86 2 : nat66_plugin_enable (u32 outside_vrf)
87 : {
88 2 : nat66_main_t *nm = &nat66_main;
89 :
90 2 : u32 static_mapping_buckets = 1024;
91 2 : uword static_mapping_memory_size = 64 << 20;
92 :
93 2 : fail_if_enabled ();
94 :
95 2 : clib_bihash_init_24_8 (&nm->sm_l, "nat66-static-map-by-local",
96 : static_mapping_buckets, static_mapping_memory_size);
97 2 : clib_bihash_init_24_8 (&nm->sm_e, "nat66-static-map-by-external",
98 : static_mapping_buckets, static_mapping_memory_size);
99 :
100 2 : nm->outside_vrf_id = outside_vrf;
101 4 : nm->outside_fib_index = fib_table_find_or_create_and_lock (
102 2 : FIB_PROTOCOL_IP6, outside_vrf, nm->nat_fib_src_hi);
103 2 : nm->enabled = 1;
104 2 : return 0;
105 : }
106 :
107 : int
108 2 : nat66_plugin_disable ()
109 : {
110 2 : nat66_main_t *nm = &nat66_main;
111 : nat66_interface_t *i, *temp;
112 2 : int error = 0;
113 :
114 2 : temp = pool_dup (nm->interfaces);
115 6 : pool_foreach (i, temp)
116 : {
117 4 : if (nat66_interface_is_inside (i))
118 3 : error = nat66_interface_add_del (i->sw_if_index, 1, 0);
119 :
120 4 : if (nat66_interface_is_outside (i))
121 1 : error = nat66_interface_add_del (i->sw_if_index, 0, 0);
122 :
123 4 : if (error)
124 : {
125 0 : nat66_elog_warn ("error occurred while removing interface");
126 : }
127 : }
128 2 : pool_free (temp);
129 2 : pool_free (nm->interfaces);
130 :
131 2 : pool_free (nm->sm);
132 2 : clib_bihash_free_24_8 (&nm->sm_l);
133 2 : clib_bihash_free_24_8 (&nm->sm_e);
134 :
135 2 : nm->interfaces = 0;
136 2 : nm->sm = 0;
137 :
138 2 : vlib_clear_combined_counters (&nm->session_counters);
139 2 : vlib_clear_simple_counters (&nm->in2out_packets);
140 2 : vlib_clear_simple_counters (&nm->out2in_packets);
141 :
142 2 : nm->enabled = 0;
143 2 : return error;
144 : }
145 :
146 : static void
147 4 : nat66_validate_counters (nat66_main_t * nm, u32 sw_if_index)
148 : {
149 4 : vlib_validate_simple_counter (&nm->in2out_packets, sw_if_index);
150 4 : vlib_zero_simple_counter (&nm->in2out_packets, sw_if_index);
151 4 : vlib_validate_simple_counter (&nm->out2in_packets, sw_if_index);
152 4 : vlib_zero_simple_counter (&nm->out2in_packets, sw_if_index);
153 4 : }
154 :
155 : int
156 8 : nat66_interface_add_del (u32 sw_if_index, u8 is_inside, u8 is_add)
157 : {
158 8 : nat66_main_t *nm = &nat66_main;
159 8 : nat66_interface_t *interface = 0, *i;
160 : const char *feature_name;
161 :
162 8 : fail_if_disabled ();
163 :
164 10 : pool_foreach (i, nm->interfaces)
165 : {
166 6 : if (i->sw_if_index == sw_if_index)
167 : {
168 4 : interface = i;
169 4 : break;
170 : }
171 : }
172 :
173 8 : if (is_add)
174 : {
175 4 : if (interface)
176 0 : return VNET_API_ERROR_VALUE_EXIST;
177 :
178 4 : pool_get (nm->interfaces, interface);
179 4 : interface->sw_if_index = sw_if_index;
180 4 : interface->flags =
181 : is_inside ? NAT66_INTERFACE_FLAG_IS_INSIDE :
182 : NAT66_INTERFACE_FLAG_IS_OUTSIDE;
183 4 : nat66_validate_counters (nm, sw_if_index);
184 : }
185 : else
186 : {
187 4 : if (!interface)
188 0 : return VNET_API_ERROR_NO_SUCH_ENTRY;
189 :
190 4 : pool_put (nm->interfaces, interface);
191 : }
192 :
193 8 : feature_name = is_inside ? "nat66-in2out" : "nat66-out2in";
194 8 : int rv = ip6_sv_reass_enable_disable_with_refcnt (sw_if_index, is_add);
195 8 : if (rv)
196 0 : return rv;
197 8 : return vnet_feature_enable_disable ("ip6-unicast", feature_name,
198 : sw_if_index, is_add, 0, 0);
199 : }
200 :
201 : void
202 0 : nat66_interfaces_walk (nat66_interface_walk_fn_t fn, void *ctx)
203 : {
204 0 : nat66_main_t *nm = &nat66_main;
205 0 : nat66_interface_t *i = 0;
206 :
207 0 : pool_foreach (i, nm->interfaces)
208 : {
209 0 : if (fn (i, ctx))
210 0 : break;
211 : }
212 0 : }
213 :
214 : nat66_static_mapping_t *
215 8 : nat66_static_mapping_get (ip6_address_t * addr, u32 fib_index, u8 is_local)
216 : {
217 8 : nat66_main_t *nm = &nat66_main;
218 8 : nat66_static_mapping_t *sm = 0;
219 : nat66_sm_key_t sm_key;
220 : clib_bihash_kv_24_8_t kv, value;
221 :
222 8 : sm_key.addr.as_u64[0] = addr->as_u64[0];
223 8 : sm_key.addr.as_u64[1] = addr->as_u64[1];
224 8 : sm_key.fib_index = fib_index;
225 8 : sm_key.rsvd = 0;
226 :
227 8 : kv.key[0] = sm_key.as_u64[0];
228 8 : kv.key[1] = sm_key.as_u64[1];
229 8 : kv.key[2] = sm_key.as_u64[2];
230 :
231 8 : if (!clib_bihash_search_24_8
232 : (is_local ? &nm->sm_l : &nm->sm_e, &kv, &value))
233 8 : sm = pool_elt_at_index (nm->sm, value.value);
234 :
235 8 : return sm;
236 : }
237 :
238 : int
239 2 : nat66_static_mapping_add_del (ip6_address_t * l_addr, ip6_address_t * e_addr,
240 : u32 vrf_id, u8 is_add)
241 : {
242 2 : nat66_main_t *nm = &nat66_main;
243 2 : int rv = 0;
244 2 : nat66_static_mapping_t *sm = 0;
245 : nat66_sm_key_t sm_key;
246 : clib_bihash_kv_24_8_t kv, value;
247 2 : u32 fib_index = fib_table_find (FIB_PROTOCOL_IP6, vrf_id);
248 :
249 2 : fail_if_disabled ();
250 :
251 2 : sm_key.addr.as_u64[0] = l_addr->as_u64[0];
252 2 : sm_key.addr.as_u64[1] = l_addr->as_u64[1];
253 2 : sm_key.fib_index = fib_index;
254 2 : sm_key.rsvd = 0;
255 2 : kv.key[0] = sm_key.as_u64[0];
256 2 : kv.key[1] = sm_key.as_u64[1];
257 2 : kv.key[2] = sm_key.as_u64[2];
258 :
259 2 : if (!clib_bihash_search_24_8 (&nm->sm_l, &kv, &value))
260 0 : sm = pool_elt_at_index (nm->sm, value.value);
261 :
262 2 : if (is_add)
263 : {
264 2 : if (sm)
265 0 : return VNET_API_ERROR_VALUE_EXIST;
266 :
267 2 : fib_index = fib_table_find_or_create_and_lock (FIB_PROTOCOL_IP6, vrf_id,
268 2 : nm->nat_fib_src_hi);
269 2 : pool_get (nm->sm, sm);
270 2 : clib_memset (sm, 0, sizeof (*sm));
271 2 : sm->l_addr.as_u64[0] = l_addr->as_u64[0];
272 2 : sm->l_addr.as_u64[1] = l_addr->as_u64[1];
273 2 : sm->e_addr.as_u64[0] = e_addr->as_u64[0];
274 2 : sm->e_addr.as_u64[1] = e_addr->as_u64[1];
275 2 : sm->fib_index = fib_index;
276 :
277 2 : sm_key.fib_index = fib_index;
278 2 : kv.key[0] = sm_key.as_u64[0];
279 2 : kv.key[1] = sm_key.as_u64[1];
280 2 : kv.key[2] = sm_key.as_u64[2];
281 2 : kv.value = sm - nm->sm;
282 2 : if (clib_bihash_add_del_24_8 (&nm->sm_l, &kv, 1))
283 0 : nat66_elog_warn ("nat66-static-map-by-local add key failed");
284 2 : sm_key.addr.as_u64[0] = e_addr->as_u64[0];
285 2 : sm_key.addr.as_u64[1] = e_addr->as_u64[1];
286 2 : sm_key.fib_index = 0;
287 2 : kv.key[0] = sm_key.as_u64[0];
288 2 : kv.key[1] = sm_key.as_u64[1];
289 2 : kv.key[2] = sm_key.as_u64[2];
290 2 : if (clib_bihash_add_del_24_8 (&nm->sm_e, &kv, 1))
291 0 : nat66_elog_warn ("nat66-static-map-by-external add key failed");
292 :
293 2 : vlib_validate_combined_counter (&nm->session_counters, kv.value);
294 2 : vlib_zero_combined_counter (&nm->session_counters, kv.value);
295 : }
296 : else
297 : {
298 0 : if (!sm)
299 0 : return VNET_API_ERROR_NO_SUCH_ENTRY;
300 :
301 0 : kv.value = sm - nm->sm;
302 0 : if (clib_bihash_add_del_24_8 (&nm->sm_l, &kv, 0))
303 0 : nat66_elog_warn ("nat66-static-map-by-local delete key failed");
304 0 : sm_key.addr.as_u64[0] = e_addr->as_u64[0];
305 0 : sm_key.addr.as_u64[1] = e_addr->as_u64[1];
306 0 : sm_key.fib_index = 0;
307 0 : kv.key[0] = sm_key.as_u64[0];
308 0 : kv.key[1] = sm_key.as_u64[1];
309 0 : kv.key[2] = sm_key.as_u64[2];
310 0 : if (clib_bihash_add_del_24_8 (&nm->sm_e, &kv, 0))
311 0 : nat66_elog_warn ("nat66-static-map-by-external delete key failed");
312 0 : fib_table_unlock (sm->fib_index, FIB_PROTOCOL_IP6, nm->nat_fib_src_hi);
313 0 : pool_put (nm->sm, sm);
314 : }
315 :
316 2 : return rv;
317 : }
318 :
319 : void
320 1 : nat66_static_mappings_walk (nat66_static_mapping_walk_fn_t fn, void *ctx)
321 : {
322 1 : nat66_main_t *nm = &nat66_main;
323 1 : nat66_static_mapping_t *sm = 0;
324 :
325 2 : pool_foreach (sm, nm->sm)
326 : {
327 1 : if (fn (sm, ctx))
328 0 : break;
329 : }
330 1 : }
331 :
332 : VLIB_PLUGIN_REGISTER () =
333 : {
334 : .version = VPP_BUILD_VER,
335 : .description = "NAT66",
336 : };
337 :
338 1119 : VLIB_INIT_FUNCTION (nat66_init);
339 :
340 : /*
341 : * fd.io coding-style-patch-verification: ON
342 : *
343 : * Local Variables:
344 : * eval: (c-set-style "gnu")
345 : * End:
346 : */
|