1#include <stdio.h>
2#include <stdlib.h>
3#include <unistd.h>
4#include <time.h>
5#include <string.h>
6#include <arpa/inet.h>
7
8#ifdef USE_NFCT
9#include <libmnl/libmnl.h>
10#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
11
12#include <linux/netfilter/nf_conntrack_tcp.h>
13
14struct data_cb_s
15{
16	struct sockaddr_storage * ext;
17	uint8_t found;
18};
19
20static int data_cb(const struct nlmsghdr *nlh, void *data)
21{
22	struct nf_conntrack *ct;
23	struct data_cb_s * d = (struct data_cb_s*) data;
24	struct sockaddr_in* ext4 = (struct sockaddr_in*) d->ext;
25
26	ct = nfct_new();
27	if (ct == NULL)
28		return MNL_CB_OK;
29	nfct_nlmsg_parse(nlh, ct);
30
31	if (data) {
32		ext4->sin_addr.s_addr = nfct_get_attr_u32(ct, ATTR_REPL_IPV4_DST);
33		ext4->sin_port =        nfct_get_attr_u16(ct, ATTR_REPL_PORT_DST);
34	}
35	d->found = 1;
36	nfct_destroy(ct);
37
38	return MNL_CB_OK;
39}
40
41int get_nat_ext_addr(struct sockaddr* src, struct sockaddr *dst, uint8_t proto,
42                     struct sockaddr_storage* ret_ext)
43{
44	struct mnl_socket *nl;
45	struct nlmsghdr *nlh;
46	struct nfgenmsg *nfh;
47	char buf[MNL_SOCKET_BUFFER_SIZE];
48	unsigned int seq, portid;
49	struct nf_conntrack *ct;
50	int ret;
51	struct data_cb_s data;
52
53	if ((!src)&&(!dst)) {
54		return 0;
55	}
56
57	if (src->sa_family != dst->sa_family) {
58		return 0;
59	}
60
61	nl = mnl_socket_open(NETLINK_NETFILTER);
62	if (nl == NULL) {
63//		perror("mnl_socket_open");
64		goto free_nl;
65	}
66
67	if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
68//		perror("mnl_socket_bind");
69		goto free_nl;
70	}
71	portid = mnl_socket_get_portid(nl);
72
73	memset(buf, 0, sizeof(buf));
74	nlh = mnl_nlmsg_put_header(buf);
75	nlh->nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_GET;
76	nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK;
77	nlh->nlmsg_seq = seq = time(NULL);
78
79	nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
80	nfh->nfgen_family = src->sa_family;
81	nfh->version = NFNETLINK_V0;
82	nfh->res_id = 0;
83
84	ct = nfct_new();
85	if (ct == NULL) {
86		goto free_nl;
87	}
88
89	nfct_set_attr_u8(ct, ATTR_L3PROTO, src->sa_family);
90	if (src->sa_family == AF_INET) {
91		struct sockaddr_in *src4 = (struct sockaddr_in *)src;
92		struct sockaddr_in *dst4 = (struct sockaddr_in *)dst;
93		nfct_set_attr_u32(ct, ATTR_IPV4_SRC, src4->sin_addr.s_addr);
94		nfct_set_attr_u32(ct, ATTR_IPV4_DST, dst4->sin_addr.s_addr);
95		nfct_set_attr_u16(ct, ATTR_PORT_SRC, src4->sin_port);
96		nfct_set_attr_u16(ct, ATTR_PORT_DST, dst4->sin_port);
97	} else if (src->sa_family == AF_INET6) {
98		struct sockaddr_in6 *src6 = (struct sockaddr_in6 *)src;
99		struct sockaddr_in6 *dst6 = (struct sockaddr_in6 *)dst;
100		nfct_set_attr(ct, ATTR_IPV6_SRC, &src6->sin6_addr);
101		nfct_set_attr(ct, ATTR_IPV6_DST, &dst6->sin6_addr);
102		nfct_set_attr_u16(ct, ATTR_PORT_SRC, src6->sin6_port);
103		nfct_set_attr_u16(ct, ATTR_PORT_DST, dst6->sin6_port);
104	}
105	nfct_set_attr_u8(ct, ATTR_L4PROTO, proto);
106
107	nfct_nlmsg_build(nlh, ct);
108
109	ret = mnl_socket_sendto(nl, nlh, nlh->nlmsg_len);
110	if (ret == -1) {
111		goto free_ct;
112	}
113
114	ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
115	data.ext = ret_ext;
116	data.found = 0;
117	while (ret > 0) {
118		ret = mnl_cb_run(buf, ret, seq, portid, data_cb, &data);
119		if (ret <= MNL_CB_STOP)
120			break;
121		ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
122	}
123
124free_ct:
125    nfct_destroy(ct);
126free_nl:
127    mnl_socket_close(nl);
128
129	return data.found;
130}
131
132#else
133#define DST "dst="
134#define DST_PORT "dport="
135#define SRC "src="
136#define SRC_PORT "sport="
137#define IP_CONNTRACK_LOCATION	"/proc/net/ip_conntrack"
138#define NF_CONNTRACK_LOCATION	"/proc/net/nf_conntrack"
139
140int get_nat_ext_addr(struct sockaddr* src, struct sockaddr *dst, uint8_t proto,
141                     struct sockaddr_storage* ret_ext)
142{
143	FILE *f;
144	int af;
145
146	if (!src)
147		return -2;
148
149	af = src->sa_family;
150
151	if ((f = fopen(NF_CONNTRACK_LOCATION, "r")) == NULL) {
152		if ((f = fopen(IP_CONNTRACK_LOCATION, "r")) == NULL) {
153			printf("could not read info about connections from the kernel, "
154				"make sure netfilter is enabled in kernel or by modules.\n");
155			return -1;
156		}
157	}
158
159	while (!feof(f)) {
160		char line[256], *str;
161		memset(line, 0, sizeof(line));
162		str = fgets(line, sizeof(line), f);
163		if (line[0] != 0) {
164			char *token, *saveptr;
165			int j;
166			uint8_t src_f, src_port_f, dst_f, dst_port_f;
167			src_f=src_port_f=dst_f=dst_port_f=0;
168
169			for (j = 1; ; j++, str = NULL) {
170				token = strtok_r(str, " ", &saveptr);
171				if (token == NULL)
172					break;
173
174				if ((j==2)&&(af!=atoi(token)))
175					break;
176				if ((j==4)&&(proto!=atoi(token)))
177					break;
178				if (j<=4)
179					continue;
180
181				if (strncmp(token, SRC, sizeof(SRC) - 1) == 0) {
182					char *srcip = token + sizeof(SRC) - 1;
183					uint32_t buf[4];
184					memset(buf,0,sizeof(buf));
185
186					if (inet_pton(af, srcip, buf)!=1)
187						break;
188
189					if (af==AF_INET) {
190						struct sockaddr_in *src4=(struct sockaddr_in*)src;
191						if (!src_f)  {
192							if (src4->sin_addr.s_addr != buf[0])
193								break;
194							src_f = 1;
195						}
196					}
197				}
198				if (strncmp(token, SRC_PORT, sizeof(SRC_PORT) - 1) == 0) {
199					char *src_port = token + sizeof(SRC_PORT) - 1;
200					uint16_t port=atoi(src_port);
201
202					if (af==AF_INET) {
203						struct sockaddr_in *src4=(struct sockaddr_in*)src;
204						if (!src_port_f)  {
205							if (ntohs(src4->sin_port) != port)
206								break;
207							src_port_f = 1;
208						}
209					}
210				}
211
212				if (strncmp(token, DST, sizeof(DST) - 1) == 0) {
213					char *dstip = token + sizeof(DST) - 1;
214					uint32_t buf[4];
215					memset(buf,0,sizeof(buf));
216					if (inet_pton(af, dstip, buf)!=1)
217						break;
218					if (af==AF_INET) {
219						struct sockaddr_in *dst4=(struct sockaddr_in*)dst;
220						if (!dst_f)  {
221							if (dst4->sin_addr.s_addr != buf[0])
222								break;
223							dst_f = 1;
224						} else {
225							struct sockaddr_in*ret4=(struct sockaddr_in*)ret_ext;
226							ret_ext->ss_family = AF_INET;
227							ret4->sin_addr.s_addr = buf[0];
228						}
229					}
230				}
231				if (strncmp(token, DST_PORT, sizeof(DST_PORT)-1) == 0) {
232					char *dst_port = token + sizeof(DST_PORT) - 1;
233					uint16_t port=atoi(dst_port);
234					if (af==AF_INET) {
235						struct sockaddr_in *dst4=(struct sockaddr_in*)dst;
236						if (!dst_port_f)  {
237							if (ntohs(dst4->sin_port) != port)
238								break;
239							dst_port_f = 1;
240						} else {
241							struct sockaddr_in*ret4=(struct sockaddr_in*)ret_ext;
242							ret_ext->ss_family = AF_INET;
243							ret4->sin_port = htons(port);
244						}
245					}
246				}
247			}
248			if (src_f && src_port_f && dst_f && dst_port_f) {
249				fclose(f);
250				return 1;
251			}
252		}
253	}
254	fclose(f);
255
256	return 0;
257}
258#endif
259