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