libxt_tcp.c revision bf97128c7262f17a02fec41cdae75b472ba77f88
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 11static 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 28static 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 39static void 40parse_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 62struct tcp_flag_names { 63 const char *name; 64 unsigned int flag; 65}; 66 67static 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 78static unsigned int 79parse_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 103static void 104parse_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 116static void 117parse_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 127static 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 139static int 140tcp_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(argv[optind-1], 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(argv[optind-1], 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, argv[optind-1], 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(argv[optind-1], &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 213static char * 214port_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 224static void 225print_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 235static void 236print_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 256static void 257print_option(u_int8_t option, int invert, int numeric) 258{ 259 if (option || invert) 260 printf("option=%s%u ", invert ? "!" : "", option); 261} 262 263static void 264print_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 285static void 286print_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 301static void 302tcp_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 324static 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 377static 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 391void 392_init(void) 393{ 394 xtables_register_match(&tcp_match); 395} 396