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