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 <ip6tables.h> 8 #include <linux/netfilter_ipv6/ip6_tables.h> 9 10 /* Function which prints out usage message. */ 11 static void 12 help(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", 27 IPTABLES_VERSION); 28 } 29 30 static 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 41 static void 42 parse_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 64 struct tcp_flag_names { 65 const char *name; 66 unsigned int flag; 67 }; 68 69 static 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 80 static unsigned int 81 parse_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 108 static void 109 parse_tcp_flags(struct ip6t_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 |= IP6T_TCP_INV_FLAGS; 119 } 120 121 static void 122 parse_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. */ 133 static void 134 init(struct ip6t_entry_match *m, unsigned int *nfcache) 135 { 136 struct ip6t_tcp *tcpinfo = (struct ip6t_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. */ 148 static int 149 parse(int c, char **argv, int invert, unsigned int *flags, 150 const struct ip6t_entry *entry, 151 unsigned int *nfcache, 152 struct ip6t_entry_match **match) 153 { 154 struct ip6t_tcp *tcpinfo = (struct ip6t_tcp *)(*match)->data; 155 156 switch (c) { 157 case '1': 158 if (*flags & TCP_SRC_PORTS) 159 exit_error(PARAMETER_PROBLEM, 160 "Only one `--source-port' allowed"); 161 check_inverse(optarg, &invert, &optind, 0); 162 parse_tcp_ports(argv[optind-1], tcpinfo->spts); 163 if (invert) 164 tcpinfo->invflags |= IP6T_TCP_INV_SRCPT; 165 *flags |= TCP_SRC_PORTS; 166 break; 167 168 case '2': 169 if (*flags & TCP_DST_PORTS) 170 exit_error(PARAMETER_PROBLEM, 171 "Only one `--destination-port' allowed"); 172 check_inverse(optarg, &invert, &optind, 0); 173 parse_tcp_ports(argv[optind-1], tcpinfo->dpts); 174 if (invert) 175 tcpinfo->invflags |= IP6T_TCP_INV_DSTPT; 176 *flags |= TCP_DST_PORTS; 177 break; 178 179 case '3': 180 if (*flags & TCP_FLAGS) 181 exit_error(PARAMETER_PROBLEM, 182 "Only one of `--syn' or `--tcp-flags' " 183 " allowed"); 184 parse_tcp_flags(tcpinfo, "SYN,RST,ACK", "SYN", invert); 185 *flags |= TCP_FLAGS; 186 break; 187 188 case '4': 189 if (*flags & TCP_FLAGS) 190 exit_error(PARAMETER_PROBLEM, 191 "Only one of `--syn' or `--tcp-flags' " 192 " allowed"); 193 check_inverse(optarg, &invert, &optind, 0); 194 195 if (!argv[optind] 196 || argv[optind][0] == '-' || argv[optind][0] == '!') 197 exit_error(PARAMETER_PROBLEM, 198 "--tcp-flags requires two args."); 199 200 parse_tcp_flags(tcpinfo, argv[optind-1], argv[optind], 201 invert); 202 optind++; 203 *flags |= TCP_FLAGS; 204 break; 205 206 case '5': 207 if (*flags & TCP_OPTION) 208 exit_error(PARAMETER_PROBLEM, 209 "Only one `--tcp-option' allowed"); 210 check_inverse(optarg, &invert, &optind, 0); 211 parse_tcp_option(argv[optind-1], &tcpinfo->option); 212 if (invert) 213 tcpinfo->invflags |= IP6T_TCP_INV_OPTION; 214 *flags |= TCP_OPTION; 215 break; 216 217 default: 218 return 0; 219 } 220 221 return 1; 222 } 223 224 /* Final check; we don't care. */ 225 static void 226 final_check(unsigned int flags) 227 { 228 } 229 230 static char * 231 port_to_service(int port) 232 { 233 struct servent *service; 234 235 if ((service = getservbyport(htons(port), "tcp"))) 236 return service->s_name; 237 238 return NULL; 239 } 240 241 static void 242 print_port(u_int16_t port, int numeric) 243 { 244 char *service; 245 246 if (numeric || (service = port_to_service(port)) == NULL) 247 printf("%u", port); 248 else 249 printf("%s", service); 250 } 251 252 static void 253 print_ports(const char *name, u_int16_t min, u_int16_t max, 254 int invert, int numeric) 255 { 256 const char *inv = invert ? "!" : ""; 257 258 if (min != 0 || max != 0xFFFF || invert) { 259 printf("%s", name); 260 if (min == max) { 261 printf(":%s", inv); 262 print_port(min, numeric); 263 } else { 264 printf("s:%s", inv); 265 print_port(min, numeric); 266 printf(":"); 267 print_port(max, numeric); 268 } 269 printf(" "); 270 } 271 } 272 273 static void 274 print_option(u_int8_t option, int invert, int numeric) 275 { 276 if (option || invert) 277 printf("option=%s%u ", invert ? "!" : "", option); 278 } 279 280 static void 281 print_tcpf(u_int8_t flags) 282 { 283 int have_flag = 0; 284 285 while (flags) { 286 unsigned int i; 287 288 for (i = 0; (flags & tcp_flag_names[i].flag) == 0; i++); 289 290 if (have_flag) 291 printf(","); 292 printf("%s", tcp_flag_names[i].name); 293 have_flag = 1; 294 295 flags &= ~tcp_flag_names[i].flag; 296 } 297 298 if (!have_flag) 299 printf("NONE"); 300 } 301 302 static void 303 print_flags(u_int8_t mask, u_int8_t cmp, int invert, int numeric) 304 { 305 if (mask || invert) { 306 printf("flags:%s", invert ? "!" : ""); 307 if (numeric) 308 printf("0x%02X/0x%02X ", mask, cmp); 309 else { 310 print_tcpf(mask); 311 printf("/"); 312 print_tcpf(cmp); 313 printf(" "); 314 } 315 } 316 } 317 318 /* Prints out the union ipt_matchinfo. */ 319 static void 320 print(const struct ip6t_ip6 *ip, 321 const struct ip6t_entry_match *match, int numeric) 322 { 323 const struct ip6t_tcp *tcp = (struct ip6t_tcp *)match->data; 324 325 printf("tcp "); 326 print_ports("spt", tcp->spts[0], tcp->spts[1], 327 tcp->invflags & IP6T_TCP_INV_SRCPT, 328 numeric); 329 print_ports("dpt", tcp->dpts[0], tcp->dpts[1], 330 tcp->invflags & IP6T_TCP_INV_DSTPT, 331 numeric); 332 print_option(tcp->option, 333 tcp->invflags & IP6T_TCP_INV_OPTION, 334 numeric); 335 print_flags(tcp->flg_mask, tcp->flg_cmp, 336 tcp->invflags & IP6T_TCP_INV_FLAGS, 337 numeric); 338 if (tcp->invflags & ~IP6T_TCP_INV_MASK) 339 printf("Unknown invflags: 0x%X ", 340 tcp->invflags & ~IP6T_TCP_INV_MASK); 341 } 342 343 /* Saves the union ipt_matchinfo in parsable form to stdout. */ 344 static void save(const struct ip6t_ip6 *ip, const struct ip6t_entry_match *match) 345 { 346 const struct ip6t_tcp *tcpinfo = (struct ip6t_tcp *)match->data; 347 348 if (tcpinfo->spts[0] != 0 349 || tcpinfo->spts[1] != 0xFFFF) { 350 if (tcpinfo->invflags & IP6T_TCP_INV_SRCPT) 351 printf("! "); 352 if (tcpinfo->spts[0] 353 != tcpinfo->spts[1]) 354 printf("--sport %u:%u ", 355 tcpinfo->spts[0], 356 tcpinfo->spts[1]); 357 else 358 printf("--sport %u ", 359 tcpinfo->spts[0]); 360 } 361 362 if (tcpinfo->dpts[0] != 0 363 || tcpinfo->dpts[1] != 0xFFFF) { 364 if (tcpinfo->invflags & IP6T_TCP_INV_DSTPT) 365 printf("! "); 366 if (tcpinfo->dpts[0] 367 != tcpinfo->dpts[1]) 368 printf("--dport %u:%u ", 369 tcpinfo->dpts[0], 370 tcpinfo->dpts[1]); 371 else 372 printf("--dport %u ", 373 tcpinfo->dpts[0]); 374 } 375 376 if (tcpinfo->option 377 || (tcpinfo->invflags & IP6T_TCP_INV_OPTION)) { 378 if (tcpinfo->invflags & IP6T_TCP_INV_OPTION) 379 printf("! "); 380 printf("--tcp-option %u ", tcpinfo->option); 381 } 382 383 if (tcpinfo->flg_mask 384 || (tcpinfo->invflags & IP6T_TCP_INV_FLAGS)) { 385 if (tcpinfo->invflags & IP6T_TCP_INV_FLAGS) 386 printf("! "); 387 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 ip6tables_match tcp = { 399 .name = "tcp", 400 .version = IPTABLES_VERSION, 401 .size = IP6T_ALIGN(sizeof(struct ip6t_tcp)), 402 .userspacesize = IP6T_ALIGN(sizeof(struct ip6t_tcp)), 403 .help = &help, 404 .init = &init, 405 .parse = &parse, 406 .final_check = &final_check, 407 .print = &print, 408 .save = &save, 409 .extra_opts = opts, 410 }; 411 412 void 413 _init(void) 414 { 415 register_match6(&tcp); 416 } 417