1/*
2 * Firewall services
3 *
4 * Copyright (C) 2013, 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: firewall.c 374496 2012-12-13 08:59:12Z $
19 */
20
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <signal.h>
25#include <sys/socket.h>
26#include <netinet/in.h>
27#include <arpa/inet.h>
28#include <sys/types.h>
29#include <dirent.h>
30#include <errno.h>
31#include <linux/sockios.h>
32#include <sys/ioctl.h>
33#include <unistd.h>
34
35#include <bcmnvram.h>
36#include <shutils.h>
37#include <rc.h>
38#include <netconf.h>
39#include <nvparse.h>
40
41/* Add filter to specified table */
42static void
43add_filter(netconf_filter_t *filter, int dir)
44{
45	filter->dir = dir;
46	netconf_add_filter(filter);
47}
48
49
50/* Add port forward and a matching ACCEPT rule to the FORWARD table */
51static void
52add_forward(netconf_nat_t *nat, int dir, int target)
53{
54	netconf_filter_t filter;
55
56	/* Set up LAN side match */
57	memset(&filter, 0, sizeof(filter));
58	filter.match.ipproto = nat->match.ipproto;
59	filter.match.src.ports[1] = nat->match.src.ports[1];
60	filter.match.dst.ipaddr.s_addr = nat->ipaddr.s_addr;
61	filter.match.dst.netmask.s_addr = htonl(0xffffffff);
62	filter.match.dst.ports[0] = nat->ports[0];
63	filter.match.dst.ports[1] = nat->ports[1];
64	strncpy(filter.match.in.name, nat->match.in.name, IFNAMSIZ);
65
66	/* Accept connection */
67	filter.target = target;
68	filter.dir = dir;
69
70	/* Do it */
71	netconf_add_nat(nat);
72	netconf_add_filter(&filter);
73}
74
75
76#if defined(LINUX_2_6_36) && defined(__CONFIG_SAMBA__)
77static void
78add_conntrack(void)
79{
80	char ifname[IFNAMSIZ];
81
82	strncpy(ifname, nvram_safe_get("lan_ifname"), IFNAMSIZ);
83
84	/* Add rules to disable conntrack on SMB ports to reduce CPU loading
85	 * for SAMBA storage application
86	 */
87	eval("iptables", "-t", "raw", "-F");
88	eval("iptables", "-t", "raw", "-A", "PREROUTING", "-i", ifname, "-p", "tcp",
89		"--dport", "137:139", "-j", "NOTRACK");
90	eval("iptables", "-t", "raw", "-A", "PREROUTING", "-i", ifname, "-p", "tcp",
91		"--dport", "445", "-j", "NOTRACK");
92	eval("iptables", "-t", "raw", "-A", "PREROUTING", "-i", ifname, "-p", "udp",
93		"--dport", "137:139", "-j", "NOTRACK");
94	eval("iptables", "-t", "raw", "-A", "PREROUTING", "-i", ifname, "-p", "udp",
95		"--dport", "445", "-j", "NOTRACK");
96	eval("iptables", "-t", "raw", "-A", "OUTPUT", "-o", ifname, "-p", "tcp",
97		"--sport", "137:139", "-j", "NOTRACK");
98	eval("iptables", "-t", "raw", "-A", "OUTPUT", "-o", ifname, "-p", "tcp",
99		"--sport", "445", "-j", "NOTRACK");
100	eval("iptables", "-t", "raw", "-A", "OUTPUT", "-o", ifname, "-p", "udp",
101		"--sport", "137:139", "-j", "NOTRACK");
102	eval("iptables", "-t", "raw", "-A", "OUTPUT", "-o", ifname, "-p", "udp",
103		"--sport", "445", "-j", "NOTRACK");
104
105	eval("iptables", "-t", "filter", "-I", "INPUT", "-i", ifname, "-p", "udp",
106		"--dport", "137:139", "-j", "ACCEPT");
107	eval("iptables", "-t", "filter", "-I", "INPUT", "-i", ifname, "-p", "udp",
108		"--dport", "445", "-j", "ACCEPT");
109	eval("iptables", "-t", "filter", "-I", "INPUT", "-i", ifname, "-p", "tcp",
110		"--dport", "137:139", "-j", "ACCEPT");
111	eval("iptables", "-t", "filter", "-I", "INPUT", "-i", ifname, "-p", "tcp",
112		"--dport", "445", "-j", "ACCEPT");
113}
114#endif /* LINUX_2_6_36 && __CONFIG_SAMBA__ */
115
116int
117start_firewall(void)
118{
119#if 0
120	DIR *dir;
121	struct dirent *file;
122	FILE *fp;
123	char name[NAME_MAX];
124	netconf_filter_t filter;
125	netconf_app_t app;
126	int i, index;
127	char var[256], *next;
128	int log_level, log_drop, log_accept;
129	struct ipaddress_pairs {
130				struct in_addr address;
131				struct in_addr mask;
132				} *ip_addrs = NULL;
133	char lan_proto[20];
134	char lan_ifname[20];
135	char lan_ipaddr[20];
136	char lan_netmask[20];
137	char *address, *mask;
138	struct ipaddress_pairs *cur_ipaddr;
139
140	/* Reset firewall */
141	netconf_reset_fw();
142
143	/* Block obviously spoofed IP addresses */
144	if (!(dir = opendir("/proc/sys/net/ipv4/conf")))
145		perror("/proc/sys/net/ipv4/conf");
146	while (dir && (file = readdir(dir))) {
147		if (strncmp(file->d_name, ".", NAME_MAX) != 0 &&
148		    strncmp(file->d_name, "..", NAME_MAX) != 0) {
149			snprintf(name, sizeof(name),
150				  "/proc/sys/net/ipv4/conf/%s/rp_filter", file->d_name);
151			if (!(fp = fopen(name, "r+"))) {
152				perror(name);
153				break;
154			}
155			fputc('1', fp);
156			fclose(fp);
157		}
158	}
159	if (dir)
160		closedir(dir);
161
162	/* Optionally log connections */
163	log_level = atoi(nvram_safe_get("log_level"));
164	log_drop = (log_level & 1) ? NETCONF_LOG_DROP : NETCONF_DROP;
165	log_accept = (log_level & 2) ? NETCONF_LOG_ACCEPT : NETCONF_ACCEPT;
166
167	/*
168	 * Inbound drops
169	 */
170
171	/* Drop invalid packets */
172	memset(&filter, 0, sizeof(filter));
173	filter.match.state = NETCONF_INVALID;
174	filter.target = NETCONF_DROP;
175	add_filter(&filter, NETCONF_IN);
176	add_filter(&filter, NETCONF_FORWARD);
177
178	/*
179	 * Forward drops
180	 */
181
182	/* Client filters */
183	for (i = 0; i < MAX_NVPARSE; i++) {
184		netconf_filter_t start, end;
185
186		if (get_filter_client(i, &start, &end) && !(start.match.flags & NETCONF_DISABLED)) {
187			while (ntohl(start.match.src.ipaddr.s_addr) <=
188				ntohl(end.match.src.ipaddr.s_addr)) {
189				/* Override target */
190				start.target = log_drop;
191				add_filter(&start, NETCONF_FORWARD);
192				start.match.src.ipaddr.s_addr =
193					htonl(ntohl(start.match.src.ipaddr.s_addr) + 1);
194			}
195		}
196	}
197
198	/* Filter by MAC address */
199	if (!nvram_match("filter_macmode", "disabled")) {
200		memset(&filter, 0, sizeof(filter));
201		strncpy(filter.match.in.name, nvram_safe_get("lan_ifname"), IFNAMSIZ);
202		if (nvram_match("filter_macmode", "allow")) {
203			/* Allow new connections from
204			 * the LAN side only from the specified addresses
205			 */
206			filter.target = log_accept;
207			filter.match.state = NETCONF_NEW;
208		} else {
209			/* Drop connections from the specified addresses */
210			filter.target = log_drop;
211		}
212		foreach(var, nvram_safe_get("filter_maclist"), next) {
213			if (ether_atoe(var, filter.match.mac.octet))
214				add_filter(&filter, NETCONF_FORWARD);
215		}
216	}
217
218	/*
219	 * Inbound accepts
220	 */
221	/* Allow new connections from the loopback interface */
222	memset(&filter, 0, sizeof(filter));
223	filter.match.state = NETCONF_NEW;
224	filter.match.src.ipaddr.s_addr = htonl(INADDR_LOOPBACK);
225	filter.match.src.netmask.s_addr = htonl(INADDR_BROADCAST);
226	strncpy(filter.match.in.name, "lo", IFNAMSIZ);
227	filter.target = log_accept;
228	add_filter(&filter, NETCONF_IN);
229	add_filter(&filter, NETCONF_FORWARD);
230
231	/* allocate memory for ip_addrs */
232	ip_addrs = malloc(sizeof(struct ipaddress_pairs) * MAX_NO_BRIDGE);
233	if (!ip_addrs) {
234		dprintf("start_firewall()Error allocating memory\n");
235		goto start_firewall_cleanup0;
236	}
237	memset(ip_addrs, 0, sizeof(struct ipaddress_pairs) * MAX_NO_BRIDGE);
238	cur_ipaddr = ip_addrs;
239
240	for (i = 0; i < MAX_NO_BRIDGE; i++) {
241		if (!i) {
242			snprintf(lan_ifname, sizeof(lan_ifname), "lan_ifname");
243			snprintf(lan_proto, sizeof(lan_proto), "lan_proto");
244			snprintf(lan_ipaddr, sizeof(lan_ipaddr), "lan_ipaddr");
245			snprintf(lan_netmask, sizeof(lan_netmask), "lan_netmask");
246		}
247		else {
248			snprintf(lan_proto, sizeof(lan_proto), "lan%x_proto", i);
249			snprintf(lan_ifname, sizeof(lan_ifname), "lan%x_ifname", i);
250			snprintf(lan_ipaddr, sizeof(lan_ipaddr), "lan%x_ipaddr", i);
251			snprintf(lan_netmask, sizeof(lan_netmask), "lan%x_netmask", i);
252		}
253		if (!strcmp(nvram_safe_get(lan_ifname), ""))
254			continue;
255
256		/* Allow new connections from the LAN side */
257		memset(&filter, 0, sizeof(filter));
258		filter.match.state = NETCONF_NEW;
259		strncpy(filter.match.in.name, nvram_safe_get(lan_ifname), IFNAMSIZ);
260		inet_aton(nvram_safe_get(lan_ipaddr), (struct in_addr *)&filter.match.src.ipaddr);
261		inet_aton(nvram_safe_get(lan_netmask), (struct in_addr *)&filter.match.src.netmask);
262		filter.match.src.ipaddr.s_addr &= filter.match.src.netmask.s_addr;
263
264		filter.target = log_accept;
265		add_filter(&filter, NETCONF_IN);
266		/* filter_macmode applies only to the first bridge (for now). */
267		/* On other bridges, add this filter */
268		if (!nvram_match("filter_macmode", "allow") || (i > 0)) {
269			add_filter(&filter, NETCONF_FORWARD);
270		}
271
272		/* Allow DHCP broadcast requests */
273		if (nvram_match(lan_proto, "dhcp")) {
274			memset(&filter, 0, sizeof(filter));
275			filter.match.state = NETCONF_NEW;
276			filter.match.ipproto = IPPROTO_UDP;
277			strncpy(filter.match.in.name, nvram_safe_get(lan_ifname), IFNAMSIZ);
278			filter.match.src.netmask.s_addr = htonl(INADDR_BROADCAST);
279			filter.match.src.ipaddr.s_addr = htonl(INADDR_ANY);
280			filter.match.dst.netmask.s_addr = htonl(INADDR_BROADCAST);
281			filter.match.dst.ipaddr.s_addr = htonl(INADDR_BROADCAST);
282			filter.match.dst.ports[0] = htons(67);
283			filter.match.dst.ports[1] = htons(67);
284			filter.match.src.ports[0] = htons(68);
285			filter.match.src.ports[1] = htons(68);
286			filter.target = log_accept;
287			add_filter(&filter, NETCONF_IN);
288		};
289
290		/* store all lanX_ipaddr lanX_netmask */
291		address = nvram_get(lan_ipaddr);
292		mask = nvram_get(lan_netmask);
293
294		if (address && mask) {
295			if (!inet_aton(address, &cur_ipaddr->address)) {
296				cprintf("start_firewall():Invalid lan_ipaddr:%s\n", address);
297				goto start_firewall_cleanup1;
298		}
299		if (!inet_aton(mask, &cur_ipaddr->mask)) {
300			cprintf("start_firewall():Invalid lan_netmask:%s\n", mask);
301			goto start_firewall_cleanup1;
302		}
303		cur_ipaddr++;
304
305		} else if (address || mask) {
306			dprintf("start_firewall(): "
307				"Both bridge ipaddress and ipmask must be defined.\n");
308			goto start_firewall_cleanup1;
309		}
310
311
312	}
313	/* Create restriction filters among bridges */
314	for (i = 1; i < MAX_NO_BRIDGE; i++) {
315		if (!i) {
316			snprintf(lan_ifname, sizeof(lan_ifname), "lan_ifname");
317		}
318		else {
319			snprintf(lan_ifname, sizeof(lan_ifname), "lan%x_ifname", i);
320		}
321		if (!strcmp(nvram_safe_get(lan_ifname), ""))
322			continue;
323
324		for (index = 0; index < MAX_NO_BRIDGE; index++) {
325
326		/* Skip making rule if the dest address is ours
327		 * Assume that the ranges do not overlap so we do not
328		 * check the mask value
329		 */
330			if (index == i)
331				continue;
332
333			memset(&filter, 0, sizeof(filter));
334			filter.match.state = NETCONF_NEW;
335			filter.match.src.ipaddr.s_addr = ip_addrs[i].address.s_addr;
336			filter.match.src.ipaddr.s_addr &= ip_addrs[i].mask.s_addr;
337			filter.match.src.netmask.s_addr = ip_addrs[i].mask.s_addr;
338			filter.match.dst.ipaddr.s_addr = ip_addrs[index].address.s_addr;
339			filter.match.dst.ipaddr.s_addr &= ip_addrs[index].mask.s_addr;
340			filter.match.dst.netmask.s_addr = ip_addrs[index].mask.s_addr;
341			strncpy(filter.match.in.name, nvram_safe_get(lan_ifname), IFNAMSIZ);
342			filter.target = log_drop;
343			add_filter(&filter, NETCONF_IN);
344			add_filter(&filter, NETCONF_FORWARD);
345		}
346	}
347
348	/* Allow established and related outbound connections back in */
349	memset(&filter, 0, sizeof(filter));
350	filter.match.state = NETCONF_ESTABLISHED | NETCONF_RELATED;
351	filter.target = log_accept;
352	add_filter(&filter, NETCONF_IN);
353	add_filter(&filter, NETCONF_FORWARD);
354
355	/*
356	 * NAT
357	 */
358
359	/* Enable IP masquerading */
360	if ((fp = fopen("/proc/sys/net/ipv4/ip_forward", "r+"))) {
361		fputc('1', fp);
362		fclose(fp);
363	} else
364		perror("/proc/sys/net/ipv4/ip_forward");
365
366#if !defined(AUTOFW_PORT_DEPRECATED)
367	/* Application specific port forwards */
368	for (i = 0; i < MAX_NVPARSE; i++) {
369		memset(&app, 0, sizeof(app));
370		if (get_autofw_port(i, &app) && !(app.match.flags & NETCONF_DISABLED))
371			netconf_add_fw((netconf_fw_t *) &app);
372	}
373#endif /* !AUTOFW_PORT_DEPRECATED */
374
375	/*
376	* Inbound defaults
377	*/
378
379	/* Log refused connections */
380	memset(&filter, 0, sizeof(filter));
381	filter.target = log_drop;
382	add_filter(&filter, NETCONF_IN);
383	add_filter(&filter, NETCONF_FORWARD);
384
385#if defined(LINUX_2_6_36) && defined(__CONFIG_SAMBA__)
386	add_conntrack();
387#endif /* LINUX_2_6_36 && __CONFIG_SAMBA__ */
388
389	dprintf("done\n");
390	return 0;
391
392start_firewall_cleanup1:
393	if (ip_addrs) {
394	 free(ip_addrs);
395	 ip_addrs = NULL;
396	}
397
398start_firewall_cleanup0:
399
400	return 1;
401#endif
402    return 0;
403}
404
405
406int
407start_firewall2(char *wan_ifname)
408{
409#if 0
410	netconf_nat_t nat;
411	netconf_filter_t filter;
412	int log_level, log_accept;
413	char tmp[100], prefix[] = "wanXXXXXXXXXX_";
414	int i;
415	char lan_ipaddr[20];
416	char lan_netmask[20];
417
418	/* Optionally log connections */
419	log_level = atoi(nvram_safe_get("log_level"));
420	log_accept = (log_level & 2) ? NETCONF_LOG_ACCEPT : NETCONF_ACCEPT;
421
422	/* Allow new connections from the WAN side */
423	if (nvram_match("fw_disable", "1")) {
424		memset(&filter, 0, sizeof(filter));
425		filter.match.state = NETCONF_NEW;
426		strncpy(filter.match.in.name, wan_ifname, IFNAMSIZ);
427		filter.target = log_accept;
428		add_filter(&filter, NETCONF_IN);
429		add_filter(&filter, NETCONF_FORWARD);
430	}
431
432	/* Enable IP masquerading for bridge */
433	for (i = 0; i < MAX_NO_BRIDGE; i++) {
434		if (!i) {
435			snprintf(lan_ipaddr, sizeof(lan_ipaddr), "lan_ipaddr");
436			snprintf(lan_netmask, sizeof(lan_netmask), "lan_netmask");
437		}
438		else {
439			snprintf(lan_ipaddr, sizeof(lan_ipaddr), "lan%x_ipaddr", i);
440			snprintf(lan_netmask, sizeof(lan_netmask), "lan%x_netmask", i);
441		}
442
443
444		memset(&nat, 0, sizeof(nat));
445		inet_aton(nvram_safe_get(lan_ipaddr), &nat.match.src.ipaddr);
446		inet_aton(nvram_safe_get(lan_netmask), &nat.match.src.netmask);
447		nat.match.src.ipaddr.s_addr &= nat.match.src.netmask.s_addr;
448		strncpy(nat.match.out.name, wan_ifname, IFNAMSIZ);
449		nat.target = NETCONF_MASQ;
450		if (nvram_match("nat_type", "cone"))
451			nat.type = NETCONF_CONE_NAT;
452		netconf_add_nat(&nat);
453	}
454
455	/* Enable remote management */
456	if (nvram_invmatch("http_wanport", "")) {
457		/* Set up WAN side match */
458		memset(&nat, 0, sizeof(nat));
459		nat.match.ipproto = IPPROTO_TCP;
460		nat.match.src.ports[1] = htons(0xffff);
461		nat.match.dst.ports[0] = htons(atoi(nvram_safe_get("http_wanport")));
462		nat.match.dst.ports[1] = htons(atoi(nvram_safe_get("http_wanport")));
463		strncpy(nat.match.in.name, wan_ifname, IFNAMSIZ);
464
465		/* Set up DNAT to LAN */
466		nat.target = NETCONF_DNAT;
467		(void) inet_aton(nvram_safe_get("lan_ipaddr"), &nat.ipaddr);
468		nat.ports[0] = htons(atoi(nvram_safe_get("http_lanport")));
469		nat.ports[1] = htons(atoi(nvram_safe_get("http_lanport")));
470
471		add_forward(&nat, NETCONF_IN, log_accept);
472	}
473
474	/* Persistent (static) port forwards */
475	for (i = 0; i < MAX_NVPARSE; i++) {
476		memset(&nat, 0, sizeof(nat));
477		if (get_forward_port(i, &nat) && !(nat.match.flags & NETCONF_DISABLED)) {
478			/* Set interface name (match packets entering WAN interface) */
479			strncpy(nat.match.in.name, wan_ifname, IFNAMSIZ);
480			add_forward(&nat, NETCONF_FORWARD, log_accept);
481		}
482	}
483
484	/* Forward all WAN ports to DMZ IP address */
485	if (nvram_invmatch("dmz_ipaddr", "")) {
486		/* Set up WAN side match */
487		memset(&nat, 0, sizeof(nat));
488		nat.match.src.ports[1] = htons(0xffff);
489		nat.match.dst.ports[1] = htons(0xffff);
490		strncpy(nat.match.in.name, wan_ifname, IFNAMSIZ);
491
492		/* Set up DNAT to LAN */
493		nat.target = NETCONF_DNAT;
494		(void) inet_aton(nvram_safe_get("dmz_ipaddr"), &nat.ipaddr);
495		nat.ports[1] = htons(0xffff);
496
497		nat.match.ipproto = IPPROTO_TCP;
498		add_forward(&nat, NETCONF_FORWARD, log_accept);
499		nat.match.ipproto = IPPROTO_UDP;
500		add_forward(&nat, NETCONF_FORWARD, log_accept);
501	}
502
503	/* Clamp TCP MSS to PMTU of WAN interface */
504	snprintf(prefix, sizeof(prefix), "wan%d_", wan_ifunit(wan_ifname));
505	if (nvram_match(strcat_r(prefix, "proto", tmp), "pppoe"))
506		netconf_clamp_mss_to_pmtu();
507
508	dprintf("done\n");
509#endif
510	return 0;
511}
512
513
514/*
515*/
516