1 IPNAT(7I) Ioctl Requests IPNAT(7I)
2
3 NAME
4 ipnat - IP Filter/NAT module interface
5
6 DESCRIPTION
7 The ipnat device provides interaction with the NAT features of the
8 illumos IP Filter.
9
10 APPLICATION PROGRAMMING INTERFACE
11 The NAT features programming model is a component of the illumos IP
12 Filter and is accessed via the NAT device file /dev/ipnat. Opening the
13 device for reading or writing determines which ioctl calls can be
14 successfully made.
15
16 IOCTLS
17 The caller must construct a ipfobj structure when issuing a SIOCGNATL or
18 SIOCSTPUT ioctl. The ipfobj structure is then passed to the ioctl call
19 and is filled out with ipfo_type set to IPFOBJ_value. IPFOBJ_value
20 provides a matching name for the structure, while ipfo_size is set to the
21 total size of the structure being passed and ipfo_ptr is set to the
22 structure address. The ipfo_rev structure should be set to the current
23 value of IPFILTER_VERSION, while ipfo_offset and ipfo_xxxpad should be
24 set to 0.
25
26 /*
27 * Structure used with SIOCGNATL/SIOCSTPUT.
28 */
29
30 /*
31 * Object structure description. For passing through in ioctls.
32 */
33 typedef struct ipfobj {
34 u_32_t ipfo_rev; /* IPFilter version (IPFILTER_VERSION) */
35 u_32_t ipfo_size; /* size of object at ipfo_ptr */
36 void *ipfo_ptr; /* pointer to object */
37 int ipfo_type; /* type of object being pointed to */
38 int ipfo_offset; /* bytes from ipfo_ptr where to start */
39 u_char ipfo_xxxpad[32]; /* reserved for future use */
40 } ipfobj_t;
41
42 #define IPFILTER_VERSION 4010901 /* IPFilter version */
43 #define IPFOBJ_NATSAVE 8 /* struct nat_save */
44 #define IPFOBJ_NATLOOKUP 9 /* struct natlookup */
45
46 The following ioctl(2) calls may be used to manipulate the ipnat sub-
47 system inside of ipf. Note that the ipnat driver only accept calls from
48 applications using the same data model as the kernel. In other words,
49 64-bit kernels can only accept calls from 64-bit applications. Calls
50 from 32-bit applications fail with EINVAL.
51
52 SIOCSTLCK Set or clear the NAT lock to prevent table updates
53 attributable to packet flow-through.
54
55 SIOCGNATL Search the NAT table for the rdr entry that matches the fields
56 in the natlookup structure. The caller must populate the
57 structure with the address/port information of the accepted
58 TCP connection (nl_inip, nl_inport) and the address/port
59 information of the peer (nl_outip, nl_outport). The nl_flags
60 field must have the IPN_TCP option set. All other fields must
61 be set to 0. If the call succeeds, nl_realip and nl_realport
62 are set to the real destination address and port,
63 respectively. The nl_inport and nl_outport fields must be in
64 host byte order. If IPN_FINDFORWARD is set in nl_flags, a
65 check is made to see if it is possible to create an outgoing
66 NAT session by checking if a packet coming from (nl_realip,
67 nl_realport) and destined for (nl_outip, nl_outport) can be
68 translated. If translation is possible, the flag remains set,
69 otherwise it is cleared in the structure returned to the
70 caller.
71
72 /*
73 * Structure used with SIOCGNATL.
74 */
75 typedef struct natlookup {
76 i6addr_t nl_inipaddr;
77 i6addr_t nl_outipaddr;
78 i6addr_t nl_realipaddr;
79 int nl_v;
80 int nl_flags;
81 u_short nl_inport;
82 u_short nl_outport;
83 u_short nl_realport;
84 } natlookup_t
85
86 #define nl_inip nl_inipaddr.in4
87 #define nl_outip nl_outipaddr.in4
88 #define nl_realip nl_realipaddr.in4
89 #define nl_inip6 nl_inipaddr.in6
90 #define nl_outip6 nl_outipaddr.in6
91 #define nl_realip6 nl_realipaddr.in6
92
93 /*
94 * Accepted values for nl_flags
95 */
96 #define IPN_TCP 0x00001
97 #define IPN_FINDFORWARD 0x400000
98
99 SIOCSTPUT Move a NAT mapping structure from user space into the kernel.
100 This ioctl is used by ipfs(1M) to restore NAT sessions saved
101 in /var/db/ipf/ipnat.ipf. The nat_save structure must have
102 its ipn_nat and ipn_ipnat structures filled out correctly.
103 Fields not assigned a value must be initialised to 0. All
104 pointer fields are adjusted, as appropriate, once the
105 structure is passed into the kernel and none are preserved.
106
107 To create a translation, the following fields must be set:
108
109 Interface name
110 The interface name on which the host is to be exited must
111 be set in nat_ifnames[0].
112
113 Local IP address and port number
114 The connection's local IP address and port number are
115 stored in network byte order using nat_inip/nat_inport.
116
117 Destination address/port
118 The destination address/port are stored in
119 nat_oip/nat_oport.
120
121 Target address/port
122 The translation's target address/port is stored in
123 nat_outip/nat_outport.
124
125 The caller must also precalculate the checksum adjustments
126 necessary to complete the translation and store those values
127 in nat_sumd (delta required for TCP header) and nat_ipsumd
128 (delta required for IP header).
129
130 /*
131 * Structures used with SIOCSTPUT.
132 */
133 typedef struct nat_save {
134 void *ipn_next;
135 struct nat ipn_nat;
136 struct ipnat ipn_ipnat;
137 struct frentry ipn_fr;
138 int ipn_dsize;
139 char ipn_data[4];
140 } nat_save_t;
141
142 typedef struct nat {
143 ipfmutex_t nat_lock;
144 struct nat *nat_next;
145 struct nat **nat_pnext;
146 struct nat *nat_hnext[2];
147 struct nat **nat_phnext[2];
148 struct hostmap *nat_hm;
149 void *nat_data;
150 struct nat **nat_me;
151 struct ipstate *nat_state;
152 struct ap_session *nat_aps;
153 frentry_t *nat_fr;
154 struct ipnat *nat_ptr;
155 void *nat_ifps[2];
156 void *nat_sync;
157 ipftqent_t nat_tqe;
158 u_32_t nat_flags;
159 u_32_t nat_sumd[2];
160 u_32_t nat_ipsumd;
161 u_32_t nat_mssclamp;
162 i6addr_t nat_inip6;
163 i6addr_t nat_outip6;
164 i6addr_t nat_oip6;
165 U_QUAD_T nat_pkts[2];
166 U_QUAD_T nat_bytes[2];
167 union {
168 udpinfo_t nat_unu;
169 tcpinfo_t nat_unt;
170 icmpinfo_t nat_uni;
171 greinfo_t nat_ugre;
172 } nat_un;
173 u_short nat_oport;
174 u_short nat_use;
175 u_char nat_p;
176 int nat_dir;
177 int nat_ref;
178 int nat_hv[2];
179 char nat_ifnames[2][LIFNAMSIZ];
180 int nat_rev;
181 int nat_v;
182 } nat_t;
183
184 #define nat_inip nat_inip6.in4
185 #define nat_outip nat_outip6.in4
186 #define nat_oip nat_oip6.in4
187 #define nat_inport nat_un.nat_unt.ts_sport
188 #define nat_outport nat_un.nat_unt.ts_dport
189 /*
190 * Values for nat_dir
191 */
192 #define NAT_INBOUND 0
193 #define NAT_OUTBOUND 1
194 /*
195 * Definitions for nat_flags
196 */
197 #define NAT_TCP 0x0001 /* IPN_TCP */
198
199 EXAMPLES
200 The following example shows how to prepare and use SIOCSTPUT to insert a
201 NAT session directly into the table. Note that the usual TCP/IP code is
202 omitted is this example.
203
204 In the code segment below, incoming_fd is the TCP connection file
205 descriptor that is accepted as part of the redirect process, while
206 remote_fd is the outgoing TCP connection to the remote server being
207 translated back to the original IP address/port pair.
208
209 Note -- The following ipnat headers must be included before you can use
210 the code shown in this example:
211
212 #include <netinet/in.h>
213 #include <arpa/inet.h>
214 #include <net/if.h>
215 #include <netinet/ipl.h>
216 #include <netinet/ip_compat.h>
217 #include <netinet/ip_fil.h>
218 #include <netinet/ip_nat.h>
219 #include <string.h>
220 #include <fcntl.h>
221
222 Note -- In the example below, various code fragments have been excluded
223 to enhance clarity.
224
225 int
226 translate_connection(int incoming_fd)
227 {
228 struct sockaddr_in usin;
229 struct natlookup nlp;
230 struct nat_save ns;
231 struct ipfobj obj;
232 struct nat *nat;
233 int remote_fd;
234 int nat_fd;
235 int onoff;
236
237 memset(&ns, 0, sizeof(ns));
238 nat = &ns.ipn_nat
239
240 namelen = sizeof(usin);
241 getsockname(remote_fd, (struct sockaddr *)&usin, &namelen);
242
243 namelen = sizeof(sin);
244 getpeername(incoming_fd, (struct sockaddr *) &sin, &namelen);
245
246 namelen = sizeof(sloc);
247 getsockname(incoming_fd, (struct sockaddr *) &sloc, &namelen);
248
249 bzero((char *) &obi, sizeof(obj));
250 obj.ipfo_rev = IPFILTER_VERSION;
251 obj.ipfo_size = sizeof(nlp);
252 obj.ipfo_ptr = &nip;
253 obj.ipfo_type = IPFOBJ_NATLOOKUP;
254
255 /*
256 * Build up the NAT natlookup structure.
257 */
258 bzero((char *) &nlp, sizeof(nlp));
259 nlp.nl_outip = sin.sin_addr;
260 nlp.nl_inip = sloc.sin_addr;
261 nlp.nl_flags = IPN_TCP;
262 nlp.nl_outport = ntohs(sin.sin_port);
263 nlp.nl_inport = ntohs(sloc.sin_port);
264
265 /*
266 * Open the NAT device and lookup the mapping pair.
267 */
268 nat_fd = open(IPNAT_NAME, O_RDWR);
269 if (ioctl(nat_fd, SIOCGNATL, &obj) != 0)
270 return -1;
271
272 nat->nat_inip = usin.sin_addr;
273 nat->nat_outip = nlp.nl_outip;
274 nat->nat_oip = nlp.nl_realip;
275
276 sum1 = LONG_SUM(ntohl(usin.sin_addr.s_addr)) +
277 ntohs(usin.sin_port);
278 sum2 = LONG_SUM(ntohl(nat->nat_outip.s_addr)) +
279 ntohs(nlp.nl_outport);
280 CALC_SUMD(sum1, sum2, sumd);
281 nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
282 nat->nat_sumd[1] = nat->nat_sumd[0];
283
284 sum1 = LONG_SUM(ntohl(usin.sin_addr.s_addr));
285 sum2 = LONG_SUM(ntohl(nat->nat_outip.s_addr));
286 CALC_SUMD(sum1, sum2, sumd);
287 nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16);
288
289 nat->nat_inport = usin.sin_port;
290 nat->nat_outport = nlp.nl_outport;
291 nat->nat_oport = nlp.nl_realport;
292
293 nat->nat_flags = IPN_TCPUDP;
294
295 /*
296 * Prepare the ipfobj structure, accordingly.
297 */
298 bzero((char *)&obi, sizeof(obj));
299 obj.ipfo_rev = IPFILTER_VERSION;
300 obj.ipfo_size = sizeof(*nsp);
301 obj.ipfo_ptr = nsp;
302 obj.ipfo_type = IPFOBJ_NATSAVE;
303
304 onoff = 1;
305 if (ioctl(nat_fd, SIOCSTPUT, &obj) != 0)
306 fprintf(stderr, "Error occurred\n");
307
308 return connect(rem_fd, (struct sockaddr)&usin, sizeof(usin));
309 }
310
311 ERRORS
312 EPERM The device has been opened for reading only. To
313 succeed, the ioctl call must be opened for both
314 reading and writing. The call may be returned if it
315 is privileged and the calling process did not assert
316 {PRIV_SYS_NET_CONFIG} in the effective set.
317
318 ENOMEM More memory was allocated than the kernel can provide.
319 The call may also be returned if the application
320 inserts a NAT entry that exceeds the hash bucket
321 chain's maximum length.
322
323 EFAULT The calling process specified an invalid pointer in
324 the ipfobj structure.
325
326 EINVAL The calling process detected a parameter or field set
327 to an unacceptable value.
328
329 EEXIST The calling process, via SIOCSTPUT, attempted to add a
330 NAT entry that already exists in the NAT table.
331
332 ESRCH The calling process called SIOCSTPUT before setting
333 the SI_NEWFR flag and providing a pointer in the
334 nat_fr field that cannot be found in the current rule
335 set.
336
337 EACCES The calling process issued a SIOCSTPUT before issuing
338 a SIOCSTLCK.
339
340 INTERFACE STABILITY
341 Committed
342
343 SEE ALSO
344 ipfs(1M), ipnat(1M), ioctl(2), attributes(5)
345
346 illumos February 17, 2020 illumos