45e36829a20fb929698b7c676ab9514991416397
[vpp.git] / src / plugins / snat / snat_det.h
1 /*
2  * snat_det.h - deterministic nat definitions
3  *
4  * Copyright (c) 2017 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  * @file
19  * @brief deterministic NAT definitions
20  */
21
22 #ifndef __included_snat_det_h__
23 #define __included_snat_det_h__
24
25 #include <vnet/ip/ip.h>
26 #include <snat/snat.h>
27
28
29 #define SNAT_DET_SES_PER_USER 1000
30
31
32 int snat_det_add_map (snat_main_t * sm, ip4_address_t * in_addr, u8 in_plen,
33                       ip4_address_t * out_addr, u8 out_plen, int is_add);
34
35 always_inline int
36 is_addr_in_net (ip4_address_t * addr, ip4_address_t * net, u8 plen)
37 {
38   if (net->as_u32 == (addr->as_u32 & ip4_main.fib_masks[plen]))
39     return 1;
40   return 0;
41 }
42
43 always_inline snat_det_map_t *
44 snat_det_map_by_user (snat_main_t * sm, ip4_address_t * user_addr)
45 {
46   snat_det_map_t *dm;
47
48   /* *INDENT-OFF* */
49   pool_foreach (dm, sm->det_maps,
50   ({
51     if (is_addr_in_net(user_addr, &dm->in_addr, dm->in_plen))
52       return dm;
53   }));
54   /* *INDENT-ON* */
55   return 0;
56 }
57
58 always_inline snat_det_map_t *
59 snat_det_map_by_out (snat_main_t * sm, ip4_address_t * out_addr)
60 {
61   snat_det_map_t *dm;
62
63   /* *INDENT-OFF* */
64   pool_foreach (dm, sm->det_maps,
65   ({
66     if (is_addr_in_net(out_addr, &dm->out_addr, dm->out_plen))
67       return dm;
68   }));
69   /* *INDENT-ON* */
70   return 0;
71 }
72
73 always_inline void
74 snat_det_forward (snat_det_map_t * dm, ip4_address_t * in_addr,
75                   ip4_address_t * out_addr, u16 * lo_port)
76 {
77   u32 in_offset, out_offset;
78
79   in_offset = clib_net_to_host_u32 (in_addr->as_u32) -
80     clib_net_to_host_u32 (dm->in_addr.as_u32);
81   out_offset = in_offset / dm->sharing_ratio;
82   out_addr->as_u32 =
83     clib_host_to_net_u32 (clib_net_to_host_u32 (dm->out_addr.as_u32) +
84                           out_offset);
85   *lo_port = 1024 + dm->ports_per_host * (in_offset % dm->sharing_ratio);
86 }
87
88 always_inline void
89 snat_det_reverse (snat_det_map_t * dm, ip4_address_t * out_addr, u16 out_port,
90                   ip4_address_t * in_addr)
91 {
92   u32 in_offset1, in_offset2, out_offset;
93
94   out_offset = clib_net_to_host_u32 (out_addr->as_u32) -
95     clib_net_to_host_u32 (dm->out_addr.as_u32);
96   in_offset1 = out_offset * dm->sharing_ratio;
97   in_offset2 = (out_port - 1024) / dm->ports_per_host;
98   in_addr->as_u32 =
99     clib_host_to_net_u32 (clib_net_to_host_u32 (dm->in_addr.as_u32) +
100                           in_offset1 + in_offset2);
101 }
102
103 always_inline u32
104 snat_det_user_ses_offset (ip4_address_t * addr, u8 plen)
105 {
106   return (clib_net_to_host_u32 (addr->as_u32) & pow2_mask (32 - plen)) *
107     SNAT_DET_SES_PER_USER;
108 }
109
110 always_inline snat_det_session_t *
111 snat_det_get_ses_by_out (snat_det_map_t * dm, ip4_address_t * in_addr,
112                          u64 out_key)
113 {
114   u32 user_offset;
115   u16 i;
116
117   user_offset = snat_det_user_ses_offset (in_addr, dm->in_plen);
118   for (i = 0; i < SNAT_DET_SES_PER_USER; i++)
119     {
120       if (dm->sessions[i + user_offset].out.as_u64 == out_key)
121         return &dm->sessions[i + user_offset];
122     }
123
124   return 0;
125 }
126
127 always_inline snat_det_session_t *
128 snat_det_find_ses_by_in (snat_det_map_t * dm, ip4_address_t * in_addr,
129                          u16 in_port, snat_det_out_key_t out_key)
130 {
131   snat_det_session_t *ses;
132   u32 user_offset;
133   u16 i;
134
135   user_offset = snat_det_user_ses_offset (in_addr, dm->in_plen);
136   for (i = 0; i < SNAT_DET_SES_PER_USER; i++)
137     {
138       ses = &dm->sessions[i + user_offset];
139       if (ses->in_port == in_port &&
140           ses->out.ext_host_addr.as_u32 == out_key.ext_host_addr.as_u32 &&
141           ses->out.ext_host_port == out_key.ext_host_port)
142         return &dm->sessions[i + user_offset];
143     }
144
145   return 0;
146 }
147
148 always_inline snat_det_session_t *
149 snat_det_ses_create (snat_det_map_t * dm, ip4_address_t * in_addr,
150                      u16 in_port, snat_det_out_key_t * out)
151 {
152   u32 user_offset;
153   u16 i;
154
155   user_offset = snat_det_user_ses_offset (in_addr, dm->in_plen);
156
157   for (i = 0; i < SNAT_DET_SES_PER_USER; i++)
158     {
159       if (!dm->sessions[i + user_offset].in_port)
160         {
161           if (__sync_bool_compare_and_swap
162               (&dm->sessions[i + user_offset].in_port, 0, in_port))
163             {
164               dm->sessions[i + user_offset].out.as_u64 = out->as_u64;
165               dm->sessions[i + user_offset].state = SNAT_SESSION_UNKNOWN;
166               dm->sessions[i + user_offset].expire = 0;
167               __sync_add_and_fetch (&dm->ses_num, 1);
168               return &dm->sessions[i + user_offset];
169             }
170         }
171     }
172
173   return 0;
174 }
175
176 always_inline void
177 snat_det_ses_close (snat_det_map_t * dm, snat_det_session_t * ses)
178 {
179   if (__sync_bool_compare_and_swap (&ses->in_port, ses->in_port, 0))
180     {
181       ses->out.as_u64 = 0;
182       __sync_add_and_fetch (&dm->ses_num, -1);
183     }
184 }
185
186 #endif /* __included_snat_det_h__ */
187
188 /*
189  * fd.io coding-style-patch-verification: ON
190  *
191  * Local Variables:
192  * eval: (c-set-style "gnu")
193  * End:
194  */