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. */ 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 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 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 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 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 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. */ 148static int 149parse(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. */ 225static void 226final_check(unsigned int flags) 227{ 228} 229 230static char * 231port_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 241static void 242print_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 252static void 253print_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 273static void 274print_option(u_int8_t option, int invert, int numeric) 275{ 276 if (option || invert) 277 printf("option=%s%u ", invert ? "!" : "", option); 278} 279 280static void 281print_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 302static void 303print_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. */ 319static void 320print(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. */ 344static 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 398static 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 412void 413_init(void) 414{ 415 register_match6(&tcp); 416} 417