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