libxt_tcp.c revision 0e2abed11985e16215559cefd90625f99317b96c
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, NULL, '1' }, 32 { "sport", 1, NULL, '1' }, /* synonym */ 33 { "destination-port", 1, NULL, '2' }, 34 { "dport", 1, NULL, '2' }, /* synonym */ 35 { "syn", 0, NULL, '3' }, 36 { "tcp-flags", 1, NULL, '4' }, 37 { "tcp-option", 1, NULL, '5' }, 38 { } 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 const 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 223static char * 224port_to_service(int port) 225{ 226 struct servent *service; 227 228 if ((service = getservbyport(htons(port), "tcp"))) 229 return service->s_name; 230 231 return NULL; 232} 233 234static void 235print_port(u_int16_t port, int numeric) 236{ 237 char *service; 238 239 if (numeric || (service = port_to_service(port)) == NULL) 240 printf("%u", port); 241 else 242 printf("%s", service); 243} 244 245static void 246print_ports(const char *name, u_int16_t min, u_int16_t max, 247 int invert, int numeric) 248{ 249 const char *inv = invert ? "!" : ""; 250 251 if (min != 0 || max != 0xFFFF || invert) { 252 printf("%s", name); 253 if (min == max) { 254 printf(":%s", inv); 255 print_port(min, numeric); 256 } else { 257 printf("s:%s", inv); 258 print_port(min, numeric); 259 printf(":"); 260 print_port(max, numeric); 261 } 262 printf(" "); 263 } 264} 265 266static void 267print_option(u_int8_t option, int invert, int numeric) 268{ 269 if (option || invert) 270 printf("option=%s%u ", invert ? "!" : "", option); 271} 272 273static void 274print_tcpf(u_int8_t flags) 275{ 276 int have_flag = 0; 277 278 while (flags) { 279 unsigned int i; 280 281 for (i = 0; (flags & tcp_flag_names[i].flag) == 0; i++); 282 283 if (have_flag) 284 printf(","); 285 printf("%s", tcp_flag_names[i].name); 286 have_flag = 1; 287 288 flags &= ~tcp_flag_names[i].flag; 289 } 290 291 if (!have_flag) 292 printf("NONE"); 293} 294 295static void 296print_flags(u_int8_t mask, u_int8_t cmp, int invert, int numeric) 297{ 298 if (mask || invert) { 299 printf("flags:%s", invert ? "!" : ""); 300 if (numeric) 301 printf("0x%02X/0x%02X ", mask, cmp); 302 else { 303 print_tcpf(mask); 304 printf("/"); 305 print_tcpf(cmp); 306 printf(" "); 307 } 308 } 309} 310 311/* Prints out the union ipt_matchinfo. */ 312static void 313print(const void *ip, 314 const struct xt_entry_match *match, int numeric) 315{ 316 const struct xt_tcp *tcp = (struct xt_tcp *)match->data; 317 318 printf("tcp "); 319 print_ports("spt", tcp->spts[0], tcp->spts[1], 320 tcp->invflags & XT_TCP_INV_SRCPT, 321 numeric); 322 print_ports("dpt", tcp->dpts[0], tcp->dpts[1], 323 tcp->invflags & XT_TCP_INV_DSTPT, 324 numeric); 325 print_option(tcp->option, 326 tcp->invflags & XT_TCP_INV_OPTION, 327 numeric); 328 print_flags(tcp->flg_mask, tcp->flg_cmp, 329 tcp->invflags & XT_TCP_INV_FLAGS, 330 numeric); 331 if (tcp->invflags & ~XT_TCP_INV_MASK) 332 printf("Unknown invflags: 0x%X ", 333 tcp->invflags & ~XT_TCP_INV_MASK); 334} 335 336/* Saves the union ipt_matchinfo in parsable form to stdout. */ 337static void save(const void *ip, const struct xt_entry_match *match) 338{ 339 const struct xt_tcp *tcpinfo = (struct xt_tcp *)match->data; 340 341 if (tcpinfo->spts[0] != 0 342 || tcpinfo->spts[1] != 0xFFFF) { 343 if (tcpinfo->invflags & XT_TCP_INV_SRCPT) 344 printf("! "); 345 if (tcpinfo->spts[0] 346 != tcpinfo->spts[1]) 347 printf("--sport %u:%u ", 348 tcpinfo->spts[0], 349 tcpinfo->spts[1]); 350 else 351 printf("--sport %u ", 352 tcpinfo->spts[0]); 353 } 354 355 if (tcpinfo->dpts[0] != 0 356 || tcpinfo->dpts[1] != 0xFFFF) { 357 if (tcpinfo->invflags & XT_TCP_INV_DSTPT) 358 printf("! "); 359 if (tcpinfo->dpts[0] 360 != tcpinfo->dpts[1]) 361 printf("--dport %u:%u ", 362 tcpinfo->dpts[0], 363 tcpinfo->dpts[1]); 364 else 365 printf("--dport %u ", 366 tcpinfo->dpts[0]); 367 } 368 369 if (tcpinfo->option 370 || (tcpinfo->invflags & XT_TCP_INV_OPTION)) { 371 if (tcpinfo->invflags & XT_TCP_INV_OPTION) 372 printf("! "); 373 printf("--tcp-option %u ", tcpinfo->option); 374 } 375 376 if (tcpinfo->flg_mask 377 || (tcpinfo->invflags & XT_TCP_INV_FLAGS)) { 378 if (tcpinfo->invflags & XT_TCP_INV_FLAGS) 379 printf("! "); 380 printf("--tcp-flags "); 381 if (tcpinfo->flg_mask != 0xFF) { 382 print_tcpf(tcpinfo->flg_mask); 383 } 384 printf(" "); 385 print_tcpf(tcpinfo->flg_cmp); 386 printf(" "); 387 } 388} 389 390static struct xtables_match tcp = { 391 .family = AF_INET, 392 .name = "tcp", 393 .version = IPTABLES_VERSION, 394 .size = XT_ALIGN(sizeof(struct xt_tcp)), 395 .userspacesize = XT_ALIGN(sizeof(struct xt_tcp)), 396 .help = &help, 397 .init = &init, 398 .parse = &parse, 399 .print = &print, 400 .save = &save, 401 .extra_opts = opts, 402}; 403 404static struct xtables_match tcp6 = { 405 .family = AF_INET6, 406 .name = "tcp", 407 .version = IPTABLES_VERSION, 408 .size = XT_ALIGN(sizeof(struct xt_tcp)), 409 .userspacesize = XT_ALIGN(sizeof(struct xt_tcp)), 410 .help = &help, 411 .init = &init, 412 .parse = &parse, 413 .print = &print, 414 .save = &save, 415 .extra_opts = opts, 416}; 417 418void 419_init(void) 420{ 421 xtables_register_match(&tcp); 422 xtables_register_match(&tcp6); 423} 424