| IPNAT(7I) | Ioctl Requests | IPNAT(7I) |
ipnat —
IPFOBJ_value. IPFOBJ_value
provides a matching name for the structure, while
ipfo_size is set to the total size of the structure
being passed and ipfo_ptr is set to the structure
address. The ipfo_rev structure should be set to the
current value of IPFILTER_VERSION, while
ipfo_offset and ipfo_xxxpad should
be set to 0.
/*
* Structure used with SIOCGNATL/SIOCSTPUT.
*/
/*
* Object structure description. For passing through in ioctls.
*/
typedef struct ipfobj {
u_32_t ipfo_rev; /* IPFilter version (IPFILTER_VERSION) */
u_32_t ipfo_size; /* size of object at ipfo_ptr */
void *ipfo_ptr; /* pointer to object */
int ipfo_type; /* type of object being pointed to */
int ipfo_offset; /* bytes from ipfo_ptr where to start */
u_char ipfo_xxxpad[32]; /* reserved for future use */
} ipfobj_t;
#define IPFILTER_VERSION 4010901 /* IPFilter version */
#define IPFOBJ_NATSAVE 8 /* struct nat_save */
#define IPFOBJ_NATLOOKUP 9 /* struct natlookup */
The following ioctl(2) calls may be used to
manipulate the ipnat sub-system inside of ipf. Note that the ipnat driver
only accept calls from applications using the same data model as the kernel.
In other words, 64-bit kernels can only accept calls from 64-bit
applications. Calls from 32-bit applications fail with
EINVAL.
SIOCSTLCKSIOCGNATLIPN_TCP option set. All other
fields must be set to 0. If the call succeeds,
nl_realip and nl_realport are
set to the real destination address and port, respectively. The
nl_inport and nl_outport
fields must be in host byte order. If
IPN_FINDFORWARD is set in
nl_flags, a check is made to see if it is possible
to create an outgoing NAT session by checking if a packet coming from
(nl_realip, nl_realport) and
destined for (nl_outip,
nl_outport) can be translated. If translation is
possible, the flag remains set, otherwise it is cleared in the structure
returned to the caller.
/*
* Structure used with SIOCGNATL.
*/
typedef struct natlookup {
i6addr_t nl_inipaddr;
i6addr_t nl_outipaddr;
i6addr_t nl_realipaddr;
int nl_v;
int nl_flags;
u_short nl_inport;
u_short nl_outport;
u_short nl_realport;
} natlookup_t
#define nl_inip nl_inipaddr.in4
#define nl_outip nl_outipaddr.in4
#define nl_realip nl_realipaddr.in4
#define nl_inip6 nl_inipaddr.in6
#define nl_outip6 nl_outipaddr.in6
#define nl_realip6 nl_realipaddr.in6
/*
* Accepted values for nl_flags
*/
#define IPN_TCP 0x00001
#define IPN_FINDFORWARD 0x400000
SIOCSTPUTTo create a translation, the following fields must be set:
The caller must also precalculate the checksum adjustments necessary to complete the translation and store those values in nat_sumd (delta required for TCP header) and nat_ipsumd (delta required for IP header).
/*
* Structures used with SIOCSTPUT.
*/
typedef struct nat_save {
void *ipn_next;
struct nat ipn_nat;
struct ipnat ipn_ipnat;
struct frentry ipn_fr;
int ipn_dsize;
char ipn_data[4];
} nat_save_t;
typedef struct nat {
ipfmutex_t nat_lock;
struct nat *nat_next;
struct nat **nat_pnext;
struct nat *nat_hnext[2];
struct nat **nat_phnext[2];
struct hostmap *nat_hm;
void *nat_data;
struct nat **nat_me;
struct ipstate *nat_state;
struct ap_session *nat_aps;
frentry_t *nat_fr;
struct ipnat *nat_ptr;
void *nat_ifps[2];
void *nat_sync;
ipftqent_t nat_tqe;
u_32_t nat_flags;
u_32_t nat_sumd[2];
u_32_t nat_ipsumd;
u_32_t nat_mssclamp;
i6addr_t nat_inip6;
i6addr_t nat_outip6;
i6addr_t nat_oip6;
U_QUAD_T nat_pkts[2];
U_QUAD_T nat_bytes[2];
union {
udpinfo_t nat_unu;
tcpinfo_t nat_unt;
icmpinfo_t nat_uni;
greinfo_t nat_ugre;
} nat_un;
u_short nat_oport;
u_short nat_use;
u_char nat_p;
int nat_dir;
int nat_ref;
int nat_hv[2];
char nat_ifnames[2][LIFNAMSIZ];
int nat_rev;
int nat_v;
} nat_t;
#define nat_inip nat_inip6.in4
#define nat_outip nat_outip6.in4
#define nat_oip nat_oip6.in4
#define nat_inport nat_un.nat_unt.ts_sport
#define nat_outport nat_un.nat_unt.ts_dport
/*
* Values for nat_dir
*/
#define NAT_INBOUND 0
#define NAT_OUTBOUND 1
/*
* Definitions for nat_flags
*/
#define NAT_TCP 0x0001 /* IPN_TCP */
In the code segment below, incoming_fd is the TCP connection file descriptor that is accepted as part of the redirect process, while remote_fd is the outgoing TCP connection to the remote server being translated back to the original IP address/port pair.
Note — The following ipnat headers must be included before you can use the code shown in this example:
#include <netinet/in.h> #include <arpa/inet.h> #include <net/if.h> #include <netinet/ipl.h> #include <netinet/ip_compat.h> #include <netinet/ip_fil.h> #include <netinet/ip_nat.h> #include <string.h> #include <fcntl.h>
Note — In the example below, various code fragments have been excluded to enhance clarity.
int
translate_connection(int incoming_fd)
{
struct sockaddr_in usin;
struct natlookup nlp;
struct nat_save ns;
struct ipfobj obj;
struct nat *nat;
int remote_fd;
int nat_fd;
int onoff;
memset(&ns, 0, sizeof(ns));
nat = &ns.ipn_nat
namelen = sizeof(usin);
getsockname(remote_fd, (struct sockaddr *)&usin, &namelen);
namelen = sizeof(sin);
getpeername(incoming_fd, (struct sockaddr *) &sin, &namelen);
namelen = sizeof(sloc);
getsockname(incoming_fd, (struct sockaddr *) &sloc, &namelen);
bzero((char *) &obi, sizeof(obj));
obj.ipfo_rev = IPFILTER_VERSION;
obj.ipfo_size = sizeof(nlp);
obj.ipfo_ptr = &nip;
obj.ipfo_type = IPFOBJ_NATLOOKUP;
/*
* Build up the NAT natlookup structure.
*/
bzero((char *) &nlp, sizeof(nlp));
nlp.nl_outip = sin.sin_addr;
nlp.nl_inip = sloc.sin_addr;
nlp.nl_flags = IPN_TCP;
nlp.nl_outport = ntohs(sin.sin_port);
nlp.nl_inport = ntohs(sloc.sin_port);
/*
* Open the NAT device and lookup the mapping pair.
*/
nat_fd = open(IPNAT_NAME, O_RDWR);
if (ioctl(nat_fd, SIOCGNATL, &obj) != 0)
return -1;
nat->nat_inip = usin.sin_addr;
nat->nat_outip = nlp.nl_outip;
nat->nat_oip = nlp.nl_realip;
sum1 = LONG_SUM(ntohl(usin.sin_addr.s_addr)) +
ntohs(usin.sin_port);
sum2 = LONG_SUM(ntohl(nat->nat_outip.s_addr)) +
ntohs(nlp.nl_outport);
CALC_SUMD(sum1, sum2, sumd);
nat->nat_sumd[0] = (sumd & 0xffff) + (sumd >> 16);
nat->nat_sumd[1] = nat->nat_sumd[0];
sum1 = LONG_SUM(ntohl(usin.sin_addr.s_addr));
sum2 = LONG_SUM(ntohl(nat->nat_outip.s_addr));
CALC_SUMD(sum1, sum2, sumd);
nat->nat_ipsumd = (sumd & 0xffff) + (sumd >> 16);
nat->nat_inport = usin.sin_port;
nat->nat_outport = nlp.nl_outport;
nat->nat_oport = nlp.nl_realport;
nat->nat_flags = IPN_TCPUDP;
/*
* Prepare the ipfobj structure, accordingly.
*/
bzero((char *)&obi, sizeof(obj));
obj.ipfo_rev = IPFILTER_VERSION;
obj.ipfo_size = sizeof(*nsp);
obj.ipfo_ptr = nsp;
obj.ipfo_type = IPFOBJ_NATSAVE;
onoff = 1;
if (ioctl(nat_fd, SIOCSTPUT, &obj) != 0)
fprintf(stderr, "Error occurred\n");
return connect(rem_fd, (struct sockaddr)&usin, sizeof(usin));
}
EPERMENOMEMEFAULTEINVALEEXISTSIOCSTPUT, attempted to
add a NAT entry that already exists in the NAT table.ESRCHSIOCSTPUT before
setting the SI_NEWFR flag and providing a pointer
in the nat_fr field that cannot be found in the
current rule set.EACESSSIOCSTPUT before
issuing a SIOCSTLCK.| October 23, 2017 | illumos |