Fork for IVI and add .changes file
[profile/ivi/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 <netinet/in.h>
8 #include <xtables.h>
9 #include <linux/netfilter/xt_tcpudp.h>
10
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] = xtables_parse_port(buffer, "tcp");
48         else {
49                 *cp = '\0';
50                 cp++;
51
52                 ports[0] = buffer[0] ? xtables_parse_port(buffer, "tcp") : 0;
53                 ports[1] = cp[0] ? xtables_parse_port(cp, "tcp") : 0xFFFF;
54
55                 if (ports[0] > ports[1])
56                         xtables_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; i < ARRAY_SIZE(tcp_flag_names); ++i)
90                         if (strcasecmp(tcp_flag_names[i].name, ptr) == 0) {
91                                 ret |= tcp_flag_names[i].flag;
92                                 break;
93                         }
94                 if (i == ARRAY_SIZE(tcp_flag_names))
95                         xtables_error(PARAMETER_PROBLEM,
96                                    "Unknown TCP flag `%s'", ptr);
97         }
98
99         free(buffer);
100         return ret;
101 }
102
103 static void
104 parse_tcp_flags(struct xt_tcp *tcpinfo,
105                 const char *mask,
106                 const char *cmp,
107                 int invert)
108 {
109         tcpinfo->flg_mask = parse_tcp_flag(mask);
110         tcpinfo->flg_cmp = parse_tcp_flag(cmp);
111
112         if (invert)
113                 tcpinfo->invflags |= XT_TCP_INV_FLAGS;
114 }
115
116 static void
117 parse_tcp_option(const char *option, u_int8_t *result)
118 {
119         unsigned int ret;
120
121         if (!xtables_strtoui(option, NULL, &ret, 1, UINT8_MAX))
122                 xtables_error(PARAMETER_PROBLEM, "Bad TCP option \"%s\"", option);
123
124         *result = ret;
125 }
126
127 static void tcp_init(struct xt_entry_match *m)
128 {
129         struct xt_tcp *tcpinfo = (struct xt_tcp *)m->data;
130
131         tcpinfo->spts[1] = tcpinfo->dpts[1] = 0xFFFF;
132 }
133
134 #define TCP_SRC_PORTS 0x01
135 #define TCP_DST_PORTS 0x02
136 #define TCP_FLAGS 0x04
137 #define TCP_OPTION      0x08
138
139 static int
140 tcp_parse(int c, char **argv, int invert, unsigned int *flags,
141           const void *entry, struct xt_entry_match **match)
142 {
143         struct xt_tcp *tcpinfo = (struct xt_tcp *)(*match)->data;
144
145         switch (c) {
146         case '1':
147                 if (*flags & TCP_SRC_PORTS)
148                         xtables_error(PARAMETER_PROBLEM,
149                                    "Only one `--source-port' allowed");
150                 xtables_check_inverse(optarg, &invert, &optind, 0, argv);
151                 parse_tcp_ports(optarg, tcpinfo->spts);
152                 if (invert)
153                         tcpinfo->invflags |= XT_TCP_INV_SRCPT;
154                 *flags |= TCP_SRC_PORTS;
155                 break;
156
157         case '2':
158                 if (*flags & TCP_DST_PORTS)
159                         xtables_error(PARAMETER_PROBLEM,
160                                    "Only one `--destination-port' allowed");
161                 xtables_check_inverse(optarg, &invert, &optind, 0, argv);
162                 parse_tcp_ports(optarg, tcpinfo->dpts);
163                 if (invert)
164                         tcpinfo->invflags |= XT_TCP_INV_DSTPT;
165                 *flags |= TCP_DST_PORTS;
166                 break;
167
168         case '3':
169                 if (*flags & TCP_FLAGS)
170                         xtables_error(PARAMETER_PROBLEM,
171                                    "Only one of `--syn' or `--tcp-flags' "
172                                    " allowed");
173                 parse_tcp_flags(tcpinfo, "SYN,RST,ACK,FIN", "SYN", invert);
174                 *flags |= TCP_FLAGS;
175                 break;
176
177         case '4':
178                 if (*flags & TCP_FLAGS)
179                         xtables_error(PARAMETER_PROBLEM,
180                                    "Only one of `--syn' or `--tcp-flags' "
181                                    " allowed");
182                 xtables_check_inverse(optarg, &invert, &optind, 0, argv);
183
184                 if (!argv[optind]
185                     || argv[optind][0] == '-' || argv[optind][0] == '!')
186                         xtables_error(PARAMETER_PROBLEM,
187                                    "--tcp-flags requires two args.");
188
189                 parse_tcp_flags(tcpinfo, optarg, argv[optind],
190                                 invert);
191                 optind++;
192                 *flags |= TCP_FLAGS;
193                 break;
194
195         case '5':
196                 if (*flags & TCP_OPTION)
197                         xtables_error(PARAMETER_PROBLEM,
198                                    "Only one `--tcp-option' allowed");
199                 xtables_check_inverse(optarg, &invert, &optind, 0, argv);
200                 parse_tcp_option(optarg, &tcpinfo->option);
201                 if (invert)
202                         tcpinfo->invflags |= XT_TCP_INV_OPTION;
203                 *flags |= TCP_OPTION;
204                 break;
205
206         default:
207                 return 0;
208         }
209
210         return 1;
211 }
212
213 static char *
214 port_to_service(int port)
215 {
216         struct servent *service;
217
218         if ((service = getservbyport(htons(port), "tcp")))
219                 return service->s_name;
220
221         return NULL;
222 }
223
224 static void
225 print_port(u_int16_t port, int numeric)
226 {
227         char *service;
228
229         if (numeric || (service = port_to_service(port)) == NULL)
230                 printf("%u", port);
231         else
232                 printf("%s", service);
233 }
234
235 static void
236 print_ports(const char *name, u_int16_t min, u_int16_t max,
237             int invert, int numeric)
238 {
239         const char *inv = invert ? "!" : "";
240
241         if (min != 0 || max != 0xFFFF || invert) {
242                 printf("%s", name);
243                 if (min == max) {
244                         printf(":%s", inv);
245                         print_port(min, numeric);
246                 } else {
247                         printf("s:%s", inv);
248                         print_port(min, numeric);
249                         printf(":");
250                         print_port(max, numeric);
251                 }
252                 printf(" ");
253         }
254 }
255
256 static void
257 print_option(u_int8_t option, int invert, int numeric)
258 {
259         if (option || invert)
260                 printf("option=%s%u ", invert ? "!" : "", option);
261 }
262
263 static void
264 print_tcpf(u_int8_t flags)
265 {
266         int have_flag = 0;
267
268         while (flags) {
269                 unsigned int i;
270
271                 for (i = 0; (flags & tcp_flag_names[i].flag) == 0; i++);
272
273                 if (have_flag)
274                         printf(",");
275                 printf("%s", tcp_flag_names[i].name);
276                 have_flag = 1;
277
278                 flags &= ~tcp_flag_names[i].flag;
279         }
280
281         if (!have_flag)
282                 printf("NONE");
283 }
284
285 static void
286 print_flags(u_int8_t mask, u_int8_t cmp, int invert, int numeric)
287 {
288         if (mask || invert) {
289                 printf("flags:%s", invert ? "!" : "");
290                 if (numeric)
291                         printf("0x%02X/0x%02X ", mask, cmp);
292                 else {
293                         print_tcpf(mask);
294                         printf("/");
295                         print_tcpf(cmp);
296                         printf(" ");
297                 }
298         }
299 }
300
301 static void
302 tcp_print(const void *ip, const struct xt_entry_match *match, int numeric)
303 {
304         const struct xt_tcp *tcp = (struct xt_tcp *)match->data;
305
306         printf("tcp ");
307         print_ports("spt", tcp->spts[0], tcp->spts[1],
308                     tcp->invflags & XT_TCP_INV_SRCPT,
309                     numeric);
310         print_ports("dpt", tcp->dpts[0], tcp->dpts[1],
311                     tcp->invflags & XT_TCP_INV_DSTPT,
312                     numeric);
313         print_option(tcp->option,
314                      tcp->invflags & XT_TCP_INV_OPTION,
315                      numeric);
316         print_flags(tcp->flg_mask, tcp->flg_cmp,
317                     tcp->invflags & XT_TCP_INV_FLAGS,
318                     numeric);
319         if (tcp->invflags & ~XT_TCP_INV_MASK)
320                 printf("Unknown invflags: 0x%X ",
321                        tcp->invflags & ~XT_TCP_INV_MASK);
322 }
323
324 static void tcp_save(const void *ip, const struct xt_entry_match *match)
325 {
326         const struct xt_tcp *tcpinfo = (struct xt_tcp *)match->data;
327
328         if (tcpinfo->spts[0] != 0
329             || tcpinfo->spts[1] != 0xFFFF) {
330                 if (tcpinfo->invflags & XT_TCP_INV_SRCPT)
331                         printf("! ");
332                 if (tcpinfo->spts[0]
333                     != tcpinfo->spts[1])
334                         printf("--sport %u:%u ",
335                                tcpinfo->spts[0],
336                                tcpinfo->spts[1]);
337                 else
338                         printf("--sport %u ",
339                                tcpinfo->spts[0]);
340         }
341
342         if (tcpinfo->dpts[0] != 0
343             || tcpinfo->dpts[1] != 0xFFFF) {
344                 if (tcpinfo->invflags & XT_TCP_INV_DSTPT)
345                         printf("! ");
346                 if (tcpinfo->dpts[0]
347                     != tcpinfo->dpts[1])
348                         printf("--dport %u:%u ",
349                                tcpinfo->dpts[0],
350                                tcpinfo->dpts[1]);
351                 else
352                         printf("--dport %u ",
353                                tcpinfo->dpts[0]);
354         }
355
356         if (tcpinfo->option
357             || (tcpinfo->invflags & XT_TCP_INV_OPTION)) {
358                 if (tcpinfo->invflags & XT_TCP_INV_OPTION)
359                         printf("! ");
360                 printf("--tcp-option %u ", tcpinfo->option);
361         }
362
363         if (tcpinfo->flg_mask
364             || (tcpinfo->invflags & XT_TCP_INV_FLAGS)) {
365                 if (tcpinfo->invflags & XT_TCP_INV_FLAGS)
366                         printf("! ");
367                 printf("--tcp-flags ");
368                 if (tcpinfo->flg_mask != 0xFF) {
369                         print_tcpf(tcpinfo->flg_mask);
370                 }
371                 printf(" ");
372                 print_tcpf(tcpinfo->flg_cmp);
373                 printf(" ");
374         }
375 }
376
377 static struct xtables_match tcp_match = {
378         .family         = NFPROTO_UNSPEC,
379         .name           = "tcp",
380         .version        = XTABLES_VERSION,
381         .size           = XT_ALIGN(sizeof(struct xt_tcp)),
382         .userspacesize  = XT_ALIGN(sizeof(struct xt_tcp)),
383         .help           = tcp_help,
384         .init           = tcp_init,
385         .parse          = tcp_parse,
386         .print          = tcp_print,
387         .save           = tcp_save,
388         .extra_opts     = tcp_opts,
389 };
390
391 void
392 _init(void)
393 {
394         xtables_register_match(&tcp_match);
395 }