1/* Shared library add-on to iptables to add multiple TCP port support. */
2#include <stdio.h>
3#include <netdb.h>
4#include <string.h>
5#include <stdlib.h>
6#include <getopt.h>
7#include <iptables.h>
8#include <linux/netfilter_ipv4/ipt_multiport.h>
9
10/* Function which prints out usage message. */
11static void
12help(void)
13{
14	printf(
15"multiport v%s options:\n"
16" --source-ports port[,port,port...]\n"
17" --sports ...\n"
18"				match source port(s)\n"
19" --destination-ports port[,port,port...]\n"
20" --dports ...\n"
21"				match destination port(s)\n"
22" --ports port[,port,port]\n"
23"				match both source and destination port(s)\n",
24IPTABLES_VERSION);
25}
26
27static struct option opts[] = {
28	{ "source-ports", 1, 0, '1' },
29	{ "sports", 1, 0, '1' }, /* synonym */
30	{ "destination-ports", 1, 0, '2' },
31	{ "dports", 1, 0, '2' }, /* synonym */
32	{ "ports", 1, 0, '3' },
33	{0}
34};
35
36static int
37service_to_port(const char *name, const char *proto)
38{
39	struct servent *service;
40
41	if ((service = getservbyname(name, proto)) != NULL)
42		return ntohs((unsigned short) service->s_port);
43
44		return -1;
45}
46
47static u_int16_t
48parse_port(const char *port, const char *proto)
49{
50	unsigned int portnum;
51
52	if (string_to_number(port, 0, 65535, &portnum) != -1 ||
53	    (portnum = service_to_port(port, proto)) != -1)
54		return (u_int16_t)portnum;
55
56	exit_error(PARAMETER_PROBLEM,
57		   "invalid port/service `%s' specified", port);
58}
59
60static unsigned int
61parse_multi_ports(const char *portstring, u_int16_t *ports, const char *proto)
62{
63	char *buffer, *cp, *next;
64	unsigned int i;
65
66	buffer = strdup(portstring);
67	if (!buffer) exit_error(OTHER_PROBLEM, "strdup failed");
68
69	for (cp=buffer, i=0; cp && i<IPT_MULTI_PORTS; cp=next,i++)
70	{
71		next=strchr(cp, ',');
72		if (next) *next++='\0';
73		ports[i] = parse_port(cp, proto);
74	}
75	if (cp) exit_error(PARAMETER_PROBLEM, "too many ports specified");
76	free(buffer);
77	return i;
78}
79
80/* Initialize the match. */
81static void
82init(struct ipt_entry_match *m, unsigned int *nfcache)
83{
84}
85
86static const char *
87check_proto(const struct ipt_entry *entry)
88{
89	if (entry->ip.proto == IPPROTO_TCP)
90		return "tcp";
91	else if (entry->ip.proto == IPPROTO_UDP)
92		return "udp";
93	else if (!entry->ip.proto)
94		exit_error(PARAMETER_PROBLEM,
95			   "multiport needs `-p tcp' or `-p udp'");
96	else
97		exit_error(PARAMETER_PROBLEM,
98			   "multiport only works with TCP or UDP");
99}
100
101/* Function which parses command options; returns true if it
102   ate an option */
103static int
104parse(int c, char **argv, int invert, unsigned int *flags,
105      const struct ipt_entry *entry,
106      unsigned int *nfcache,
107      struct ipt_entry_match **match)
108{
109	const char *proto;
110	struct ipt_multiport *multiinfo
111		= (struct ipt_multiport *)(*match)->data;
112
113	switch (c) {
114	case '1':
115		proto = check_proto(entry);
116		multiinfo->count = parse_multi_ports(argv[optind-1],
117						     multiinfo->ports, proto);
118		multiinfo->flags = IPT_MULTIPORT_SOURCE;
119		*nfcache |= NFC_IP_SRC_PT;
120		break;
121
122	case '2':
123		proto = check_proto(entry);
124		multiinfo->count = parse_multi_ports(argv[optind-1],
125						     multiinfo->ports, proto);
126		multiinfo->flags = IPT_MULTIPORT_DESTINATION;
127		*nfcache |= NFC_IP_DST_PT;
128		break;
129
130	case '3':
131		proto = check_proto(entry);
132		multiinfo->count = parse_multi_ports(argv[optind-1],
133						     multiinfo->ports, proto);
134		multiinfo->flags = IPT_MULTIPORT_EITHER;
135		*nfcache |= NFC_IP_SRC_PT | NFC_IP_DST_PT;
136		break;
137
138	default:
139		return 0;
140	}
141
142	if (*flags)
143		exit_error(PARAMETER_PROBLEM,
144			   "multiport can only have one option");
145	*flags = 1;
146	return 1;
147}
148
149/* Final check; must specify something. */
150static void
151final_check(unsigned int flags)
152{
153	if (!flags)
154		exit_error(PARAMETER_PROBLEM, "multiport expection an option");
155}
156
157static char *
158port_to_service(int port, u_int8_t proto)
159{
160	struct servent *service;
161
162	if ((service = getservbyport(htons(port),
163				     proto == IPPROTO_TCP ? "tcp" : "udp")))
164		return service->s_name;
165
166	return NULL;
167}
168
169static void
170print_port(u_int16_t port, u_int8_t protocol, int numeric)
171{
172	char *service;
173
174	if (numeric || (service = port_to_service(port, protocol)) == NULL)
175		printf("%u", port);
176	else
177		printf("%s", service);
178}
179
180/* Prints out the matchinfo. */
181static void
182print(const struct ipt_ip *ip,
183      const struct ipt_entry_match *match,
184      int numeric)
185{
186	const struct ipt_multiport *multiinfo
187		= (const struct ipt_multiport *)match->data;
188	unsigned int i;
189
190	printf("multiport ");
191
192	switch (multiinfo->flags) {
193	case IPT_MULTIPORT_SOURCE:
194		printf("sports ");
195		break;
196
197	case IPT_MULTIPORT_DESTINATION:
198		printf("dports ");
199		break;
200
201	case IPT_MULTIPORT_EITHER:
202		printf("ports ");
203		break;
204
205	default:
206		printf("ERROR ");
207		break;
208	}
209
210	for (i=0; i < multiinfo->count; i++) {
211		printf("%s", i ? "," : "");
212		print_port(multiinfo->ports[i], ip->proto, numeric);
213	}
214	printf(" ");
215}
216
217/* Saves the union ipt_matchinfo in parsable form to stdout. */
218static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
219{
220	const struct ipt_multiport *multiinfo
221		= (const struct ipt_multiport *)match->data;
222	unsigned int i;
223
224	switch (multiinfo->flags) {
225	case IPT_MULTIPORT_SOURCE:
226		printf("--sports ");
227		break;
228
229	case IPT_MULTIPORT_DESTINATION:
230		printf("--dports ");
231		break;
232
233	case IPT_MULTIPORT_EITHER:
234		printf("--ports ");
235		break;
236	}
237
238	for (i=0; i < multiinfo->count; i++) {
239		printf("%s", i ? "," : "");
240		print_port(multiinfo->ports[i], ip->proto, 0);
241	}
242	printf(" ");
243}
244
245static
246struct iptables_match multiport
247= { NULL,
248    "multiport",
249    IPTABLES_VERSION,
250    IPT_ALIGN(sizeof(struct ipt_multiport)),
251    IPT_ALIGN(sizeof(struct ipt_multiport)),
252    &help,
253    &init,
254    &parse,
255    &final_check,
256    &print,
257    &save,
258    opts
259};
260
261void
262_init(void)
263{
264	register_match(&multiport);
265}
266