cnat: flag to disable rsession
[vpp.git] / src / plugins / cnat / cnat_src_policy.c
1 /*
2  * Copyright (c) 2020 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 <cnat/cnat_src_policy.h>
17 #include <cnat/cnat_inline.h>
18
19 #include <cnat/cnat_session.h>
20 #include <cnat/cnat_translation.h>
21
22 cnat_src_policy_main_t cnat_src_policy_main;
23
24 void
25 cnat_register_vip_src_policy (cnat_vip_source_policy_t fp)
26 {
27   cnat_src_policy_main.vip_policy = fp;
28 }
29
30 cnat_source_policy_errors_t
31 cnat_vip_default_source_policy (vlib_main_t * vm,
32                                 vlib_buffer_t * b,
33                                 cnat_session_t * session,
34                                 u32 * rsession_flags,
35                                 const cnat_translation_t * ct,
36                                 cnat_node_ctx_t * ctx)
37 {
38   ip_protocol_t iproto;
39   udp_header_t *udp0;
40   ip4_header_t *ip4;
41   ip6_header_t *ip6;
42
43   if (AF_IP4 == ctx->af)
44     {
45       ip4 = vlib_buffer_get_current (b);
46       iproto = ip4->protocol;
47       udp0 = (udp_header_t *) (ip4 + 1);
48     }
49   else
50     {
51       ip6 = vlib_buffer_get_current (b);
52       iproto = ip6->protocol;
53       udp0 = (udp_header_t *) (ip6 + 1);
54     }
55
56   int rv = 0;
57   if (!session->value.cs_port[VLIB_RX])
58     {
59       u16 sport;
60       sport = udp0->src_port;
61       /* Allocate a port only if asked and if we actually sNATed */
62       if ((ct->flags & CNAT_TR_FLAG_ALLOCATE_PORT) &&
63           (*rsession_flags & CNAT_SESSION_FLAG_HAS_SNAT))
64         {
65           sport = 0;            /* force allocation */
66           session->value.flags |= CNAT_SESSION_FLAG_ALLOC_PORT;
67           rv = cnat_allocate_port (&sport, iproto);
68           if (rv)
69             return CNAT_SOURCE_ERROR_EXHAUSTED_PORTS;
70         }
71
72       session->value.cs_port[VLIB_RX] = sport;
73     }
74   return 0;
75 }
76
77 always_inline cnat_src_port_allocator_t *
78 cnat_get_src_port_allocator (ip_protocol_t iproto)
79 {
80   cnat_src_policy_main_t *cspm = &cnat_src_policy_main;
81   switch (iproto)
82     {
83     case IP_PROTOCOL_TCP:
84       return &cspm->src_ports[CNAT_SPORT_PROTO_TCP];
85     case IP_PROTOCOL_UDP:
86       return &cspm->src_ports[CNAT_SPORT_PROTO_UDP];
87     case IP_PROTOCOL_ICMP:
88       return &cspm->src_ports[CNAT_SPORT_PROTO_ICMP];
89     case IP_PROTOCOL_ICMP6:
90       return &cspm->src_ports[CNAT_SPORT_PROTO_ICMP6];
91     default:
92       return 0;
93     }
94 }
95
96 void
97 cnat_free_port (u16 port, ip_protocol_t iproto)
98 {
99   cnat_src_port_allocator_t *ca;
100   ca = cnat_get_src_port_allocator (iproto);
101   if (!ca)
102     return;
103   clib_spinlock_lock (&ca->lock);
104   clib_bitmap_set_no_check (ca->bmap, port, 0);
105   clib_spinlock_unlock (&ca->lock);
106 }
107
108 int
109 cnat_allocate_port (u16 * port, ip_protocol_t iproto)
110 {
111   *port = clib_net_to_host_u16 (*port);
112   if (*port == 0)
113     *port = MIN_SRC_PORT;
114   cnat_src_port_allocator_t *ca;
115   ca = cnat_get_src_port_allocator (iproto);
116   if (!ca)
117     return -1;
118   clib_spinlock_lock (&ca->lock);
119   if (clib_bitmap_get_no_check (ca->bmap, *port))
120     {
121       *port = clib_bitmap_next_clear (ca->bmap, *port);
122       if (PREDICT_FALSE (*port >= UINT16_MAX))
123         *port = clib_bitmap_next_clear (ca->bmap, MIN_SRC_PORT);
124       if (PREDICT_FALSE (*port >= UINT16_MAX))
125         {
126           clib_spinlock_unlock (&ca->lock);
127           return -1;
128         }
129     }
130   clib_bitmap_set_no_check (ca->bmap, *port, 1);
131   *port = clib_host_to_net_u16 (*port);
132   clib_spinlock_unlock (&ca->lock);
133   return 0;
134 }
135
136 static clib_error_t *
137 cnat_src_policy_init (vlib_main_t * vm)
138 {
139   cnat_src_policy_main_t *cspm = &cnat_src_policy_main;
140   cspm->vip_policy = cnat_vip_default_source_policy;
141   cspm->default_policy = cnat_vip_default_source_policy;
142
143   vec_validate (cspm->src_ports, CNAT_N_SPORT_PROTO);
144   for (int i = 0; i < CNAT_N_SPORT_PROTO; i++)
145     {
146       clib_spinlock_init (&cspm->src_ports[i].lock);
147       clib_bitmap_validate (cspm->src_ports[i].bmap, UINT16_MAX);
148     }
149   /* Inject cleanup callback */
150   cnat_free_port_cb = cnat_free_port;
151   return (NULL);
152 }
153
154 VLIB_INIT_FUNCTION (cnat_src_policy_init);
155
156 /*
157  * fd.io coding-style-patch-verification: ON
158  *
159  * Local Variables:
160  * eval: (c-set-style "gnu")
161  * End:
162  */