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