iptables-1.2.9-2.3.1.src.rpm
[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                 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. */
171 static void
172 final_check(unsigned int flags)
173 {
174         if (!flags)
175                 exit_error(PARAMETER_PROBLEM, "mport expects an option");
176 }
177
178 static char *
179 port_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
190 static void
191 print_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. */
202 static void
203 print(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. */
250 static 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)
273                     && minfo->ports[i] == 65535)
274                         break;
275                 if (i == IPT_MULTI_PORTS-1
276                     && minfo->ports[i-1] == minfo->ports[i])
277                         break;
278                 printf("%s", i ? "," : "");
279                 print_port(minfo->ports[i], ip->proto, 1);
280                 if (pflags & (1<<i)) {
281                         printf(":");
282                         print_port(minfo->ports[++i], ip->proto, 1);
283                 }
284         }
285         printf(" ");
286 }
287
288 struct iptables_match mport
289 = { NULL,
290     "mport",
291     IPTABLES_VERSION,
292     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
303 void
304 _init(void)
305 {
306         register_match(&mport);
307 }