1/*
2 * Broadcom UPnP module linux netfilter configuration
3 *
4 * Copyright (C) 2010, 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: upnpnat.c,v 1.5 2009/11/10 06:38:36 Exp $
19 */
20#include <stdio.h>
21#include <stdlib.h>
22#include <errno.h>
23#include <string.h>
24#include <arpa/inet.h>
25#include <netconf.h>
26
27/*
28 * UPnP portmapping --
29 * Add port forward and a matching ACCEPT rule to the FORWARD table
30 */
31static void
32add_nat_entry(netconf_nat_t *entry, int log_level)
33{
34	int dir = NETCONF_FORWARD;
35	int target = (log_level & 2) ? NETCONF_LOG_ACCEPT : NETCONF_ACCEPT;
36	netconf_filter_t filter;
37	struct in_addr netmask = { 0xffffffff };
38	netconf_nat_t nat = *entry;
39
40	/* Set up LAN side match */
41	memset(&filter, 0, sizeof(filter));
42	filter.match.ipproto = nat.match.ipproto;
43	filter.match.src.ports[1] = nat.match.src.ports[1];
44	filter.match.dst.ipaddr.s_addr = nat.ipaddr.s_addr;
45	filter.match.dst.netmask.s_addr = netmask.s_addr;
46	filter.match.dst.ports[0] = nat.ports[0];
47	filter.match.dst.ports[1] = nat.ports[1];
48	strncpy(filter.match.in.name, nat.match.in.name, IFNAMSIZ);
49
50	/* Accept connection */
51	filter.target = target;
52	filter.dir = dir;
53
54	/* Do it */
55	netconf_add_nat(&nat);
56	netconf_add_filter(&filter);
57}
58
59/* Combination PREROUTING DNAT and FORWARD ACCEPT */
60static void
61delete_nat_entry(netconf_nat_t *entry, int log_level)
62{
63	int dir = NETCONF_FORWARD;
64	int target = (log_level & 2) ? NETCONF_LOG_ACCEPT : NETCONF_ACCEPT;
65	netconf_filter_t filter;
66	struct in_addr netmask = { 0xffffffff };
67	netconf_nat_t nat = *entry;
68
69	/* Set up LAN side match */
70	memset(&filter, 0, sizeof(filter));
71	filter.match.ipproto = nat.match.ipproto;
72	filter.match.src.ports[1] = nat.match.src.ports[1];
73	filter.match.dst.ipaddr.s_addr = nat.ipaddr.s_addr;
74	filter.match.dst.netmask.s_addr = netmask.s_addr;
75	filter.match.dst.ports[0] = nat.ports[0];
76	filter.match.dst.ports[1] = nat.ports[1];
77	strncpy(filter.match.in.name, nat.match.in.name, IFNAMSIZ);
78
79	/* Accept connection */
80	filter.target = target;
81	filter.dir = dir;
82
83	/* Do it */
84	errno = netconf_del_nat(&nat);
85	if (errno)
86		fprintf(stderr, "netconf_del_nat returned error %d\n", errno);
87
88	errno = netconf_del_filter(&filter);
89	if (errno)
90		fprintf(stderr, "netconf_del_filter returned error %d\n", errno);
91}
92
93void
94usage(void)
95{
96	fprintf(stderr,
97	        "usage: upnpnat [-i ifname] [-remote value] "
98		"[-eport value] [-proto value] [-eport value] "
99		"[-client value] [-en vlaue] [-loglevel value]");
100	exit(0);
101}
102
103
104/* Command to add or delete from NAT engine */
105int
106main(int argc, char *argv[])
107{
108	netconf_nat_t nat, *entry = &nat;
109	char ifname[IFNAMSIZ] = {0};
110	char Proto[8] = {0};
111	char IntIP[sizeof("255.255.255.255")] = {0};
112	char rthost[sizeof("255.255.255.255")] = {0};
113	unsigned short IntPort = 0;
114	unsigned short ExtPort = 0;
115	int enable = 0;
116	int log_level = 0;
117
118	/* Skip program name */
119	--argc;
120	++argv;
121
122	if (!*argv)
123		usage();
124
125	for (; *argv; ++argv) {
126		if (!strcmp(*argv, "-i")) {
127			if (*++argv) {
128				strncpy(ifname, *argv, sizeof(ifname));
129			}
130		}
131		else if (!strcmp(*argv, "-remote")) {
132			if (*++argv) {
133				strncpy(rthost, *argv, sizeof(rthost));
134				rthost[sizeof(rthost)-1] = 0;
135			}
136		}
137		else if (!strcmp(*argv, "-eport")) {
138			if (*++argv) {
139				ExtPort = atoi(*argv);
140			}
141		}
142		else if (!strcmp(*argv, "-proto")) {
143			if (*++argv) {
144				strncpy(Proto, *argv, sizeof(Proto));
145				Proto[sizeof(Proto)-1] = 0;
146			}
147		}
148		else if (!strcmp(*argv, "-iport")) {
149			if (*++argv) {
150				IntPort = atoi(*argv);
151			}
152		}
153		else if (!strcmp(*argv, "-client")) {
154			if (*++argv) {
155				strncpy(IntIP, *argv, sizeof(IntIP));
156				IntIP[sizeof(IntIP)-1] = 0;
157			}
158		}
159		else if (!strcmp(*argv, "-en")) {
160			if (*++argv) {
161				enable = atoi(*argv);
162			}
163		}
164		else if (!strcmp(*argv, "-log_level")) {
165			if (*++argv) {
166				log_level = atoi(*argv);
167			}
168		}
169		else {
170			usage();
171		}
172	}
173
174	memset(entry, 0, sizeof(netconf_nat_t));
175
176	/* accept from any port */
177	strncpy(entry->match.in.name, ifname, IFNAMSIZ);
178	entry->match.src.ports[0] = 0;
179	entry->match.src.ports[1] = htons(0xffff);
180	entry->match.dst.ports[0] = htons(ExtPort);
181	entry->match.dst.ports[1] = htons(ExtPort);
182
183	if (strlen(rthost)) {
184		inet_aton("255.255.255.255", &entry->match.dst.netmask);
185		inet_aton(rthost, &entry->match.dst.ipaddr);
186	}
187
188	/* parse the specification of the internal NAT client. */
189	entry->target = NETCONF_DNAT;
190
191	if (IntPort != 0) {
192		/* parse the internal ip address. */
193		inet_aton(IntIP, (struct in_addr *)&entry->ipaddr);
194
195		/* parse the internal port number */
196		entry->ports[0] = htons(IntPort);
197		entry->ports[1] = htons(IntPort);
198	}
199
200	if (strcasecmp(Proto, "TCP") == 0) {
201		entry->match.ipproto = IPPROTO_TCP;
202	}
203	else if (strcasecmp(Proto, "UDP") == 0) {
204		entry->match.ipproto = IPPROTO_UDP;
205	}
206
207	/* set to NAT kernel */
208	if (enable) {
209		entry->match.flags &= ~NETCONF_DISABLED;
210		add_nat_entry(entry, log_level);
211	}
212	else {
213		entry->match.flags |= NETCONF_DISABLED;
214		delete_nat_entry(entry, log_level);
215	}
216
217	return 0;
218}
219