Sapan says vnet_tun is obsolete.
[iptables.git] / extensions / libxt_tcp.c
1 /* Shared library add-on to iptables to add TCP support. */
2 #include <stdio.h>
3 #include <netdb.h>
4 #include <string.h>
5 #include <stdlib.h>
6 #include <getopt.h>
7 #include <xtables.h>
8 #include <linux/netfilter/xt_tcpudp.h>
9
10 /* Function which prints out usage message. */
11 static void tcp_help(void)
12 {
13         printf(
14 "tcp match options:\n"
15 " --tcp-flags [!] mask comp     match when TCP flags & mask == comp\n"
16 "                               (Flags: SYN ACK FIN RST URG PSH ALL NONE)\n"
17 "[!] --syn                      match when only SYN flag set\n"
18 "                               (equivalent to --tcp-flags SYN,RST,ACK,FIN SYN)\n"
19 " --source-port [!] port[:port]\n"
20 " --sport ...\n"
21 "                               match source port(s)\n"
22 " --destination-port [!] port[:port]\n"
23 " --dport ...\n"
24 "                               match destination port(s)\n"
25 " --tcp-option [!] number       match if TCP option set\n");
26 }
27
28 static const struct option tcp_opts[] = {
29         { "source-port", 1, NULL, '1' },
30         { "sport", 1, NULL, '1' }, /* synonym */
31         { "destination-port", 1, NULL, '2' },
32         { "dport", 1, NULL, '2' }, /* synonym */
33         { "syn", 0, NULL, '3' },
34         { "tcp-flags", 1, NULL, '4' },
35         { "tcp-option", 1, NULL, '5' },
36         { .name = NULL }
37 };
38
39 static void
40 parse_tcp_ports(const char *portstring, u_int16_t *ports)
41 {
42         char *buffer;
43         char *cp;
44
45         buffer = strdup(portstring);
46         if ((cp = strchr(buffer, ':')) == NULL)
47                 ports[0] = ports[1] = parse_port(buffer, "tcp");
48         else {
49                 *cp = '\0';
50                 cp++;
51
52                 ports[0] = buffer[0] ? parse_port(buffer, "tcp") : 0;
53                 ports[1] = cp[0] ? parse_port(cp, "tcp") : 0xFFFF;
54
55                 if (ports[0] > ports[1])
56                         exit_error(PARAMETER_PROBLEM,
57                                    "invalid portrange (min > max)");
58         }
59         free(buffer);
60 }
61
62 struct tcp_flag_names {
63         const char *name;
64         unsigned int flag;
65 };
66
67 static const struct tcp_flag_names tcp_flag_names[]
68 = { { "FIN", 0x01 },
69     { "SYN", 0x02 },
70     { "RST", 0x04 },
71     { "PSH", 0x08 },
72     { "ACK", 0x10 },
73     { "URG", 0x20 },
74     { "ALL", 0x3F },
75     { "NONE", 0 },
76 };
77
78 static unsigned int
79 parse_tcp_flag(const char *flags)
80 {
81         unsigned int ret = 0;
82         char *ptr;
83         char *buffer;
84
85         buffer = strdup(flags);
86
87         for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) {
88                 unsigned int i;
89                 for (i = 0;
90                      i < sizeof(tcp_flag_names)/sizeof(struct tcp_flag_names);
91                      i++) {
92                         if (strcasecmp(tcp_flag_names[i].name, ptr) == 0) {
93                                 ret |= tcp_flag_names[i].flag;
94                                 break;
95                         }
96                 }
97                 if (i == sizeof(tcp_flag_names)/sizeof(struct tcp_flag_names))
98                         exit_error(PARAMETER_PROBLEM,
99                                    "Unknown TCP flag `%s'", ptr);
100                 }
101
102         free(buffer);
103         return ret;
104 }
105
106 static void
107 parse_tcp_flags(struct xt_tcp *tcpinfo,
108                 const char *mask,
109                 const char *cmp,
110                 int invert)
111 {
112         tcpinfo->flg_mask = parse_tcp_flag(mask);
113         tcpinfo->flg_cmp = parse_tcp_flag(cmp);
114
115         if (invert)
116                 tcpinfo->invflags |= XT_TCP_INV_FLAGS;
117 }
118
119 static void
120 parse_tcp_option(const char *option, u_int8_t *result)
121 {
122         unsigned int ret;
123
124         if (string_to_number(option, 1, 255, &ret) == -1)
125                 exit_error(PARAMETER_PROBLEM, "Bad TCP option `%s'", option);
126
127         *result = (u_int8_t)ret;
128 }
129
130 /* Initialize the match. */
131 static void tcp_init(struct xt_entry_match *m)
132 {
133         struct xt_tcp *tcpinfo = (struct xt_tcp *)m->data;
134
135         tcpinfo->spts[1] = tcpinfo->dpts[1] = 0xFFFF;
136 }
137
138 #define TCP_SRC_PORTS 0x01
139 #define TCP_DST_PORTS 0x02
140 #define TCP_FLAGS 0x04
141 #define TCP_OPTION      0x08
142
143 /* Function which parses command options; returns true if it
144    ate an option. */
145 static int
146 tcp_parse(int c, char **argv, int invert, unsigned int *flags,
147           const void *entry, struct xt_entry_match **match)
148 {
149         struct xt_tcp *tcpinfo = (struct xt_tcp *)(*match)->data;
150
151         switch (c) {
152         case '1':
153                 if (*flags & TCP_SRC_PORTS)
154                         exit_error(PARAMETER_PROBLEM,
155                                    "Only one `--source-port' allowed");
156                 check_inverse(optarg, &invert, &optind, 0);
157                 parse_tcp_ports(argv[optind-1], tcpinfo->spts);
158                 if (invert)
159                         tcpinfo->invflags |= XT_TCP_INV_SRCPT;
160                 *flags |= TCP_SRC_PORTS;
161                 break;
162
163         case '2':
164                 if (*flags & TCP_DST_PORTS)
165                         exit_error(PARAMETER_PROBLEM,
166                                    "Only one `--destination-port' allowed");
167                 check_inverse(optarg, &invert, &optind, 0);
168                 parse_tcp_ports(argv[optind-1], tcpinfo->dpts);
169                 if (invert)
170                         tcpinfo->invflags |= XT_TCP_INV_DSTPT;
171                 *flags |= TCP_DST_PORTS;
172                 break;
173
174         case '3':
175                 if (*flags & TCP_FLAGS)
176                         exit_error(PARAMETER_PROBLEM,
177                                    "Only one of `--syn' or `--tcp-flags' "
178                                    " allowed");
179                 parse_tcp_flags(tcpinfo, "SYN,RST,ACK,FIN", "SYN", invert);
180                 *flags |= TCP_FLAGS;
181                 break;
182
183         case '4':
184                 if (*flags & TCP_FLAGS)
185                         exit_error(PARAMETER_PROBLEM,
186                                    "Only one of `--syn' or `--tcp-flags' "
187                                    " allowed");
188                 check_inverse(optarg, &invert, &optind, 0);
189
190                 if (!argv[optind]
191                     || argv[optind][0] == '-' || argv[optind][0] == '!')
192                         exit_error(PARAMETER_PROBLEM,
193                                    "--tcp-flags requires two args.");
194
195                 parse_tcp_flags(tcpinfo, argv[optind-1], argv[optind],
196                                 invert);
197                 optind++;
198                 *flags |= TCP_FLAGS;
199                 break;
200
201         case '5':
202                 if (*flags & TCP_OPTION)
203                         exit_error(PARAMETER_PROBLEM,
204                                    "Only one `--tcp-option' allowed");
205                 check_inverse(optarg, &invert, &optind, 0);
206                 parse_tcp_option(argv[optind-1], &tcpinfo->option);
207                 if (invert)
208                         tcpinfo->invflags |= XT_TCP_INV_OPTION;
209                 *flags |= TCP_OPTION;
210                 break;
211
212         default:
213                 return 0;
214         }
215
216         return 1;
217 }
218
219 static char *
220 port_to_service(int port)
221 {
222         struct servent *service;
223
224         if ((service = getservbyport(htons(port), "tcp")))
225                 return service->s_name;
226
227         return NULL;
228 }
229
230 static void
231 print_port(u_int16_t port, int numeric)
232 {
233         char *service;
234
235         if (numeric || (service = port_to_service(port)) == NULL)
236                 printf("%u", port);
237         else
238                 printf("%s", service);
239 }
240
241 static void
242 print_ports(const char *name, u_int16_t min, u_int16_t max,
243             int invert, int numeric)
244 {
245         const char *inv = invert ? "!" : "";
246
247         if (min != 0 || max != 0xFFFF || invert) {
248                 printf("%s", name);
249                 if (min == max) {
250                         printf(":%s", inv);
251                         print_port(min, numeric);
252                 } else {
253                         printf("s:%s", inv);
254                         print_port(min, numeric);
255                         printf(":");
256                         print_port(max, numeric);
257                 }
258                 printf(" ");
259         }
260 }
261
262 static void
263 print_option(u_int8_t option, int invert, int numeric)
264 {
265         if (option || invert)
266                 printf("option=%s%u ", invert ? "!" : "", option);
267 }
268
269 static void
270 print_tcpf(u_int8_t flags)
271 {
272         int have_flag = 0;
273
274         while (flags) {
275                 unsigned int i;
276
277                 for (i = 0; (flags & tcp_flag_names[i].flag) == 0; i++);
278
279                 if (have_flag)
280                         printf(",");
281                 printf("%s", tcp_flag_names[i].name);
282                 have_flag = 1;
283
284                 flags &= ~tcp_flag_names[i].flag;
285         }
286
287         if (!have_flag)
288                 printf("NONE");
289 }
290
291 static void
292 print_flags(u_int8_t mask, u_int8_t cmp, int invert, int numeric)
293 {
294         if (mask || invert) {
295                 printf("flags:%s", invert ? "!" : "");
296                 if (numeric)
297                         printf("0x%02X/0x%02X ", mask, cmp);
298                 else {
299                         print_tcpf(mask);
300                         printf("/");
301                         print_tcpf(cmp);
302                         printf(" ");
303                 }
304         }
305 }
306
307 /* Prints out the union ipt_matchinfo. */
308 static void
309 tcp_print(const void *ip, const struct xt_entry_match *match, int numeric)
310 {
311         const struct xt_tcp *tcp = (struct xt_tcp *)match->data;
312
313         printf("tcp ");
314         print_ports("spt", tcp->spts[0], tcp->spts[1],
315                     tcp->invflags & XT_TCP_INV_SRCPT,
316                     numeric);
317         print_ports("dpt", tcp->dpts[0], tcp->dpts[1],
318                     tcp->invflags & XT_TCP_INV_DSTPT,
319                     numeric);
320         print_option(tcp->option,
321                      tcp->invflags & XT_TCP_INV_OPTION,
322                      numeric);
323         print_flags(tcp->flg_mask, tcp->flg_cmp,
324                     tcp->invflags & XT_TCP_INV_FLAGS,
325                     numeric);
326         if (tcp->invflags & ~XT_TCP_INV_MASK)
327                 printf("Unknown invflags: 0x%X ",
328                        tcp->invflags & ~XT_TCP_INV_MASK);
329 }
330
331 /* Saves the union ipt_matchinfo in parsable form to stdout. */
332 static void tcp_save(const void *ip, const struct xt_entry_match *match)
333 {
334         const struct xt_tcp *tcpinfo = (struct xt_tcp *)match->data;
335
336         if (tcpinfo->spts[0] != 0
337             || tcpinfo->spts[1] != 0xFFFF) {
338                 if (tcpinfo->invflags & XT_TCP_INV_SRCPT)
339                         printf("! ");
340                 if (tcpinfo->spts[0]
341                     != tcpinfo->spts[1])
342                         printf("--sport %u:%u ",
343                                tcpinfo->spts[0],
344                                tcpinfo->spts[1]);
345                 else
346                         printf("--sport %u ",
347                                tcpinfo->spts[0]);
348         }
349
350         if (tcpinfo->dpts[0] != 0
351             || tcpinfo->dpts[1] != 0xFFFF) {
352                 if (tcpinfo->invflags & XT_TCP_INV_DSTPT)
353                         printf("! ");
354                 if (tcpinfo->dpts[0]
355                     != tcpinfo->dpts[1])
356                         printf("--dport %u:%u ",
357                                tcpinfo->dpts[0],
358                                tcpinfo->dpts[1]);
359                 else
360                         printf("--dport %u ",
361                                tcpinfo->dpts[0]);
362         }
363
364         if (tcpinfo->option
365             || (tcpinfo->invflags & XT_TCP_INV_OPTION)) {
366                 if (tcpinfo->invflags & XT_TCP_INV_OPTION)
367                         printf("! ");
368                 printf("--tcp-option %u ", tcpinfo->option);
369         }
370
371         if (tcpinfo->flg_mask
372             || (tcpinfo->invflags & XT_TCP_INV_FLAGS)) {
373                 if (tcpinfo->invflags & XT_TCP_INV_FLAGS)
374                         printf("! ");
375                 printf("--tcp-flags ");
376                 if (tcpinfo->flg_mask != 0xFF) {
377                         print_tcpf(tcpinfo->flg_mask);
378                 }
379                 printf(" ");
380                 print_tcpf(tcpinfo->flg_cmp);
381                 printf(" ");
382         }
383 }
384
385 static struct xtables_match tcp_match = {
386         .family         = AF_INET,
387         .name           = "tcp",
388         .version        = XTABLES_VERSION,
389         .size           = XT_ALIGN(sizeof(struct xt_tcp)),
390         .userspacesize  = XT_ALIGN(sizeof(struct xt_tcp)),
391         .help           = tcp_help,
392         .init           = tcp_init,
393         .parse          = tcp_parse,
394         .print          = tcp_print,
395         .save           = tcp_save,
396         .extra_opts     = tcp_opts,
397 };
398
399 static struct xtables_match tcp_match6 = {
400         .family         = AF_INET6,
401         .name           = "tcp",
402         .version        = XTABLES_VERSION,
403         .size           = XT_ALIGN(sizeof(struct xt_tcp)),
404         .userspacesize  = XT_ALIGN(sizeof(struct xt_tcp)),
405         .help           = tcp_help,
406         .init           = tcp_init,
407         .parse          = tcp_parse,
408         .print          = tcp_print,
409         .save           = tcp_save,
410         .extra_opts     = tcp_opts,
411 };
412
413 void
414 _init(void)
415 {
416         xtables_register_match(&tcp_match);
417         xtables_register_match(&tcp_match6);
418 }