iptables-1.3.2-20050720
[iptables.git] / extensions / libipt_mport.c
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_mport.h>
9
10 /* Function which prints out usage message. */
11 static void
12 help(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",
24 IPTABLES_VERSION);
25 }
26
27 static 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
36 static int
37 service_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
47 static u_int16_t
48 parse_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
60 static void
61 parse_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. */
105 static void
106 init(struct ipt_entry_match *m, unsigned int *nfcache)
107 {
108 }
109
110 static const char *
111 check_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 */
127 static int
128 parse(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                 check_inverse(argv[optind-1], &invert, &optind, 0);
140                 proto = check_proto(entry);
141                 parse_multi_ports(argv[optind-1], minfo, proto);
142                 minfo->flags = IPT_MPORT_SOURCE;
143                 break;
144
145         case '2':
146                 check_inverse(argv[optind-1], &invert, &optind, 0);
147                 proto = check_proto(entry);
148                 parse_multi_ports(argv[optind-1], minfo, proto);
149                 minfo->flags = IPT_MPORT_DESTINATION;
150                 break;
151
152         case '3':
153                 check_inverse(argv[optind-1], &invert, &optind, 0);
154                 proto = check_proto(entry);
155                 parse_multi_ports(argv[optind-1], minfo, proto);
156                 minfo->flags = IPT_MPORT_EITHER;
157                 break;
158
159         default:
160                 return 0;
161         }
162
163         if (invert)
164                 exit_error(PARAMETER_PROBLEM,
165                            "multiport does not support invert");
166
167         if (*flags)
168                 exit_error(PARAMETER_PROBLEM,
169                            "multiport can only have one option");
170         *flags = 1;
171         return 1;
172 }
173
174 /* Final check; must specify something. */
175 static void
176 final_check(unsigned int flags)
177 {
178         if (!flags)
179                 exit_error(PARAMETER_PROBLEM, "mport expects an option");
180 }
181
182 static char *
183 port_to_service(int port, u_int8_t proto)
184 {
185         struct servent *service;
186
187         if ((service = getservbyport(htons(port),
188                                      proto == IPPROTO_TCP ? "tcp" : "udp")))
189                 return service->s_name;
190
191         return NULL;
192 }
193
194 static void
195 print_port(u_int16_t port, u_int8_t protocol, int numeric)
196 {
197         char *service;
198
199         if (numeric || (service = port_to_service(port, protocol)) == NULL)
200                 printf("%u", port);
201         else
202                 printf("%s", service);
203 }
204
205 /* Prints out the matchinfo. */
206 static void
207 print(const struct ipt_ip *ip,
208       const struct ipt_entry_match *match,
209       int numeric)
210 {
211         const struct ipt_mport *minfo
212                 = (const struct ipt_mport *)match->data;
213         unsigned int i;
214         u_int16_t pflags = minfo->pflags;
215
216         printf("mport ");
217
218         switch (minfo->flags) {
219         case IPT_MPORT_SOURCE:
220                 printf("sports ");
221                 break;
222
223         case IPT_MPORT_DESTINATION:
224                 printf("dports ");
225                 break;
226
227         case IPT_MPORT_EITHER:
228                 printf("ports ");
229                 break;
230
231         default:
232                 printf("ERROR ");
233                 break;
234         }
235
236         for (i=0; i < IPT_MULTI_PORTS; i++) {
237                 if (pflags & (1<<i)
238                     && minfo->ports[i] == 65535)
239                         break;
240                 if (i == IPT_MULTI_PORTS-1
241                     && minfo->ports[i-1] == minfo->ports[i])
242                         break;
243                 printf("%s", i ? "," : "");
244                 print_port(minfo->ports[i], ip->proto, numeric);
245                 if (pflags & (1<<i)) {
246                         printf(":");
247                         print_port(minfo->ports[++i], ip->proto, numeric);
248                 }
249         }
250         printf(" ");
251 }
252
253 /* Saves the union ipt_matchinfo in parsable form to stdout. */
254 static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
255 {
256         const struct ipt_mport *minfo
257                 = (const struct ipt_mport *)match->data;
258         unsigned int i;
259         u_int16_t pflags = minfo->pflags;
260
261         switch (minfo->flags) {
262         case IPT_MPORT_SOURCE:
263                 printf("--sports ");
264                 break;
265
266         case IPT_MPORT_DESTINATION:
267                 printf("--dports ");
268                 break;
269
270         case IPT_MPORT_EITHER:
271                 printf("--ports ");
272                 break;
273         }
274
275         for (i=0; i < IPT_MULTI_PORTS; i++) {
276                 if (pflags & (1<<i)
277                     && minfo->ports[i] == 65535)
278                         break;
279                 if (i == IPT_MULTI_PORTS-1
280                     && minfo->ports[i-1] == minfo->ports[i])
281                         break;
282                 printf("%s", i ? "," : "");
283                 print_port(minfo->ports[i], ip->proto, 1);
284                 if (pflags & (1<<i)) {
285                         printf(":");
286                         print_port(minfo->ports[++i], ip->proto, 1);
287                 }
288         }
289         printf(" ");
290 }
291
292 static struct iptables_match mport = { 
293         .next           = NULL,
294         .name           = "mport",
295         .version        = IPTABLES_VERSION,
296         .size           = IPT_ALIGN(sizeof(struct ipt_mport)),
297         .userspacesize  = IPT_ALIGN(sizeof(struct ipt_mport)),
298         .help           = &help,
299         .init           = &init,
300         .parse          = &parse,
301         .final_check    = &final_check,
302         .print          = &print,
303         .save           = &save,
304         .extra_opts     = opts
305 };
306
307 void
308 _init(void)
309 {
310         register_match(&mport);
311 }