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