blob: 2ae61ff42c18afa4e232cf78a0ba76f85664f195 [file] [log] [blame]
Andreas Ferber47caec02001-06-05 11:56:38 +00001/* 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_mport.h>
9
10/* Function which prints out usage message. */
11static void
12help(void)
13{
14 printf(
15"mport v%s options:\n"
16" --source-ports port[,port:port,port...]\n"
17" --sports ...\n"
18" match source port(s)\n"
19" --destination-ports port[,port:port,port...]\n"
20" --dports ...\n"
21" match destination port(s)\n"
22" --ports port[,port:port,port]\n"
23" match both source and destination port(s)\n",
Harald Welte80fe35d2002-05-29 13:08:15 +000024IPTABLES_VERSION);
Andreas Ferber47caec02001-06-05 11:56:38 +000025}
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{
Harald Welteb4719762001-07-23 02:14:22 +000050 unsigned int portnum;
51
Harald Welte6b9e8f12001-10-15 15:47:06 +000052 if (string_to_number(port, 0, 65535, &portnum) != -1 ||
Andreas Ferber47caec02001-06-05 11:56:38 +000053 (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 void
61parse_multi_ports(const char *portstring, struct ipt_mport *minfo,
62 const char *proto)
63{
64 char *buffer, *cp, *next, *range;
65 unsigned int i;
66 u_int16_t m;
67
68 buffer = strdup(portstring);
69 if (!buffer) exit_error(OTHER_PROBLEM, "strdup failed");
70
71 minfo->pflags = 0;
72
73 for (cp=buffer, i=0, m=1; cp && i<IPT_MULTI_PORTS; cp=next,i++,m<<=1)
74 {
75 next=strchr(cp, ',');
76 if (next) *next++='\0';
77 range = strchr(cp, ':');
78 if (range) {
79 if (i == IPT_MULTI_PORTS-1)
80 exit_error(PARAMETER_PROBLEM,
81 "too many ports specified");
82 *range++ = '\0';
83 }
84 minfo->ports[i] = parse_port(cp, proto);
85 if (range) {
86 minfo->pflags |= m;
87 minfo->ports[++i] = parse_port(range, proto);
88 if (minfo->ports[i-1] >= minfo->ports[i])
89 exit_error(PARAMETER_PROBLEM,
90 "invalid portrange specified");
91 m <<= 1;
92 }
93 }
94 if (cp) exit_error(PARAMETER_PROBLEM, "too many ports specified");
95 if (i == IPT_MULTI_PORTS-1)
96 minfo->ports[i] = minfo->ports[i-1];
97 else if (i < IPT_MULTI_PORTS-1) {
98 minfo->ports[i] = ~0;
99 minfo->pflags |= 1<<i;
100 }
101 free(buffer);
102}
103
104/* Initialize the match. */
105static void
106init(struct ipt_entry_match *m, unsigned int *nfcache)
107{
108}
109
110static const char *
111check_proto(const struct ipt_entry *entry)
112{
113 if (entry->ip.proto == IPPROTO_TCP)
114 return "tcp";
115 else if (entry->ip.proto == IPPROTO_UDP)
116 return "udp";
117 else if (!entry->ip.proto)
118 exit_error(PARAMETER_PROBLEM,
119 "multiport needs `-p tcp' or `-p udp'");
120 else
121 exit_error(PARAMETER_PROBLEM,
122 "multiport only works with TCP or UDP");
123}
124
125/* Function which parses command options; returns true if it
126 ate an option */
127static int
128parse(int c, char **argv, int invert, unsigned int *flags,
129 const struct ipt_entry *entry,
130 unsigned int *nfcache,
131 struct ipt_entry_match **match)
132{
133 const char *proto;
134 struct ipt_mport *minfo
135 = (struct ipt_mport *)(*match)->data;
136
137 switch (c) {
138 case '1':
139 proto = check_proto(entry);
140 parse_multi_ports(argv[optind-1], minfo, proto);
141 minfo->flags = IPT_MPORT_SOURCE;
142 *nfcache |= NFC_IP_SRC_PT;
143 break;
144
145 case '2':
146 proto = check_proto(entry);
147 parse_multi_ports(argv[optind-1], minfo, proto);
148 minfo->flags = IPT_MPORT_DESTINATION;
149 *nfcache |= NFC_IP_DST_PT;
150 break;
151
152 case '3':
153 proto = check_proto(entry);
154 parse_multi_ports(argv[optind-1], minfo, proto);
155 minfo->flags = IPT_MPORT_EITHER;
156 *nfcache |= NFC_IP_SRC_PT | NFC_IP_DST_PT;
157 break;
158
159 default:
160 return 0;
161 }
162
163 if (*flags)
164 exit_error(PARAMETER_PROBLEM,
165 "multiport can only have one option");
166 *flags = 1;
167 return 1;
168}
169
170/* Final check; must specify something. */
171static void
172final_check(unsigned int flags)
173{
174 if (!flags)
175 exit_error(PARAMETER_PROBLEM, "mport expects an option");
176}
177
178static char *
179port_to_service(int port, u_int8_t proto)
180{
181 struct servent *service;
182
183 if ((service = getservbyport(htons(port),
184 proto == IPPROTO_TCP ? "tcp" : "udp")))
185 return service->s_name;
186
187 return NULL;
188}
189
190static void
191print_port(u_int16_t port, u_int8_t protocol, int numeric)
192{
193 char *service;
194
195 if (numeric || (service = port_to_service(port, protocol)) == NULL)
196 printf("%u", port);
197 else
198 printf("%s", service);
199}
200
201/* Prints out the matchinfo. */
202static void
203print(const struct ipt_ip *ip,
204 const struct ipt_entry_match *match,
205 int numeric)
206{
207 const struct ipt_mport *minfo
208 = (const struct ipt_mport *)match->data;
209 unsigned int i;
210 u_int16_t pflags = minfo->pflags;
211
212 printf("mport ");
213
214 switch (minfo->flags) {
215 case IPT_MPORT_SOURCE:
216 printf("sports ");
217 break;
218
219 case IPT_MPORT_DESTINATION:
220 printf("dports ");
221 break;
222
223 case IPT_MPORT_EITHER:
224 printf("ports ");
225 break;
226
227 default:
228 printf("ERROR ");
229 break;
230 }
231
232 for (i=0; i < IPT_MULTI_PORTS; i++) {
233 if (pflags & (1<<i)
234 && minfo->ports[i] == 65535)
235 break;
236 if (i == IPT_MULTI_PORTS-1
237 && minfo->ports[i-1] == minfo->ports[i])
238 break;
239 printf("%s", i ? "," : "");
240 print_port(minfo->ports[i], ip->proto, numeric);
241 if (pflags & (1<<i)) {
242 printf(":");
243 print_port(minfo->ports[++i], ip->proto, numeric);
244 }
245 }
246 printf(" ");
247}
248
249/* Saves the union ipt_matchinfo in parsable form to stdout. */
250static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
251{
252 const struct ipt_mport *minfo
253 = (const struct ipt_mport *)match->data;
254 unsigned int i;
255 u_int16_t pflags = minfo->pflags;
256
257 switch (minfo->flags) {
258 case IPT_MPORT_SOURCE:
259 printf("--sports ");
260 break;
261
262 case IPT_MPORT_DESTINATION:
263 printf("--dports ");
264 break;
265
266 case IPT_MPORT_EITHER:
267 printf("--ports ");
268 break;
269 }
270
271 for (i=0; i < IPT_MULTI_PORTS; i++) {
272 if (pflags & (1<<i)
Bob Hockneyf6ed1d62002-04-16 20:45:21 +0000273 && minfo->ports[i] == 65535)
Andreas Ferber47caec02001-06-05 11:56:38 +0000274 break;
275 if (i == IPT_MULTI_PORTS-1
276 && minfo->ports[i-1] == minfo->ports[i])
277 break;
278 printf("%s", i ? "," : "");
Thomas Woerner01cbaa62003-07-14 20:01:29 +0000279 print_port(minfo->ports[i], ip->proto, 1);
Andreas Ferber47caec02001-06-05 11:56:38 +0000280 if (pflags & (1<<i)) {
281 printf(":");
Thomas Woerner01cbaa62003-07-14 20:01:29 +0000282 print_port(minfo->ports[++i], ip->proto, 1);
Andreas Ferber47caec02001-06-05 11:56:38 +0000283 }
284 }
285 printf(" ");
286}
287
288struct iptables_match mport
289= { NULL,
290 "mport",
Harald Welte80fe35d2002-05-29 13:08:15 +0000291 IPTABLES_VERSION,
Andreas Ferber47caec02001-06-05 11:56:38 +0000292 IPT_ALIGN(sizeof(struct ipt_mport)),
293 IPT_ALIGN(sizeof(struct ipt_mport)),
294 &help,
295 &init,
296 &parse,
297 &final_check,
298 &print,
299 &save,
300 opts
301};
302
303void
304_init(void)
305{
306 register_match(&mport);
307}