1/*
2 * Network configuration layer (Linux)
3 *
4 * Copyright (C) 2014, Broadcom Corporation. All Rights Reserved.
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
15 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 *
18 * $Id: netconf_linux.c 358181 2012-09-21 13:59:23Z $
19 */
20
21#include <stdio.h>
22#include <stdlib.h>
23#include <ctype.h>
24#include <errno.h>
25#include <error.h>
26#include <string.h>
27#include <time.h>
28#include <unistd.h>
29#include <syslog.h>
30#include <sys/ioctl.h>
31#include <sys/types.h>
32#include <sys/socket.h>
33#include <net/if.h>
34#include <netinet/in.h>
35#include <arpa/inet.h>
36#include <net/if_arp.h>
37
38#include <typedefs.h>
39#include <proto/ethernet.h>
40#include <netconf.h>
41#include <netconf_linux.h>
42
43#ifndef LINUX_2_6_36
44typedef struct ipt_time_info		time_info_t;
45#define TIME_INFO_EXTRA_BYTES		8
46#else
47typedef struct xt_time_info		time_info_t;
48#define TIME_INFO_EXTRA_BYTES		0
49#define IPT_ALIGN			XT_ALIGN
50#endif
51
52/* Loops over each match in the ipt_entry */
53#define for_each_ipt_match(match, entry) \
54	for ((match) = (struct ipt_entry_match *) &(entry)->elems[0]; \
55	     (int) (match) < (int) (entry) + (entry)->target_offset; \
56	     (match) = (struct ipt_entry_match *) ((int) (match) + (match)->u.match_size))
57
58/* Supported ipt table names */
59static const char *ipt_table_names[] = { "filter", "nat", NULL };
60
61/* ipt table name appropriate for target (indexed by netconf_fw_t.target) */
62static const char * ipt_table_name[] = {
63	"filter", "filter", "filter", "filter",
64	"nat", "nat", "nat", "nat"
65};
66
67/* ipt target name (indexed by netconf_fw_t.target) */
68static const char * ipt_target_name[] = {
69	"DROP", "ACCEPT", "logdrop", "logaccept",
70	"SNAT", "DNAT", "MASQUERADE", "autofw"
71};
72
73/* ipt target data size (indexed by netconf_fw_t.target) */
74#ifndef LINUX_2_6_36
75static const size_t ipt_target_size[] = {
76	sizeof(int), sizeof(int), sizeof(int), sizeof(int),
77	sizeof(struct ip_nat_multi_range), sizeof(struct ip_nat_multi_range), sizeof(struct ip_nat_multi_range), sizeof(struct ip_autofw_info)
78};
79#else /* linux-2.6.36 */
80/* ipt target data size (indexed by netconf_fw_t.target) */
81static const size_t ipt_target_size[] = {
82	sizeof(int), sizeof(int), sizeof(int), sizeof(int),
83	sizeof(struct nf_nat_multi_range), sizeof(struct nf_nat_multi_range), sizeof(struct nf_nat_multi_range), sizeof(struct ip_autofw_info)
84};
85#endif /* linux-2.6.36 */
86
87/* ipt filter chain name appropriate for direction (indexed by netconf_filter_t.dir) */
88static const char * ipt_filter_chain_name[] = {
89	"INPUT", "FORWARD", "OUTPUT"
90};
91
92/* ipt nat chain name appropriate for target (indexed by netconf_nat_t.target) */
93static const char * ipt_nat_chain_name[] = {
94	NULL, NULL, NULL, NULL,
95	"POSTROUTING", "PREROUTING", "POSTROUTING"
96};
97
98/* Returns a netconf_dir index */
99static int
100filter_dir(const char *name)
101{
102	if (strncmp(name, "INPUT", IPT_FUNCTION_MAXNAMELEN) == 0)
103		return NETCONF_IN;
104	else if (strncmp(name, "FORWARD", IPT_FUNCTION_MAXNAMELEN) == 0)
105		return NETCONF_FORWARD;
106	else if (strncmp(name, "OUTPUT", IPT_FUNCTION_MAXNAMELEN) == 0)
107		return NETCONF_OUT;
108	else
109		return -1;
110}
111
112/* Returns a netconf_target index */
113static int
114#ifndef LINUX_2_6_36
115target_num(const struct ipt_entry *entry, iptc_handle_t *handle)
116#else /* linux-2.6.36 */
117target_num(const struct ipt_entry *entry, struct iptc_handle *handle)
118#endif /* linux-2.6.36 */
119{
120	const char *name = iptc_get_target(entry, handle);
121
122	if (!name)
123		return -1;
124
125	if (strncmp(name, "DROP", IPT_FUNCTION_MAXNAMELEN) == 0)
126		return NETCONF_DROP;
127	else if (strncmp(name, "ACCEPT", IPT_FUNCTION_MAXNAMELEN) == 0)
128		return NETCONF_ACCEPT;
129	else if (strncmp(name, "logdrop", IPT_FUNCTION_MAXNAMELEN) == 0)
130		return NETCONF_LOG_DROP;
131	else if (strncmp(name, "logaccept", IPT_FUNCTION_MAXNAMELEN) == 0)
132		return NETCONF_LOG_ACCEPT;
133	else if (strncmp(name, "SNAT", IPT_FUNCTION_MAXNAMELEN) == 0)
134		return NETCONF_SNAT;
135	else if (strncmp(name, "DNAT", IPT_FUNCTION_MAXNAMELEN) == 0)
136		return NETCONF_DNAT;
137	else if (strncmp(name, "MASQUERADE", IPT_FUNCTION_MAXNAMELEN) == 0)
138		return NETCONF_MASQ;
139	else if (strncmp(name, "autofw", IPT_FUNCTION_MAXNAMELEN) == 0)
140		return NETCONF_APP;
141	else
142		return -1;
143}
144
145#ifdef LINUX_2_6_36
146/* User: match.day, SUN/0~MON/1 ~ SAT/6 */
147/* Kernel: MON/1 ~ SAT/6~SUN/7 */
148/* Need special handle for Sunday */
149/* Be aware: we steal the flags bit 1 ~ 7 to store the begin day */
150static void
151get_days(unsigned int *days, time_info_t *time)
152{
153	int i, j;
154	char weekdays_map[7] = {0};
155
156	/* Translate from Kernel to User */
157	for (i = 1; i <= 7; i++) {
158		if (time->weekdays_match & (1 << i)) {
159			if (i == 7)
160				weekdays_map[0] = 1;
161			else
162				weekdays_map[i] = 1;
163		}
164	}
165
166	/* Begin day */
167	for (i = 1; i <= 7; i++) {
168		if (time->flags & (1 << i)) {
169			if (i == 7)
170				days[0] = 0;
171			else
172				days[0] = i;
173			break;
174		}
175	}
176
177	/* End day */
178	for (i = days[0], j = 0; j < 7; i = (i + 1) % 7, j++) {
179		if (weekdays_map[i])
180			days[1] = i;
181		else
182			break;
183	}
184}
185
186/* User: match.day, SUN/0~MON/1 ~ SAT/6 */
187/* Kernel: MON/1 ~ SAT/6~SUN/7 */
188/* Need special handle for Sunday */
189/* Be aware: we steal the flags bit 1 ~ 7 to store the begin day */
190static void
191set_days(unsigned int *days, time_info_t *time)
192{
193	int i;
194
195	for (i = days[0]; i != days[1]; i = (i + 1) % 7) {
196		if (!i)
197			time->weekdays_match |= (1 << 7);
198		else
199			time->weekdays_match |= (1 << i);
200	}
201
202	if (!days[1])
203		time->weekdays_match |= (1 << 7);
204	else
205		time->weekdays_match |= (1 << days[1]);
206
207	/* Use local time */
208	time->flags = XT_TIME_LOCAL_TZ;
209
210	/* Steal flags to store the begin day */
211	if (days[0] == 0)
212		time->flags |= (1 << 7);
213	else
214		time->flags |= (1 << days[0]);
215}
216#endif /* LINUX_2_6_36 */
217
218/*
219 * Get a list of the current firewall entries
220 * @param	fw_list	list of firewall entries
221 * @return	0 on success and errno on failure
222 */
223int
224netconf_get_fw(netconf_fw_t *fw_list)
225{
226	const char **table;
227	const char *chain;
228	const struct ipt_entry *entry;
229#ifndef LINUX_2_6_36
230	iptc_handle_t handle = NULL;
231#else /* linux-2.6.36 */
232	struct iptc_handle *handle = NULL;
233#endif /* linux-2.6.36 */
234
235	/* Initialize list */
236	netconf_list_init(fw_list);
237
238	/* Search all default tables */
239	for (table = &ipt_table_names[0]; *table; table++) {
240
241		if (strcmp(*table, "filter") && strcmp(*table, "nat"))
242			continue;
243
244		if (!(handle = iptc_init(*table))) {
245			fprintf(stderr, "%s\n", iptc_strerror(errno));
246			goto err;
247		}
248
249		/* Search all default chains */
250#ifndef LINUX_2_6_36
251		for (chain = iptc_first_chain(&handle); chain; chain = iptc_next_chain(&handle)) {
252#else /* linux-2.6.36 */
253		for (chain = iptc_first_chain(handle); chain; chain = iptc_next_chain(handle)) {
254#endif /* linux-2.6.36 */
255			if (strcmp(chain, "INPUT") && strcmp(chain, "FORWARD") && strcmp(chain, "OUTPUT") &&
256			    strcmp(chain, "PREROUTING") && strcmp(chain, "POSTROUTING"))
257				continue;
258
259			/* Search all entries */
260#ifndef LINUX_2_6_36
261			for (entry = iptc_first_rule(chain, &handle); entry; entry = iptc_next_rule(entry, &handle)) {
262				int num = target_num(entry, &handle);
263#else /* linux-2.6.36 */
264			for (entry = iptc_first_rule(chain, handle); entry; entry = iptc_next_rule(entry, handle)) {
265				int num = target_num(entry, handle);
266#endif /* linux-2.6.36 */
267				netconf_fw_t *fw = NULL;
268				netconf_filter_t *filter = NULL;
269				netconf_nat_t *nat = NULL;
270				netconf_app_t *app = NULL;
271
272				const struct ipt_entry_match *match;
273				const struct ipt_entry_target *target;
274				struct ipt_mac_info *mac = NULL;
275				struct ipt_state_info *state = NULL;
276				time_info_t *time = NULL;
277
278				/* Only know about TCP/UDP */
279				if (!netconf_valid_ipproto(entry->ip.proto))
280					continue;
281
282				/* Only know about target types in the specified tables */
283				if (!netconf_valid_target(num) ||
284				    strncmp(ipt_table_name[num], *table, IPT_FUNCTION_MAXNAMELEN) != 0)
285					continue;
286
287				/* Only know about specified target types */
288				if (netconf_valid_filter(num)) {
289					filter = calloc(1, sizeof(netconf_filter_t));
290					fw = (netconf_fw_t *) filter;
291				}
292				else if (netconf_valid_nat(num)) {
293					nat = calloc(1, sizeof(netconf_nat_t));
294					fw = (netconf_fw_t *) nat;
295				}
296				else if (num == NETCONF_APP) {
297					app = calloc(1, sizeof(netconf_app_t));
298					fw = (netconf_fw_t *) app;
299				}
300				else
301					continue;
302
303				if (!fw) {
304					perror("calloc");
305					goto err;
306				}
307				netconf_list_add(fw, fw_list);
308
309				/* Get IP addresses */
310				fw->match.src.ipaddr.s_addr = entry->ip.src.s_addr;
311				fw->match.src.netmask.s_addr = entry->ip.smsk.s_addr;
312				fw->match.dst.ipaddr.s_addr = entry->ip.dst.s_addr;
313				fw->match.dst.netmask.s_addr = entry->ip.dmsk.s_addr;
314				fw->match.flags |= (entry->ip.invflags & IPT_INV_SRCIP) ? NETCONF_INV_SRCIP : 0;
315				fw->match.flags |= (entry->ip.invflags & IPT_INV_DSTIP) ? NETCONF_INV_DSTIP : 0;
316
317				/* Get interface names */
318				strncpy(fw->match.in.name, entry->ip.iniface, IFNAMSIZ);
319				strncpy(fw->match.out.name, entry->ip.outiface, IFNAMSIZ);
320				fw->match.flags |= (entry->ip.invflags & IPT_INV_VIA_IN) ? NETCONF_INV_IN : 0;
321				fw->match.flags |= (entry->ip.invflags & IPT_INV_VIA_OUT) ? NETCONF_INV_OUT : 0;
322
323				/* Get TCP port(s) */
324				if (entry->ip.proto == IPPROTO_TCP) {
325					struct ipt_tcp *tcp = NULL;
326
327					for_each_ipt_match(match, entry) {
328						if (strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN) != 0)
329							continue;
330
331						tcp = (struct ipt_tcp *) &match->data[0];
332						break;
333					}
334
335					if (tcp) {
336						/* Match ports stored in host order for some stupid reason */
337						fw->match.ipproto = IPPROTO_TCP;
338						fw->match.src.ports[0] = htons(tcp->spts[0]);
339						fw->match.src.ports[1] = htons(tcp->spts[1]);
340						fw->match.dst.ports[0] = htons(tcp->dpts[0]);
341						fw->match.dst.ports[1] = htons(tcp->dpts[1]);
342						fw->match.flags |= (tcp->invflags & IPT_TCP_INV_SRCPT) ? NETCONF_INV_SRCPT : 0;
343						fw->match.flags |= (tcp->invflags & IPT_TCP_INV_DSTPT) ? NETCONF_INV_DSTPT : 0;
344					}
345				}
346
347				/* Get UDP port(s) */
348				else if (entry->ip.proto == IPPROTO_UDP) {
349					struct ipt_udp *udp = NULL;
350
351					for_each_ipt_match(match, entry) {
352						if (strncmp(match->u.user.name, "udp", IPT_FUNCTION_MAXNAMELEN) != 0)
353							continue;
354
355						udp = (struct ipt_udp *) &match->data[0];
356						break;
357					}
358
359					if (udp) {
360						/* Match ports stored in host order for some stupid reason */
361						fw->match.ipproto = IPPROTO_UDP;
362						fw->match.src.ports[0] = htons(udp->spts[0]);
363						fw->match.src.ports[1] = htons(udp->spts[1]);
364						fw->match.dst.ports[0] = htons(udp->dpts[0]);
365						fw->match.dst.ports[1] = htons(udp->dpts[1]);
366						fw->match.flags |= (udp->invflags & IPT_UDP_INV_SRCPT) ? NETCONF_INV_SRCPT : 0;
367						fw->match.flags |= (udp->invflags & IPT_UDP_INV_DSTPT) ? NETCONF_INV_DSTPT : 0;
368					}
369				}
370
371				/* Get source MAC address */
372				for_each_ipt_match(match, entry) {
373					if (strncmp(match->u.user.name, "mac", IPT_FUNCTION_MAXNAMELEN) != 0)
374						continue;
375
376					mac = (struct ipt_mac_info *) &match->data[0];
377					break;
378				}
379				if (mac) {
380					memcpy(fw->match.mac.octet, mac->srcaddr, ETHER_ADDR_LEN);
381					fw->match.flags |= mac->invert ? NETCONF_INV_MAC : 0;
382				}
383
384				/* Get packet state */
385				for_each_ipt_match(match, entry) {
386					if (strncmp(match->u.user.name, "state", IPT_FUNCTION_MAXNAMELEN) != 0)
387						continue;
388
389					state = (struct ipt_state_info *) &match->data[0];
390					break;
391				}
392				if (state) {
393					fw->match.state |= (state->statemask & IPT_STATE_INVALID) ? NETCONF_INVALID : 0;
394					fw->match.state |= (state->statemask & IPT_STATE_BIT(IP_CT_ESTABLISHED)) ? NETCONF_ESTABLISHED : 0;
395					fw->match.state |= (state->statemask & IPT_STATE_BIT(IP_CT_RELATED)) ? NETCONF_RELATED : 0;
396					fw->match.state |= (state->statemask & IPT_STATE_BIT(IP_CT_NEW)) ? NETCONF_NEW : 0;
397				}
398
399				/* Get local time */
400				for_each_ipt_match(match, entry) {
401					if (strncmp(match->u.user.name, "time", IPT_FUNCTION_MAXNAMELEN) != 0)
402						continue;
403
404					/* We added 8 bytes of day range at the end */
405					if (match->u.match_size < (IPT_ALIGN(sizeof(struct ipt_entry_match)) +
406								   IPT_ALIGN(sizeof(time_info_t) + TIME_INFO_EXTRA_BYTES)))
407						continue;
408
409					time = (time_info_t *) &match->data[0];
410					break;
411				}
412				if (time) {
413#ifndef LINUX_2_6_36
414					unsigned int *days = (unsigned int *) &time[1];
415
416					fw->match.days[0] = days[0];
417					fw->match.days[1] = days[1];
418					fw->match.secs[0] = time->time_start;
419					fw->match.secs[1] = time->time_stop;
420#else
421					/* Get days from weekdays_match and flags */
422					get_days(fw->match.days, time);
423					fw->match.secs[0] = time->daytime_start;
424					fw->match.secs[1] = time->daytime_stop;
425#endif
426				}
427
428				/* Set target type */
429				fw->target = num;
430				target = (struct ipt_entry_target *) ((int) entry + entry->target_offset);
431
432				/* Get filter target information */
433				if (filter) {
434					if (!netconf_valid_dir(filter->dir = filter_dir(chain)))
435						goto err;
436				}
437
438				/* Get NAT target information */
439				else if (nat) {
440#ifndef LINUX_2_6_36
441					struct ip_nat_multi_range *mr = (struct ip_nat_multi_range *) &target->data[0];
442					struct ip_nat_range *range = (struct ip_nat_range *) &mr->range[0];
443#else /* linux-2.6.36 */
444					struct nf_nat_multi_range *mr = (struct nf_nat_multi_range *) &target->data[0];
445					struct nf_nat_range *range = (struct nf_nat_range *) &mr->range[0];
446
447#endif /* linux-2.6.36 */
448					/* Get mapped IP address */
449					nat->ipaddr.s_addr = range->min_ip;
450
451					/* Get mapped TCP port(s) */
452					if (entry->ip.proto == IPPROTO_TCP) {
453						nat->ports[0] = range->min.tcp.port;
454						nat->ports[1] = range->max.tcp.port;
455					}
456
457					/* Get mapped UDP port(s) */
458					else if (entry->ip.proto == IPPROTO_UDP) {
459						nat->ports[0] = range->min.udp.port;
460						nat->ports[1] = range->max.udp.port;
461					}
462				}
463
464				/* Get application specific port forward information */
465				else if (app) {
466					struct ip_autofw_info *info = (struct ip_autofw_info *) &target->data[0];
467
468					app->proto = info->proto;
469					app->dport[0] = info->dport[0];
470					app->dport[1] = info->dport[1];
471					app->to[0] = info->to[0];
472					app->to[1] = info->to[1];
473				}
474			}
475		}
476
477#ifndef LINUX_2_6_36
478		if (!iptc_commit(&handle)) {
479#else /* linux-2.6.36 */
480		if (!iptc_commit(handle)) {
481#endif /* linux-2.6.36 */
482#ifdef LINUX_2_6_36
483			iptc_free(handle);
484#endif
485			fprintf(stderr, "%s\n", iptc_strerror(errno));
486			handle = NULL;
487			goto err;
488		}
489	}
490
491#ifdef LINUX_2_6_36
492	iptc_free(handle);
493#endif
494	return 0;
495
496 err:
497	if (handle)
498#ifndef LINUX_2_6_36
499		iptc_commit(&handle);
500#else /* linux-2.6.36 */
501		iptc_commit(handle);
502#endif /* linux-2.6.36 */
503#ifdef LINUX_2_6_36
504	if (handle)
505		iptc_free(handle);
506#endif
507	netconf_list_free(fw_list);
508	return errno;
509}
510
511/* Logical XOR */
512#define lxor(a, b) (((a) && !(b)) || (!(a) && (b)))
513
514/*
515 * Get the index of a firewall entry
516 * @param	fw	firewall entry to look for
517 * @return	index of firewall entry or <0 if not found or an error occurred
518 */
519static int
520netconf_fw_index(const netconf_fw_t *fw)
521{
522	const netconf_filter_t *filter = NULL;
523	const netconf_nat_t *nat = NULL;
524	const netconf_app_t *app = NULL;
525	const char **table;
526	const char *chain;
527	const struct ipt_entry *entry = NULL;
528#ifndef LINUX_2_6_36
529	iptc_handle_t handle = NULL;
530#else /* linux-2.6.36 */
531	struct iptc_handle *handle = NULL;
532#endif /* linux-2.6.36 */
533
534	int ret = 0;
535
536	if (!netconf_valid_ipproto(fw->match.ipproto)) {
537		fprintf(stderr, "invalid IP protocol %d\n", fw->match.ipproto);
538		return -EINVAL;
539	}
540
541	/* Only know about specified target types */
542	if (netconf_valid_filter(fw->target)) {
543		filter = (netconf_filter_t *) fw;
544		if (!netconf_valid_dir(filter->dir)) {
545			fprintf(stderr, "invalid filter direction %d\n", filter->dir);
546			return -EINVAL;
547		}
548	}
549	else if (netconf_valid_nat(fw->target))
550		nat = (netconf_nat_t *) fw;
551	else if (fw->target == NETCONF_APP)
552		app = (netconf_app_t *) fw;
553	else {
554		fprintf(stderr, "invalid target type %d\n", fw->target);
555		return -EINVAL;
556	}
557
558	/* Search all default tables */
559	for (table = &ipt_table_names[0]; *table; table++) {
560
561		/* Only consider specified tables */
562		if (strncmp(ipt_table_name[fw->target], *table, IPT_FUNCTION_MAXNAMELEN) != 0)
563			continue;
564
565		if (!(handle = iptc_init(*table))) {
566			fprintf(stderr, "%s\n", iptc_strerror(errno));
567			return -errno;
568		}
569
570		/* Search all default chains */
571#ifndef LINUX_2_6_36
572		for (chain = iptc_first_chain(&handle); chain; chain = iptc_next_chain(&handle)) {
573#else /* linux-2.6.36 */
574		for (chain = iptc_first_chain(handle); chain; chain = iptc_next_chain(handle)) {
575#endif /* linux-2.6.36 */
576
577			/* Only consider specified chains */
578			if (filter && strncmp(chain, ipt_filter_chain_name[filter->dir], sizeof(ipt_chainlabel)) != 0)
579				continue;
580			else if (nat && strncmp(chain, ipt_nat_chain_name[nat->target], sizeof(ipt_chainlabel)) != 0)
581				continue;
582			else if (app && strncmp(chain, "PREROUTING", sizeof(ipt_chainlabel)) != 0)
583				continue;
584
585			/* Search all entries */
586#ifndef LINUX_2_6_36
587			for (ret = 0, entry = iptc_first_rule(chain, &handle); entry; ret++, entry = iptc_next_rule(entry, &handle)) {
588#else /* linux-2.6.36 */
589			for (ret = 0, entry = iptc_first_rule(chain, handle); entry; ret++, entry = iptc_next_rule(entry, handle)) {
590#endif /* linux-2.6.36 */
591				const struct ipt_entry_match *match;
592				const struct ipt_entry_target *target;
593				struct ipt_mac_info *mac = NULL;
594				struct ipt_state_info *state = NULL;
595				time_info_t *time = NULL;
596
597				/* Only know about TCP/UDP */
598				if (entry->ip.proto != fw->match.ipproto)
599					continue;
600
601				/* Compare IP address(es) */
602				if (entry->ip.src.s_addr != fw->match.src.ipaddr.s_addr ||
603				    entry->ip.smsk.s_addr != fw->match.src.netmask.s_addr ||
604				    entry->ip.dst.s_addr != fw->match.dst.ipaddr.s_addr ||
605				    entry->ip.dmsk.s_addr != fw->match.dst.netmask.s_addr)
606					continue;
607
608				if (lxor(entry->ip.invflags & IPT_INV_SRCIP, fw->match.flags & NETCONF_INV_SRCIP) ||
609				    lxor(entry->ip.invflags & IPT_INV_DSTIP, fw->match.flags & NETCONF_INV_DSTIP))
610					continue;
611
612				/* Compare interface names */
613				if (strncmp(fw->match.in.name, entry->ip.iniface, IFNAMSIZ) != 0 ||
614				    strncmp(fw->match.out.name, entry->ip.outiface, IFNAMSIZ) != 0)
615					continue;
616
617				/* Compare TCP port(s) */
618				if (fw->match.ipproto == IPPROTO_TCP) {
619					struct ipt_tcp *tcp = NULL;
620
621					for_each_ipt_match(match, entry) {
622						if (strncmp(match->u.user.name, "tcp", IPT_FUNCTION_MAXNAMELEN) != 0)
623							continue;
624
625						tcp = (struct ipt_tcp *) &match->data[0];
626						break;
627					}
628
629					/* Match ports stored in host order for some stupid reason */
630					if (!tcp ||
631					    tcp->spts[0] != ntohs(fw->match.src.ports[0]) ||
632					    tcp->spts[1] != ntohs(fw->match.src.ports[1]) ||
633					    tcp->dpts[0] != ntohs(fw->match.dst.ports[0]) ||
634					    tcp->dpts[1] != ntohs(fw->match.dst.ports[1]))
635						continue;
636
637					if (lxor(tcp->invflags & IPT_TCP_INV_SRCPT, fw->match.flags & NETCONF_INV_SRCPT) ||
638					    lxor(tcp->invflags & IPT_TCP_INV_DSTPT, fw->match.flags & NETCONF_INV_DSTPT))
639						continue;
640				}
641
642				/* Compare UDP port(s) */
643				else if (fw->match.ipproto == IPPROTO_UDP) {
644					struct ipt_udp *udp = NULL;
645
646					for_each_ipt_match(match, entry) {
647						if (strncmp(match->u.user.name, "udp", IPT_FUNCTION_MAXNAMELEN) != 0)
648							continue;
649
650						udp = (struct ipt_udp *) &match->data[0];
651						break;
652					}
653
654					/* Match ports stored in host order for some stupid reason */
655					if (!udp ||
656					    udp->spts[0] != ntohs(fw->match.src.ports[0]) ||
657					    udp->spts[1] != ntohs(fw->match.src.ports[1]) ||
658					    udp->dpts[0] != ntohs(fw->match.dst.ports[0]) ||
659					    udp->dpts[1] != ntohs(fw->match.dst.ports[1]))
660						continue;
661
662					if (lxor(udp->invflags & IPT_UDP_INV_SRCPT, fw->match.flags & NETCONF_INV_SRCPT) ||
663					    lxor(udp->invflags & IPT_UDP_INV_DSTPT, fw->match.flags & NETCONF_INV_DSTPT))
664						continue;
665				}
666
667				/* Compare source MAC addresses */
668				if (!ETHER_ISNULLADDR(fw->match.mac.octet)) {
669					for_each_ipt_match(match, entry) {
670						if (strncmp(match->u.user.name, "mac", IPT_FUNCTION_MAXNAMELEN) != 0)
671							continue;
672
673						mac = (struct ipt_mac_info *) &match->data[0];
674						break;
675					}
676
677					if (!mac ||
678					    memcmp(mac->srcaddr, fw->match.mac.octet, ETHER_ADDR_LEN) != 0 ||
679					    ( mac->invert && !(fw->match.flags & NETCONF_INV_MAC)) ||
680					    (!mac->invert &&  (fw->match.flags & NETCONF_INV_MAC)))
681						continue;
682				}
683
684				/* Compare packet states */
685				if (fw->match.state) {
686					for_each_ipt_match(match, entry) {
687						if (strncmp(match->u.user.name, "state", IPT_FUNCTION_MAXNAMELEN) != 0)
688							continue;
689
690						state = (struct ipt_state_info *) &match->data[0];
691						break;
692					}
693
694					if (!state ||
695					    lxor(state->statemask & IPT_STATE_INVALID, fw->match.state & NETCONF_INVALID) ||
696					    lxor(state->statemask & IPT_STATE_BIT(IP_CT_ESTABLISHED), fw->match.state & NETCONF_ESTABLISHED) ||
697					    lxor(state->statemask & IPT_STATE_BIT(IP_CT_RELATED), fw->match.state & NETCONF_RELATED) ||
698					    lxor(state->statemask & IPT_STATE_BIT(IP_CT_NEW), fw->match.state & NETCONF_NEW))
699						continue;
700				}
701
702				/* Compare local time */
703				if (fw->match.secs[0] || fw->match.secs[1]) {
704					for_each_ipt_match(match, entry) {
705						if (strncmp(match->u.user.name, "time", IPT_FUNCTION_MAXNAMELEN) != 0)
706							continue;
707
708						/* We added 8 bytes of day range at the end */
709						if (match->u.match_size < (IPT_ALIGN(sizeof(struct ipt_entry_match)) +
710									   IPT_ALIGN(sizeof(time_info_t) + TIME_INFO_EXTRA_BYTES)))
711							continue;
712
713						time = (time_info_t *) &match->data[0];
714						break;
715					}
716
717					if (!time)
718						continue;
719					else {
720						unsigned int time_start, time_stop;
721#ifndef LINUX_2_6_36
722						unsigned int *days = (unsigned int *) &time[1];
723
724						time_start = time->time_start;
725						time_stop = time->time_stop;
726#else
727						unsigned int days[2];
728
729						time_start = time->daytime_start;
730						time_stop = time->daytime_stop;
731						get_days(days, time);
732#endif
733						if (fw->match.days[0] != days[0] ||
734						    fw->match.days[1] != days[1] ||
735						    fw->match.secs[0] != time_start ||
736						    fw->match.secs[1] != time_stop)
737							continue;
738					}
739				}
740
741				/* Compare target type */
742#ifndef LINUX_2_6_36
743				if (fw->target != target_num(entry, &handle))
744#else
745				if (fw->target != target_num(entry, handle))
746#endif /* linux-2.6.36 */
747					continue;
748				target = (struct ipt_entry_target *) ((int) entry + entry->target_offset);
749
750				/* Compare NAT target information */
751				if (nat) {
752#ifndef LINUX_2_6_36
753					struct ip_nat_multi_range *mr = (struct ip_nat_multi_range *) &target->data[0];
754					struct ip_nat_range *range = (struct ip_nat_range *) &mr->range[0];
755#else
756					struct nf_nat_multi_range *mr = (struct nf_nat_multi_range *) &target->data[0];
757					struct nf_nat_range *range = (struct nf_nat_range *) &mr->range[0];
758#endif /* linux-2.6.36 */
759
760					/* Compare mapped IP address */
761					if (range->min_ip != nat->ipaddr.s_addr)
762						continue;
763
764					/* Compare mapped TCP port(s) */
765					if (fw->match.ipproto == IPPROTO_TCP) {
766						if (range->min.tcp.port != nat->ports[0] ||
767						    range->max.tcp.port != nat->ports[1])
768							continue;
769					}
770
771					/* Compare mapped UDP port(s) */
772					else if (fw->match.ipproto == IPPROTO_UDP) {
773						if (range->min.udp.port != nat->ports[0] ||
774						    range->max.udp.port != nat->ports[1])
775							continue;
776					}
777				}
778
779				/* Compare application specific port forward information */
780				else if (app) {
781					struct ip_autofw_info *info = (struct ip_autofw_info *) &target->data[0];
782
783					if (app->proto != info->proto ||
784					    app->dport[0] != info->dport[0] ||
785					    app->dport[1] != info->dport[1] ||
786					    app->to[0] != info->to[0] ||
787					    app->to[1] != info->to[1])
788						continue;
789				}
790
791				break;
792			}
793
794			if (entry)
795				break;
796		}
797
798#ifndef LINUX_2_6_36
799		if (!iptc_commit(&handle)) {
800#else
801		if (!iptc_commit(handle)) {
802#endif /* linux-2.6.36 */
803#ifdef LINUX_2_6_36
804			iptc_free(handle);
805#endif
806			fprintf(stderr, "%s\n", iptc_strerror(errno));
807			return -errno;
808		}
809
810#ifdef LINUX_2_6_36
811		iptc_free(handle);
812#endif
813		if (entry)
814			break;
815	}
816
817	return (entry ? ret : -ENOENT);
818}
819
820/*
821 * See if a given firewall entry already exists
822 * @param	nat	NAT entry to look for
823 * @return	whether NAT entry exists
824 */
825int
826netconf_fw_exists(netconf_fw_t *fw)
827{
828	return (netconf_fw_index(fw) >= 0);
829}
830
831/*
832 * Allocate and append a match structure to an existing ipt_entry
833 * @param	pentry			pointer to pointer to initialized ipt_entry
834 * @param	name			name of match
835 * @param	match_data_size		size of data portion of match structure
836 * @return	pointer to newly created match header inside ipt_entry
837 */
838static struct ipt_entry_match *
839netconf_append_match(struct ipt_entry **pentry, const char *name, size_t match_data_size)
840{
841	struct ipt_entry *entry;
842	struct ipt_entry_match *match;
843	size_t match_size = 0;
844
845	match_size += IPT_ALIGN(sizeof(struct ipt_entry_match));
846	match_size += IPT_ALIGN(match_data_size);
847
848	if (!(entry = realloc(*pentry, (*pentry)->next_offset + match_size))) {
849		perror("realloc");
850		return NULL;
851	}
852
853	match = (struct ipt_entry_match *) ((int) entry + entry->next_offset);
854	entry->next_offset += match_size;
855	entry->target_offset += match_size;
856	memset(match, 0, match_size);
857
858	strncpy(match->u.user.name, name, IPT_FUNCTION_MAXNAMELEN);
859	match->u.match_size = match_size;
860
861	*pentry = entry;
862	return match;
863}
864
865/*
866 * Allocate and append a target structure to an existing ipt_entry
867 * @param	pentry			pointer to pointer to initialized ipt_entry with matches
868 * @param	name			name of target
869 * @param	target_data_size	size of data portion of target structure
870 * @return	pointer to newly created target header inside ipt_entry
871 */
872static struct ipt_entry_target *
873netconf_append_target(struct ipt_entry **pentry, const char *name, size_t target_data_size)
874{
875	struct ipt_entry *entry;
876	struct ipt_entry_target *target;
877	size_t target_size = 0;
878
879	target_size += IPT_ALIGN(sizeof(struct ipt_entry_target));
880	target_size += IPT_ALIGN(target_data_size);
881
882	if (!(entry = realloc(*pentry, (*pentry)->next_offset + target_size))) {
883		perror("realloc");
884		return NULL;
885	}
886
887	target = (struct ipt_entry_target *) ((int) entry + entry->next_offset);
888	entry->next_offset += target_size;
889	memset(target, 0, target_size);
890
891	strncpy(target->u.user.name, name, IPT_FUNCTION_MAXNAMELEN);
892	target->u.target_size = target_size;
893
894	*pentry = entry;
895	return target;
896}
897
898/*
899 * Insert an entry into a reasonable location in the chain
900 * @param	chain	chain name
901 * @param	entry	iptables entry
902 * @param	handle	table handle
903 * @return	TRUE on success and 0 on failure
904 */
905static int
906#ifdef LINUX26
907#ifndef LINUX_2_6_36
908insert_entry(const char *chain, const char *target_name, struct ipt_entry *entry, iptc_handle_t *handle)
909#else
910insert_entry(const char *chain, const char *target_name, struct ipt_entry *entry, struct iptc_handle *handle)
911#endif /* linux-2.6.36 */
912#else /* LINUX26 */
913insert_entry(const char *chain, struct ipt_entry *entry, iptc_handle_t *handle)
914#endif /* LINUX26 */
915{
916	int i;
917	struct ipt_ip blank;
918	const struct ipt_entry *rule;
919	struct ipt_entry_target *target;
920
921	target = (struct ipt_entry_target *) ((int) entry + entry->target_offset);
922	memset(&blank, 0, sizeof(struct ipt_ip));
923
924	/* If this is a default policy (no match) insert at the end of the chain */
925	if (entry->target_offset == sizeof(struct ipt_entry) &&
926	    !memcmp(&entry->ip, &blank, sizeof(struct ipt_ip)))
927		return iptc_append_entry(chain, entry, handle);
928
929	/* If dropping insert at the beginning of the chain */
930#ifdef LINUX26
931	if (!strcmp(target_name, "DROP") ||
932	    !strcmp(target_name, "logdrop"))
933		return iptc_insert_entry(chain, entry, 0, handle);
934	/* If accepting insert after the last drop but before the first default policy */
935	else if (!strcmp(target_name, "ACCEPT") ||
936		 !strcmp(target_name, "logaccept")) {
937#else /* LINUX26 */
938	if (!strcmp(iptc_get_target(entry, handle), "DROP") ||
939	    !strcmp(iptc_get_target(entry, handle), "logdrop"))
940		return iptc_insert_entry(chain, entry, 0, handle);
941	/* If accepting insert after the last drop but before the first default policy */
942	else if (!strcmp(iptc_get_target(entry, handle), "ACCEPT") ||
943		 !strcmp(iptc_get_target(entry, handle), "logaccept")) {
944#endif /* LINUX26 */
945		for (i = 0, rule = iptc_first_rule(chain, handle); rule;
946		     i++, rule = iptc_next_rule(rule, handle)) {
947			if ((strcmp(iptc_get_target(rule, handle), "DROP") &&
948			     strcmp(iptc_get_target(rule, handle), "logdrop")) ||
949			    (rule->target_offset == sizeof(struct ipt_entry) &&
950			     !memcmp(&rule->ip, &blank, sizeof(struct ipt_ip))))
951				break;
952		}
953		return iptc_insert_entry(chain, entry, i, handle);
954	}
955
956	/* Otherwise insert at the end of the chain */
957	else
958		return iptc_append_entry(chain, entry, handle);
959}
960
961/*
962 * Add a firewall entry
963 * @param	fw	firewall entry
964 * @return	0 on success and errno on failure
965 */
966int
967netconf_add_fw(netconf_fw_t *fw)
968{
969	netconf_filter_t *filter = NULL;
970	netconf_nat_t *nat = NULL;
971	netconf_app_t *app = NULL;
972
973	struct ipt_entry *entry;
974	struct ipt_entry_match *match;
975	struct ipt_entry_target *target;
976#ifndef LINUX_2_6_36
977	iptc_handle_t handle = NULL;
978#else
979	struct iptc_handle *handle = NULL;
980#endif
981
982	if (!netconf_valid_ipproto(fw->match.ipproto)) {
983		fprintf(stderr, "invalid IP protocol %d\n", fw->match.ipproto);
984		return -EINVAL;
985	}
986
987	if (!netconf_valid_target(fw->target)) {
988		fprintf(stderr, "invalid target type %d\n", fw->target);
989		return EINVAL;
990	}
991
992	/* Only know about specified target types */
993	if (netconf_valid_filter(fw->target))
994		filter = (netconf_filter_t *) fw;
995	else if (netconf_valid_nat(fw->target))
996		nat = (netconf_nat_t *) fw;
997	else if (fw->target == NETCONF_APP)
998		app = (netconf_app_t *) fw;
999	else
1000		return EINVAL;
1001
1002	/* Allocate entry */
1003	if (!(entry = calloc(1, sizeof(struct ipt_entry)))) {
1004		perror("calloc");
1005		return errno;
1006	}
1007
1008	/* Initialize entry parameters */
1009	entry->nfcache |= NFC_UNKNOWN;
1010	entry->next_offset = entry->target_offset = sizeof(struct ipt_entry);
1011
1012	if (nat && (nat->type == NETCONF_CONE_NAT))
1013		entry->nfcache |= NETCONF_CONE_NAT;
1014
1015	/* Match by IP address(es) */
1016	if (fw->match.src.ipaddr.s_addr & fw->match.src.netmask.s_addr) {
1017		entry->ip.src.s_addr = fw->match.src.ipaddr.s_addr;
1018		entry->ip.smsk.s_addr = fw->match.src.netmask.s_addr;
1019		entry->nfcache |= NFC_IP_SRC;
1020		entry->ip.invflags |= (fw->match.flags & NETCONF_INV_SRCIP) ? IPT_INV_SRCIP : 0;
1021	}
1022	if (fw->match.dst.ipaddr.s_addr & fw->match.dst.netmask.s_addr) {
1023		entry->ip.dst.s_addr = fw->match.dst.ipaddr.s_addr;
1024		entry->ip.dmsk.s_addr = fw->match.dst.netmask.s_addr;
1025		entry->nfcache |= NFC_IP_DST;
1026		entry->ip.invflags |= (fw->match.flags & NETCONF_INV_DSTIP) ? IPT_INV_DSTIP : 0;
1027	}
1028
1029	/* Match by inbound or outbound interface name */
1030	if (strlen(fw->match.in.name) > 0) {
1031		strncpy(entry->ip.iniface, fw->match.in.name, IFNAMSIZ);
1032		memset(&entry->ip.iniface_mask, 0, IFNAMSIZ);
1033		memset(&entry->ip.iniface_mask, 0xff, strlen(fw->match.in.name) + 1);
1034		entry->ip.invflags |= (fw->match.flags & NETCONF_INV_IN) ? IPT_INV_VIA_IN : 0;
1035		entry->nfcache |= NFC_IP_IF_IN;
1036	}
1037	if (strlen(fw->match.out.name) > 0) {
1038		strncpy(entry->ip.outiface, fw->match.out.name, IFNAMSIZ);
1039		memset(&entry->ip.outiface_mask, 0, IFNAMSIZ);
1040		memset(&entry->ip.outiface_mask, 0xff, strlen(fw->match.in.name) + 1);
1041		entry->ip.invflags |= (fw->match.flags & NETCONF_INV_IN) ? IPT_INV_VIA_OUT : 0;
1042		entry->nfcache |= NFC_IP_IF_OUT;
1043	}
1044
1045	/* Match by TCP port(s) */
1046	if (fw->match.ipproto == IPPROTO_TCP) {
1047		struct ipt_tcp *tcp;
1048
1049		if (!(match = netconf_append_match(&entry, "tcp", sizeof(struct ipt_tcp))))
1050			goto err;
1051		tcp = (struct ipt_tcp *) &match->data[0];
1052
1053		entry->ip.proto = IPPROTO_TCP;
1054		entry->nfcache |= NFC_IP_PROTO;
1055
1056		/* Match ports stored in host order for some stupid reason */
1057		tcp->spts[0] = ntohs(fw->match.src.ports[0]);
1058		tcp->spts[1] = ntohs(fw->match.src.ports[1]);
1059		tcp->invflags |= (fw->match.flags & NETCONF_INV_SRCPT) ? IPT_TCP_INV_SRCPT : 0;
1060		entry->nfcache |= (tcp->spts[0] != 0 || tcp->spts[1] != 0xffff) ? NFC_IP_SRC_PT : 0;
1061
1062		/* Match ports stored in host order for some stupid reason */
1063		tcp->dpts[0] = ntohs(fw->match.dst.ports[0]);
1064		tcp->dpts[1] = ntohs(fw->match.dst.ports[1]);
1065		tcp->invflags |= (fw->match.flags & NETCONF_INV_DSTPT) ? IPT_TCP_INV_DSTPT : 0;
1066		entry->nfcache |= (tcp->dpts[0] != 0 || tcp->dpts[1] != 0xffff) ? NFC_IP_DST_PT : 0;
1067	}
1068
1069	/* Match by UDP port(s) */
1070	else if (fw->match.ipproto == IPPROTO_UDP) {
1071		struct ipt_udp *udp;
1072
1073		if (!(match = netconf_append_match(&entry, "udp", sizeof(struct ipt_udp))))
1074			goto err;
1075		udp = (struct ipt_udp *) &match->data[0];
1076
1077		entry->ip.proto = IPPROTO_UDP;
1078		entry->nfcache |= NFC_IP_PROTO;
1079
1080		/* Match ports stored in host order for some stupid reason */
1081		udp->spts[0] = ntohs(fw->match.src.ports[0]);
1082		udp->spts[1] = ntohs(fw->match.src.ports[1]);
1083		udp->invflags |= (fw->match.flags & NETCONF_INV_SRCPT) ? IPT_UDP_INV_SRCPT : 0;
1084		entry->nfcache |= (udp->spts[0] != 0 || udp->spts[1] != 0xffff) ? NFC_IP_SRC_PT : 0;
1085
1086		/* Match ports stored in host order for some stupid reason */
1087		udp->dpts[0] = ntohs(fw->match.dst.ports[0]);
1088		udp->dpts[1] = ntohs(fw->match.dst.ports[1]);
1089		udp->invflags |= (fw->match.flags & NETCONF_INV_DSTPT) ? IPT_UDP_INV_DSTPT : 0;
1090		entry->nfcache |= (udp->dpts[0] != 0 || udp->dpts[1] != 0xffff) ? NFC_IP_DST_PT : 0;
1091	}
1092
1093	/* Match by source MAC address */
1094	if (!ETHER_ISNULLADDR(fw->match.mac.octet)) {
1095		struct ipt_mac_info *mac;
1096
1097		if (!(match = netconf_append_match(&entry, "mac", sizeof(struct ipt_mac_info))))
1098			goto err;
1099		mac = (struct ipt_mac_info *) &match->data[0];
1100
1101		memcpy(mac->srcaddr, fw->match.mac.octet, ETHER_ADDR_LEN);
1102		mac->invert = (fw->match.flags & NETCONF_INV_MAC) ? 1 : 0;
1103	}
1104
1105	/* Match by packet state */
1106	if (fw->match.state) {
1107		struct ipt_state_info *state;
1108
1109		if (!(match = netconf_append_match(&entry, "state", sizeof(struct ipt_state_info))))
1110			goto err;
1111		state = (struct ipt_state_info *) &match->data[0];
1112
1113		state->statemask |= (fw->match.state & NETCONF_INVALID) ? IPT_STATE_INVALID : 0;
1114		state->statemask |= (fw->match.state & NETCONF_ESTABLISHED) ? IPT_STATE_BIT(IP_CT_ESTABLISHED) : 0;
1115		state->statemask |= (fw->match.state & NETCONF_RELATED) ? IPT_STATE_BIT(IP_CT_RELATED) : 0;
1116		state->statemask |= (fw->match.state & NETCONF_NEW) ? IPT_STATE_BIT(IP_CT_NEW) : 0;
1117	}
1118
1119	/* Match by local time */
1120	if (fw->match.secs[0] || fw->match.secs[1]) {
1121		time_info_t *time;
1122#ifndef LINUX_2_6_36
1123		unsigned int *days;
1124		int i;
1125#endif
1126		if (fw->match.secs[0] >= (24 * 60 * 60) || fw->match.secs[1] >= (24 * 60 * 60) ||
1127		    fw->match.days[0] >= 7 || fw->match.days[1] >= 7) {
1128			fprintf(stderr, "invalid time %d-%d:%d-%d\n",
1129				fw->match.days[0], fw->match.days[1],
1130				fw->match.secs[0], fw->match.secs[1]);
1131			goto err;
1132		}
1133
1134		if (!(match = netconf_append_match(&entry, "time", sizeof(time_info_t) + TIME_INFO_EXTRA_BYTES)))
1135			goto err;
1136		time = (time_info_t *) &match->data[0];
1137
1138#ifndef LINUX_2_6_36
1139		days = (unsigned int *) &time[1];
1140		days[0] = fw->match.days[0];
1141		days[1] = fw->match.days[1];
1142
1143		for (i = fw->match.days[0]; i != fw->match.days[1]; i = (i + 1) % 7)
1144			time->days_match |= (1 << i);
1145		time->days_match |= (1 << fw->match.days[1]);
1146		time->time_start = fw->match.secs[0];
1147		time->time_stop = fw->match.secs[1];
1148#else
1149		/* We don't need absolute date match */
1150		time->date_start = 0;
1151		time->date_stop = ~0U;
1152		/* Seconds per day */
1153		time->daytime_start = fw->match.secs[0];
1154		time->daytime_stop = fw->match.secs[1];
1155		/* We don't need month days match */
1156		time->monthdays_match = XT_TIME_ALL_MONTHDAYS;
1157		/* Week days match */
1158		set_days(fw->match.days, time);
1159#endif
1160	}
1161
1162	/* Allocate target */
1163	if (!(target = netconf_append_target(&entry, ipt_target_name[fw->target], ipt_target_size[fw->target])))
1164		goto err;
1165
1166	if (!(handle = iptc_init(ipt_table_name[fw->target]))) {
1167		fprintf(stderr, "%s\n", iptc_strerror(errno));
1168		goto err;
1169	}
1170
1171	/* Set filter target information */
1172	if (filter) {
1173		if (!netconf_valid_dir(filter->dir)) {
1174			fprintf(stderr, "invalid filter direction %d\n", filter->dir);
1175			goto err;
1176		}
1177
1178#ifdef LINUX26
1179#ifndef LINUX_2_6_36
1180		if (!insert_entry(ipt_filter_chain_name[filter->dir], ipt_target_name[fw->target], entry, &handle)) {
1181#else
1182		if (!insert_entry(ipt_filter_chain_name[filter->dir], ipt_target_name[fw->target], entry, handle)) {
1183#endif
1184#else /* LINUX26 */
1185		if (!insert_entry(ipt_filter_chain_name[filter->dir], entry, &handle)) {
1186#endif /* LINUX26 */
1187			fprintf(stderr, "%s\n", iptc_strerror(errno));
1188			goto err;
1189		}
1190	}
1191
1192	/* Set NAT target information */
1193	else if (nat) {
1194#ifndef LINUX_2_6_36
1195		struct ip_nat_multi_range *mr = (struct ip_nat_multi_range *) &target->data[0];
1196		struct ip_nat_range *range = (struct ip_nat_range *) &mr->range[0];
1197#else
1198		struct nf_nat_multi_range *mr = (struct nf_nat_multi_range *) &target->data[0];
1199		struct nf_nat_range *range = (struct nf_nat_range *) &mr->range[0];
1200#endif /* linux-2.6.36 */
1201
1202		mr->rangesize = 1;
1203
1204		/* Map to IP address */
1205		if (nat->ipaddr.s_addr) {
1206			range->min_ip = range->max_ip = nat->ipaddr.s_addr;
1207			range->flags |= IP_NAT_RANGE_MAP_IPS;
1208		}
1209
1210		/* Map to TCP port(s) */
1211		if (nat->match.ipproto == IPPROTO_TCP) {
1212			range->min.tcp.port = nat->ports[0];
1213			range->max.tcp.port = nat->ports[1];
1214			range->flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
1215		}
1216
1217		/* Map to UDP port(s) */
1218		else if (nat->match.ipproto == IPPROTO_UDP) {
1219			range->min.udp.port = nat->ports[0];
1220			range->max.udp.port = nat->ports[1];
1221			range->flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
1222		}
1223
1224#ifdef LINUX26
1225#ifndef LINUX_2_6_36
1226		if (!insert_entry(ipt_nat_chain_name[fw->target], ipt_target_name[fw->target], entry, &handle)) {
1227#else
1228		if (!insert_entry(ipt_nat_chain_name[fw->target], ipt_target_name[fw->target], entry, handle)) {
1229#endif /* linux-2.6.36 */
1230#else /* LINUX26 */
1231		if (!insert_entry(ipt_nat_chain_name[fw->target], entry, &handle)) {
1232#endif /* LINUX26 */
1233			fprintf(stderr, "%s\n", iptc_strerror(errno));
1234			goto err;
1235		}
1236	}
1237
1238	else if (app) {
1239		struct ip_autofw_info *info = (struct ip_autofw_info *) &target->data[0];
1240
1241		info->proto = app->proto;
1242		info->dport[0] = app->dport[0];
1243		info->dport[1] = app->dport[1];
1244		info->to[0] = app->to[0];
1245		info->to[1] = app->to[1];
1246
1247#ifdef LINUX26
1248#ifndef LINUX_2_6_36
1249		if (!insert_entry("PREROUTING", ipt_target_name[fw->target], entry, &handle)) {
1250#else
1251		if (!insert_entry("PREROUTING", ipt_target_name[fw->target], entry, handle)) {
1252#endif /* linux-2.6.36 */
1253#else /* LINUX26 */
1254		if (!insert_entry("PREROUTING", entry, &handle)) {
1255#endif /* LINUX26 */
1256			fprintf(stderr, "%s\n", iptc_strerror(errno));
1257			goto err;
1258		}
1259	}
1260
1261#ifndef LINUX_2_6_36
1262	if (!iptc_commit(&handle)) {
1263#else
1264	if (!iptc_commit(handle)) {
1265#endif /* linux-2.6.36 */
1266		fprintf(stderr, "%s\n", iptc_strerror(errno));
1267		goto err;
1268	}
1269
1270#ifdef LINUX_2_6_36
1271	iptc_free(handle);
1272#endif
1273	free(entry);
1274	return 0;
1275
1276 err:
1277	if (handle)
1278#ifndef LINUX_2_6_36
1279		iptc_commit(&handle);
1280#else
1281		iptc_commit(handle);
1282#endif /* linux-2.6.36 */
1283#ifdef LINUX_2_6_36
1284	if (handle)
1285		iptc_free(handle);
1286#endif
1287	free(entry);
1288	return errno;
1289}
1290
1291/*
1292 * Delete a firewall entry
1293 * @param	fw	firewall entry
1294 * @return	0 on success and errno on failure
1295 */
1296int
1297netconf_del_fw(netconf_fw_t *fw)
1298{
1299	int num;
1300	const char *chain;
1301#ifndef LINUX_2_6_36
1302	iptc_handle_t handle;
1303#else
1304	struct iptc_handle *handle;
1305#endif /* linux-2.6.36 */
1306
1307	/* netconf_fw_index() sanity checks fw */
1308	if ((num = netconf_fw_index(fw)) < 0)
1309		return -num;
1310
1311	/* Pick the right chain name */
1312	if (netconf_valid_filter(fw->target))
1313		chain = ipt_filter_chain_name[((netconf_filter_t *) fw)->dir];
1314	else if (netconf_valid_nat(fw->target))
1315		chain = ipt_nat_chain_name[fw->target];
1316	else if (fw->target == NETCONF_APP)
1317		chain = "PREROUTING";
1318	else
1319		return EINVAL;
1320
1321	/* Commit changes */
1322	if (!(handle = iptc_init(ipt_table_name[fw->target])) ||
1323#ifndef LINUX_2_6_36
1324	    !iptc_delete_num_entry(chain, num, &handle) ||
1325	    !iptc_commit(&handle)) {
1326#else
1327	    !iptc_delete_num_entry(chain, num, handle) ||
1328	    !iptc_commit(handle)) {
1329#endif /* linux-2.6.36 */
1330#ifdef LINUX_2_6_36
1331		if (handle)
1332			iptc_free(handle);
1333#endif
1334		fprintf(stderr, "%s\n", iptc_strerror(errno));
1335		return errno;
1336	}
1337
1338#ifdef LINUX_2_6_36
1339	iptc_free(handle);
1340#endif
1341	return 0;
1342}
1343
1344/*
1345 * Add or delete a firewall entry or list of firewall entries
1346 * @param	fw_list	firewall entry or list of firewall entries
1347 * @bool	del	whether to delete or add
1348 * @return	0 on success and errno on failure
1349 */
1350static int
1351netconf_manip_fw(netconf_fw_t *fw_list, bool del)
1352{
1353	netconf_fw_t *fw;
1354	int ret;
1355
1356	/* Single firewall entry */
1357	if (netconf_list_empty(fw_list) || !fw_list->next)
1358		return (del ? netconf_del_fw(fw_list) : netconf_add_fw(fw_list));
1359
1360	/* List of firewall entries */
1361	netconf_list_for_each(fw, fw_list) {
1362		if ((ret = del ? netconf_del_fw(fw) : netconf_add_fw(fw)))
1363			return ret;
1364	}
1365
1366	return 0;
1367}
1368
1369/*
1370 * Add a NAT entry or list of NAT entries
1371 * @param	nat_list	NAT entry or list of NAT entries
1372 * @return	0 on success and errno on failure
1373 */
1374int
1375netconf_add_nat(netconf_nat_t *nat_list)
1376{
1377	return netconf_manip_fw((netconf_fw_t *) nat_list, 0);
1378}
1379
1380/*
1381 * Delete a NAT entry or list of NAT entries
1382 * @param	nat_list	NAT entry or list of NAT entries
1383 * @return	0 on success and errno on failure
1384 */
1385int
1386netconf_del_nat(netconf_nat_t *nat_list)
1387{
1388	return netconf_manip_fw((netconf_fw_t *) nat_list, 1);
1389}
1390
1391/*
1392 * Get an array of the current NAT entries
1393 * @param	nat_array	array of NAT entries
1394 * @param	space		Pointer to size of nat_array in bytes
1395 * @return 0 on success and errno on failure
1396 */
1397int
1398netconf_get_nat(netconf_nat_t *nat_array, int *space)
1399{
1400	netconf_fw_t *fw, fw_list;
1401	int ret;
1402	int found = 0;
1403
1404	if ((ret = netconf_get_fw(&fw_list)))
1405		return ret;
1406
1407	netconf_list_for_each(fw, &fw_list) {
1408		if (netconf_valid_nat(fw->target)) {
1409			found++;
1410			if (*space && *space >= (found * sizeof(netconf_nat_t)))
1411				memcpy(&nat_array[found - 1], (netconf_nat_t *) fw, sizeof(netconf_nat_t));
1412		}
1413	}
1414
1415	if (!*space)
1416		*space = found * sizeof(netconf_nat_t);
1417
1418	netconf_list_free(&fw_list);
1419	return 0;
1420}
1421
1422/*
1423 * Add a filter entry or list of filter entries
1424 * @param	filter_list	filter entry or list of filter entries
1425 * @return	0 on success and errno on failure
1426 */
1427int
1428netconf_add_filter(netconf_filter_t *filter_list)
1429{
1430	return netconf_manip_fw((netconf_fw_t *) filter_list, 0);
1431}
1432
1433/*
1434 * Delete a filter entry or list of filter entries
1435 * @param	filter_list	filter entry or list of filter entries
1436 * @return	0 on success and errno on failure
1437 */
1438int
1439netconf_del_filter(netconf_filter_t *filter_list)
1440{
1441	return netconf_manip_fw((netconf_fw_t *) filter_list, 1);
1442}
1443
1444/*
1445 * Get an array of the current filter entries
1446 * @param	filter_array	array of filter entries
1447 * @param	space		Pointer to size of filter_array in bytes
1448 * @return 0 on success and errno on failure
1449 */
1450int
1451netconf_get_filter(netconf_filter_t *filter_array, int *space)
1452{
1453	netconf_fw_t *fw, fw_list;
1454	int ret;
1455	int found = 0;
1456
1457	if ((ret = netconf_get_fw(&fw_list)))
1458		return ret;
1459
1460	netconf_list_for_each(fw, &fw_list) {
1461		if (netconf_valid_filter(fw->target)) {
1462			found++;
1463			if (*space && *space >= (found * sizeof(netconf_filter_t)))
1464				memcpy(&filter_array[found - 1], (netconf_filter_t *) fw, sizeof(netconf_filter_t));
1465		}
1466	}
1467
1468	if (!*space)
1469		*space = found * sizeof(netconf_filter_t);
1470
1471	netconf_list_free(&fw_list);
1472	return 0;
1473}
1474
1475/*
1476 * Generates an ipt_entry with an optional match and one target
1477 * @param match_name		match name
1478 * @param match_data		match data
1479 * @param match_data_size	match data size
1480 * @param target_name		target name
1481 * @param target_data		target data
1482 * @param target_data_size	target data size
1483 * @return newly allocated and initialized ipt_entry
1484 */
1485static struct ipt_entry *
1486netconf_generate_entry(const char *match_name, const void *match_data, size_t match_data_size,
1487		       const char *target_name, const void *target_data, size_t target_data_size)
1488{
1489	struct ipt_entry *entry;
1490	struct ipt_entry_match *match;
1491	struct ipt_entry_target *target;
1492
1493	/* Allocate entry */
1494	if (!(entry = calloc(1, sizeof(struct ipt_entry)))) {
1495		perror("calloc");
1496		return NULL;
1497	}
1498
1499	/* Initialize entry parameters */
1500	entry->next_offset = entry->target_offset = sizeof(struct ipt_entry);
1501
1502	/* Allocate space for and copy match data */
1503	if (match_data) {
1504		if (!(match = netconf_append_match(&entry, match_name, match_data_size)))
1505			goto err;
1506		memcpy(&match->data[0], match_data, match_data_size);
1507	}
1508
1509	/* Allocate space for and copy target data */
1510	if (!(target = netconf_append_target(&entry, target_name, target_data_size)))
1511		goto err;
1512	memcpy(&target->data[0], target_data, target_data_size);
1513
1514	return entry;
1515
1516 err:
1517	free(entry);
1518	return NULL;
1519}
1520
1521static int
1522netconf_reset_chain(char *table, char *chain)
1523{
1524#ifndef LINUX_2_6_36
1525	iptc_handle_t handle = NULL;
1526#else
1527	struct iptc_handle *handle = NULL;
1528#endif /* linux-2.6.36 */
1529
1530	/* Get handle to table */
1531	if (!(handle = iptc_init(table)))
1532		goto err;
1533
1534	/* Create chain if necessary */
1535	if (!iptc_is_chain(chain, handle))
1536#ifndef LINUX_2_6_36
1537		if (!iptc_create_chain(chain, &handle))
1538#else
1539		if (!iptc_create_chain(chain, handle))
1540#endif /* linux-2.6.36 */
1541			goto err;
1542
1543	/* Flush entries and commit */
1544#ifndef LINUX_2_6_36
1545	if (!iptc_flush_entries(chain, &handle) ||
1546	    !iptc_commit(&handle))
1547#else
1548	if (!iptc_flush_entries(chain, handle) ||
1549	    !iptc_commit(handle))
1550#endif /* linux-2.6.36 */
1551		goto err;
1552
1553#ifdef LINUX_2_6_36
1554	iptc_free(handle);
1555#endif
1556	return 0;
1557
1558 err:
1559	if (handle)
1560#ifndef LINUX_2_6_36
1561		iptc_commit(&handle);
1562#else
1563		iptc_commit(handle);
1564#endif /* linux-2.6.36 */
1565#ifdef LINUX_2_6_36
1566	if (handle)
1567		iptc_free(handle);
1568#endif
1569	fprintf(stderr, "%s\n", iptc_strerror(errno));
1570	return errno;
1571}
1572
1573/*
1574 * Reset the firewall to a sane state
1575 * @return	0 on success and errno on failure
1576 */
1577int
1578netconf_reset_fw(void)
1579{
1580#ifndef LINUX_2_6_36
1581	iptc_handle_t handle = NULL;
1582#else
1583	struct iptc_handle *handle = NULL;
1584#endif /* linux-2.6.36 */
1585	struct ipt_entry *entry = NULL;
1586	struct ipt_state_info state;
1587	struct ipt_log_info log;
1588	int ret, unused;
1589
1590	/* Reset default chains */
1591	if ((ret = netconf_reset_chain("filter", "INPUT")) ||
1592	    (ret = netconf_reset_chain("filter", "FORWARD")) ||
1593	    (ret = netconf_reset_chain("filter", "OUTPUT")) ||
1594	    (ret = netconf_reset_chain("nat", "PREROUTING")) ||
1595	    (ret = netconf_reset_chain("nat", "POSTROUTING")) ||
1596	    (ret = netconf_reset_chain("nat", "OUTPUT")))
1597		return ret;
1598
1599	/* Reset custom chains */
1600	if ((ret = netconf_reset_chain("filter", "logdrop")) ||
1601	    (ret = netconf_reset_chain("filter", "logaccept")))
1602		goto err;
1603
1604	/* Log only when a connection is attempted */
1605	memset(&state, 0, sizeof(state));
1606	state.statemask = IPT_STATE_BIT(IP_CT_NEW);
1607
1608	/* Set miscellaneous log parameters */
1609	memset(&log, 0, sizeof(log));
1610	log.level = LOG_WARNING;
1611	log.logflags = 0xf;
1612
1613	/* Log packet */
1614	strncpy(log.prefix, "DROP ", sizeof(log.prefix));
1615	if (!(entry = netconf_generate_entry("state", &state, sizeof(state), "LOG", &log, sizeof(log))))
1616		return ENOMEM;
1617	entry->nfcache |= NFC_UNKNOWN;
1618	if (!(handle = iptc_init("filter")) ||
1619#ifndef LINUX_2_6_36
1620	    !iptc_insert_entry("logdrop", entry, 0, &handle) ||
1621	    !iptc_commit(&handle))
1622#else
1623	    !iptc_insert_entry("logdrop", entry, 0, handle) ||
1624	    !iptc_commit(handle))
1625#endif /* linux-2.6.36 */
1626		goto err;
1627#ifdef LINUX_2_6_36
1628	iptc_free(handle);
1629#endif
1630	free(entry);
1631
1632	/* Drop packet */
1633	if (!(entry = netconf_generate_entry(NULL, NULL, 0, "DROP", &unused, sizeof(unused))))
1634		return ENOMEM;
1635	entry->nfcache |= NFC_UNKNOWN;
1636	if (!(handle = iptc_init("filter")) ||
1637#ifndef LINUX_2_6_36
1638	    !iptc_insert_entry("logdrop", entry, 1, &handle) ||
1639	    !iptc_commit(&handle))
1640#else
1641	    !iptc_insert_entry("logdrop", entry, 1, handle) ||
1642	    !iptc_commit(handle))
1643#endif /* linux-2.6.36 */
1644		goto err;
1645#ifdef LINUX_2_6_36
1646	iptc_free(handle);
1647#endif
1648	free(entry);
1649
1650	/* Log packet */
1651	strncpy(log.prefix, "ACCEPT ", sizeof(log.prefix));
1652	if (!(entry = netconf_generate_entry("state", &state, sizeof(state), "LOG", &log, sizeof(log))))
1653		return ENOMEM;
1654	entry->nfcache |= NFC_UNKNOWN;
1655	if (!(handle = iptc_init("filter")) ||
1656#ifndef LINUX_2_6_36
1657	    !iptc_insert_entry("logaccept", entry, 0, &handle) ||
1658	    !iptc_commit(&handle))
1659#else
1660	    !iptc_insert_entry("logaccept", entry, 0, handle) ||
1661	    !iptc_commit(handle))
1662#endif /* linux-2.6.36 */
1663		goto err;
1664#ifdef LINUX_2_6_36
1665	iptc_free(handle);
1666#endif
1667	free(entry);
1668
1669	/* Accept packet */
1670	if (!(entry = netconf_generate_entry(NULL, NULL, 0, "ACCEPT", &unused, sizeof(unused))))
1671		return ENOMEM;
1672	entry->nfcache |= NFC_UNKNOWN;
1673	if (!(handle = iptc_init("filter")) ||
1674#ifndef LINUX_2_6_36
1675	    !iptc_insert_entry("logaccept", entry, 1, &handle) ||
1676	    !iptc_commit(&handle))
1677#else
1678	    !iptc_insert_entry("logaccept", entry, 1, handle) ||
1679	    !iptc_commit(handle))
1680#endif /* linux-2.6.36 */
1681		goto err;
1682#ifdef LINUX_2_6_36
1683	iptc_free(handle);
1684#endif
1685	free(entry);
1686
1687	return 0;
1688
1689 err:
1690#ifdef LINUX_2_6_36
1691	if (handle)
1692		iptc_free(handle);
1693#endif
1694	if (entry)
1695		free(entry);
1696	fprintf(stderr, "%s\n", iptc_strerror(errno));
1697	return errno;
1698}
1699
1700/*
1701 * Below are miscellaneous functions that do not fit into the grand
1702 * scheme of netconf
1703 */
1704
1705/*
1706 * Clamp TCP MSS value to PMTU of interface (for masquerading through PPPoE)
1707 * @return	0 on success and errno on failure
1708 */
1709int
1710netconf_clamp_mss_to_pmtu(void)
1711{
1712	struct ipt_entry *entry;
1713#ifndef LINUX_2_6_36
1714	iptc_handle_t handle;
1715#else
1716	struct iptc_handle *handle = NULL;
1717#endif /* linux-2.6.36 */
1718	struct ipt_tcp tcp;
1719	struct ipt_tcpmss_info tcpmss;
1720
1721	/* Match on SYN=1 RST=0 */
1722	memset(&tcp, 0, sizeof(tcp));
1723	tcp.spts[1] = tcp.dpts[1] = 0xffff;
1724	tcp.flg_mask = TH_SYN | TH_RST;
1725	tcp.flg_cmp = TH_SYN;
1726
1727	/* Clamp TCP MSS to PMTU */
1728	memset(&tcpmss, 0, sizeof(tcpmss));
1729	tcpmss.mss = IPT_TCPMSS_CLAMP_PMTU;
1730
1731	/* Generate and complete the entry */
1732	if (!(entry = netconf_generate_entry("tcp", &tcp, sizeof(tcp), "TCPMSS", &tcpmss, sizeof(tcpmss))))
1733		return ENOMEM;
1734	entry->ip.proto = IPPROTO_TCP;
1735	entry->nfcache |= NFC_IP_PROTO | NFC_IP_TCPFLAGS;
1736
1737	/* Do it */
1738	if (!(handle = iptc_init("filter")) ||
1739#ifndef LINUX_2_6_36
1740	    !iptc_insert_entry("FORWARD", entry, 0, &handle) ||
1741	    !iptc_commit(&handle)) {
1742#else
1743	    !iptc_insert_entry("FORWARD", entry, 0, handle) ||
1744	    !iptc_commit(handle)) {
1745#endif /* linux-2.6.36 */
1746#ifdef LINUX_2_6_36
1747		if (handle)
1748			iptc_free(handle);
1749#endif
1750		fprintf(stderr, "%s\n", iptc_strerror(errno));
1751		free(entry);
1752		return errno;
1753	}
1754
1755#ifdef LINUX_2_6_36
1756	if (handle)
1757		iptc_free(handle);
1758#endif
1759	free(entry);
1760	return 0;
1761}
1762