libxt_tcp.c revision 7ac405297ec38449b30e3b05fd6bf2082fd3d803
1/* Shared library add-on to iptables to add TCP support. */ 2#include <stdbool.h> 3#include <stdio.h> 4#include <netdb.h> 5#include <string.h> 6#include <stdlib.h> 7#include <getopt.h> 8#include <netinet/in.h> 9#include <xtables.h> 10#include <linux/netfilter/xt_tcpudp.h> 11 12static void tcp_help(void) 13{ 14 printf( 15"tcp match 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,FIN 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"); 27} 28 29static const struct option tcp_opts[] = { 30 {.name = "source-port", .has_arg = true, .val = '1'}, 31 {.name = "sport", .has_arg = true, .val = '1'}, /* synonym */ 32 {.name = "destination-port", .has_arg = true, .val = '2'}, 33 {.name = "dport", .has_arg = true, .val = '2'}, /* synonym */ 34 {.name = "syn", .has_arg = false, .val = '3'}, 35 {.name = "tcp-flags", .has_arg = true, .val = '4'}, 36 {.name = "tcp-option", .has_arg = true, .val = '5'}, 37 XT_GETOPT_TABLEEND, 38}; 39 40static void 41parse_tcp_ports(const char *portstring, uint16_t *ports) 42{ 43 char *buffer; 44 char *cp; 45 46 buffer = strdup(portstring); 47 if ((cp = strchr(buffer, ':')) == NULL) 48 ports[0] = ports[1] = xtables_parse_port(buffer, "tcp"); 49 else { 50 *cp = '\0'; 51 cp++; 52 53 ports[0] = buffer[0] ? xtables_parse_port(buffer, "tcp") : 0; 54 ports[1] = cp[0] ? xtables_parse_port(cp, "tcp") : 0xFFFF; 55 56 if (ports[0] > ports[1]) 57 xtables_error(PARAMETER_PROBLEM, 58 "invalid portrange (min > max)"); 59 } 60 free(buffer); 61} 62 63struct tcp_flag_names { 64 const char *name; 65 unsigned int flag; 66}; 67 68static const struct tcp_flag_names tcp_flag_names[] 69= { { "FIN", 0x01 }, 70 { "SYN", 0x02 }, 71 { "RST", 0x04 }, 72 { "PSH", 0x08 }, 73 { "ACK", 0x10 }, 74 { "URG", 0x20 }, 75 { "ALL", 0x3F }, 76 { "NONE", 0 }, 77}; 78 79static unsigned int 80parse_tcp_flag(const char *flags) 81{ 82 unsigned int ret = 0; 83 char *ptr; 84 char *buffer; 85 86 buffer = strdup(flags); 87 88 for (ptr = strtok(buffer, ","); ptr; ptr = strtok(NULL, ",")) { 89 unsigned int i; 90 for (i = 0; i < ARRAY_SIZE(tcp_flag_names); ++i) 91 if (strcasecmp(tcp_flag_names[i].name, ptr) == 0) { 92 ret |= tcp_flag_names[i].flag; 93 break; 94 } 95 if (i == ARRAY_SIZE(tcp_flag_names)) 96 xtables_error(PARAMETER_PROBLEM, 97 "Unknown TCP flag `%s'", ptr); 98 } 99 100 free(buffer); 101 return ret; 102} 103 104static void 105parse_tcp_flags(struct xt_tcp *tcpinfo, 106 const char *mask, 107 const char *cmp, 108 int invert) 109{ 110 tcpinfo->flg_mask = parse_tcp_flag(mask); 111 tcpinfo->flg_cmp = parse_tcp_flag(cmp); 112 113 if (invert) 114 tcpinfo->invflags |= XT_TCP_INV_FLAGS; 115} 116 117static void 118parse_tcp_option(const char *option, uint8_t *result) 119{ 120 unsigned int ret; 121 122 if (!xtables_strtoui(option, NULL, &ret, 1, UINT8_MAX)) 123 xtables_error(PARAMETER_PROBLEM, "Bad TCP option \"%s\"", option); 124 125 *result = ret; 126} 127 128static void tcp_init(struct xt_entry_match *m) 129{ 130 struct xt_tcp *tcpinfo = (struct xt_tcp *)m->data; 131 132 tcpinfo->spts[1] = tcpinfo->dpts[1] = 0xFFFF; 133} 134 135#define TCP_SRC_PORTS 0x01 136#define TCP_DST_PORTS 0x02 137#define TCP_FLAGS 0x04 138#define TCP_OPTION 0x08 139 140static int 141tcp_parse(int c, char **argv, int invert, unsigned int *flags, 142 const void *entry, struct xt_entry_match **match) 143{ 144 struct xt_tcp *tcpinfo = (struct xt_tcp *)(*match)->data; 145 146 switch (c) { 147 case '1': 148 if (*flags & TCP_SRC_PORTS) 149 xtables_error(PARAMETER_PROBLEM, 150 "Only one `--source-port' allowed"); 151 xtables_check_inverse(optarg, &invert, &optind, 0, argv); 152 parse_tcp_ports(optarg, tcpinfo->spts); 153 if (invert) 154 tcpinfo->invflags |= XT_TCP_INV_SRCPT; 155 *flags |= TCP_SRC_PORTS; 156 break; 157 158 case '2': 159 if (*flags & TCP_DST_PORTS) 160 xtables_error(PARAMETER_PROBLEM, 161 "Only one `--destination-port' allowed"); 162 xtables_check_inverse(optarg, &invert, &optind, 0, argv); 163 parse_tcp_ports(optarg, tcpinfo->dpts); 164 if (invert) 165 tcpinfo->invflags |= XT_TCP_INV_DSTPT; 166 *flags |= TCP_DST_PORTS; 167 break; 168 169 case '3': 170 if (*flags & TCP_FLAGS) 171 xtables_error(PARAMETER_PROBLEM, 172 "Only one of `--syn' or `--tcp-flags' " 173 " allowed"); 174 parse_tcp_flags(tcpinfo, "SYN,RST,ACK,FIN", "SYN", invert); 175 *flags |= TCP_FLAGS; 176 break; 177 178 case '4': 179 if (*flags & TCP_FLAGS) 180 xtables_error(PARAMETER_PROBLEM, 181 "Only one of `--syn' or `--tcp-flags' " 182 " allowed"); 183 xtables_check_inverse(optarg, &invert, &optind, 0, argv); 184 185 if (!argv[optind] 186 || argv[optind][0] == '-' || argv[optind][0] == '!') 187 xtables_error(PARAMETER_PROBLEM, 188 "--tcp-flags requires two args."); 189 190 parse_tcp_flags(tcpinfo, optarg, argv[optind], 191 invert); 192 optind++; 193 *flags |= TCP_FLAGS; 194 break; 195 196 case '5': 197 if (*flags & TCP_OPTION) 198 xtables_error(PARAMETER_PROBLEM, 199 "Only one `--tcp-option' allowed"); 200 xtables_check_inverse(optarg, &invert, &optind, 0, argv); 201 parse_tcp_option(optarg, &tcpinfo->option); 202 if (invert) 203 tcpinfo->invflags |= XT_TCP_INV_OPTION; 204 *flags |= TCP_OPTION; 205 break; 206 207 default: 208 return 0; 209 } 210 211 return 1; 212} 213 214static char * 215port_to_service(int port) 216{ 217 struct servent *service; 218 219 if ((service = getservbyport(htons(port), "tcp"))) 220 return service->s_name; 221 222 return NULL; 223} 224 225static void 226print_port(uint16_t port, int numeric) 227{ 228 char *service; 229 230 if (numeric || (service = port_to_service(port)) == NULL) 231 printf("%u", port); 232 else 233 printf("%s", service); 234} 235 236static void 237print_ports(const char *name, uint16_t min, uint16_t max, 238 int invert, int numeric) 239{ 240 const char *inv = invert ? "!" : ""; 241 242 if (min != 0 || max != 0xFFFF || invert) { 243 printf("%s", name); 244 if (min == max) { 245 printf(":%s", inv); 246 print_port(min, numeric); 247 } else { 248 printf("s:%s", inv); 249 print_port(min, numeric); 250 printf(":"); 251 print_port(max, numeric); 252 } 253 printf(" "); 254 } 255} 256 257static void 258print_option(uint8_t option, int invert, int numeric) 259{ 260 if (option || invert) 261 printf("option=%s%u ", invert ? "!" : "", option); 262} 263 264static void 265print_tcpf(uint8_t flags) 266{ 267 int have_flag = 0; 268 269 while (flags) { 270 unsigned int i; 271 272 for (i = 0; (flags & tcp_flag_names[i].flag) == 0; i++); 273 274 if (have_flag) 275 printf(","); 276 printf("%s", tcp_flag_names[i].name); 277 have_flag = 1; 278 279 flags &= ~tcp_flag_names[i].flag; 280 } 281 282 if (!have_flag) 283 printf("NONE"); 284} 285 286static void 287print_flags(uint8_t mask, uint8_t cmp, int invert, int numeric) 288{ 289 if (mask || invert) { 290 printf("flags:%s", invert ? "!" : ""); 291 if (numeric) 292 printf("0x%02X/0x%02X ", mask, cmp); 293 else { 294 print_tcpf(mask); 295 printf("/"); 296 print_tcpf(cmp); 297 printf(" "); 298 } 299 } 300} 301 302static void 303tcp_print(const void *ip, const struct xt_entry_match *match, int numeric) 304{ 305 const struct xt_tcp *tcp = (struct xt_tcp *)match->data; 306 307 printf("tcp "); 308 print_ports("spt", tcp->spts[0], tcp->spts[1], 309 tcp->invflags & XT_TCP_INV_SRCPT, 310 numeric); 311 print_ports("dpt", tcp->dpts[0], tcp->dpts[1], 312 tcp->invflags & XT_TCP_INV_DSTPT, 313 numeric); 314 print_option(tcp->option, 315 tcp->invflags & XT_TCP_INV_OPTION, 316 numeric); 317 print_flags(tcp->flg_mask, tcp->flg_cmp, 318 tcp->invflags & XT_TCP_INV_FLAGS, 319 numeric); 320 if (tcp->invflags & ~XT_TCP_INV_MASK) 321 printf("Unknown invflags: 0x%X ", 322 tcp->invflags & ~XT_TCP_INV_MASK); 323} 324 325static void tcp_save(const void *ip, const struct xt_entry_match *match) 326{ 327 const struct xt_tcp *tcpinfo = (struct xt_tcp *)match->data; 328 329 if (tcpinfo->spts[0] != 0 330 || tcpinfo->spts[1] != 0xFFFF) { 331 if (tcpinfo->invflags & XT_TCP_INV_SRCPT) 332 printf("! "); 333 if (tcpinfo->spts[0] 334 != tcpinfo->spts[1]) 335 printf("--sport %u:%u ", 336 tcpinfo->spts[0], 337 tcpinfo->spts[1]); 338 else 339 printf("--sport %u ", 340 tcpinfo->spts[0]); 341 } 342 343 if (tcpinfo->dpts[0] != 0 344 || tcpinfo->dpts[1] != 0xFFFF) { 345 if (tcpinfo->invflags & XT_TCP_INV_DSTPT) 346 printf("! "); 347 if (tcpinfo->dpts[0] 348 != tcpinfo->dpts[1]) 349 printf("--dport %u:%u ", 350 tcpinfo->dpts[0], 351 tcpinfo->dpts[1]); 352 else 353 printf("--dport %u ", 354 tcpinfo->dpts[0]); 355 } 356 357 if (tcpinfo->option 358 || (tcpinfo->invflags & XT_TCP_INV_OPTION)) { 359 if (tcpinfo->invflags & XT_TCP_INV_OPTION) 360 printf("! "); 361 printf("--tcp-option %u ", tcpinfo->option); 362 } 363 364 if (tcpinfo->flg_mask 365 || (tcpinfo->invflags & XT_TCP_INV_FLAGS)) { 366 if (tcpinfo->invflags & XT_TCP_INV_FLAGS) 367 printf("! "); 368 printf("--tcp-flags "); 369 if (tcpinfo->flg_mask != 0xFF) { 370 print_tcpf(tcpinfo->flg_mask); 371 } 372 printf(" "); 373 print_tcpf(tcpinfo->flg_cmp); 374 printf(" "); 375 } 376} 377 378static struct xtables_match tcp_match = { 379 .family = NFPROTO_UNSPEC, 380 .name = "tcp", 381 .version = XTABLES_VERSION, 382 .size = XT_ALIGN(sizeof(struct xt_tcp)), 383 .userspacesize = XT_ALIGN(sizeof(struct xt_tcp)), 384 .help = tcp_help, 385 .init = tcp_init, 386 .parse = tcp_parse, 387 .print = tcp_print, 388 .save = tcp_save, 389 .extra_opts = tcp_opts, 390}; 391 392void 393_init(void) 394{ 395 xtables_register_match(&tcp_match); 396} 397