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