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 12 static 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 29 static 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 40 static void 41 parse_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 63 struct tcp_flag_names { 64 const char *name; 65 unsigned int flag; 66 }; 67 68 static 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 79 static unsigned int 80 parse_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 104 static void 105 parse_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 117 static void 118 parse_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 128 static 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 140 static int 141 tcp_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 parse_tcp_ports(optarg, tcpinfo->spts); 152 if (invert) 153 tcpinfo->invflags |= XT_TCP_INV_SRCPT; 154 *flags |= TCP_SRC_PORTS; 155 break; 156 157 case '2': 158 if (*flags & TCP_DST_PORTS) 159 xtables_error(PARAMETER_PROBLEM, 160 "Only one `--destination-port' allowed"); 161 parse_tcp_ports(optarg, tcpinfo->dpts); 162 if (invert) 163 tcpinfo->invflags |= XT_TCP_INV_DSTPT; 164 *flags |= TCP_DST_PORTS; 165 break; 166 167 case '3': 168 if (*flags & TCP_FLAGS) 169 xtables_error(PARAMETER_PROBLEM, 170 "Only one of `--syn' or `--tcp-flags' " 171 " allowed"); 172 parse_tcp_flags(tcpinfo, "SYN,RST,ACK,FIN", "SYN", invert); 173 *flags |= TCP_FLAGS; 174 break; 175 176 case '4': 177 if (*flags & TCP_FLAGS) 178 xtables_error(PARAMETER_PROBLEM, 179 "Only one of `--syn' or `--tcp-flags' " 180 " allowed"); 181 if (!argv[optind] 182 || argv[optind][0] == '-' || argv[optind][0] == '!') 183 xtables_error(PARAMETER_PROBLEM, 184 "--tcp-flags requires two args."); 185 186 parse_tcp_flags(tcpinfo, optarg, argv[optind], 187 invert); 188 optind++; 189 *flags |= TCP_FLAGS; 190 break; 191 192 case '5': 193 if (*flags & TCP_OPTION) 194 xtables_error(PARAMETER_PROBLEM, 195 "Only one `--tcp-option' allowed"); 196 parse_tcp_option(optarg, &tcpinfo->option); 197 if (invert) 198 tcpinfo->invflags |= XT_TCP_INV_OPTION; 199 *flags |= TCP_OPTION; 200 break; 201 } 202 203 return 1; 204 } 205 206 static const char * 207 port_to_service(int port) 208 { 209 const struct servent *service; 210 211 if ((service = getservbyport(htons(port), "tcp"))) 212 return service->s_name; 213 214 return NULL; 215 } 216 217 static void 218 print_port(uint16_t port, int numeric) 219 { 220 const char *service; 221 222 if (numeric || (service = port_to_service(port)) == NULL) 223 printf("%u", port); 224 else 225 printf("%s", service); 226 } 227 228 static void 229 print_ports(const char *name, uint16_t min, uint16_t max, 230 int invert, int numeric) 231 { 232 const char *inv = invert ? "!" : ""; 233 234 if (min != 0 || max != 0xFFFF || invert) { 235 printf(" %s", name); 236 if (min == max) { 237 printf(":%s", inv); 238 print_port(min, numeric); 239 } else { 240 printf("s:%s", inv); 241 print_port(min, numeric); 242 printf(":"); 243 print_port(max, numeric); 244 } 245 } 246 } 247 248 static void 249 print_option(uint8_t option, int invert, int numeric) 250 { 251 if (option || invert) 252 printf(" option=%s%u", invert ? "!" : "", option); 253 } 254 255 static void 256 print_tcpf(uint8_t flags) 257 { 258 int have_flag = 0; 259 260 while (flags) { 261 unsigned int i; 262 263 for (i = 0; (flags & tcp_flag_names[i].flag) == 0; i++); 264 265 if (have_flag) 266 printf(","); 267 printf("%s", tcp_flag_names[i].name); 268 have_flag = 1; 269 270 flags &= ~tcp_flag_names[i].flag; 271 } 272 273 if (!have_flag) 274 printf("NONE"); 275 } 276 277 static void 278 print_flags(uint8_t mask, uint8_t cmp, int invert, int numeric) 279 { 280 if (mask || invert) { 281 printf(" flags:%s", invert ? "!" : ""); 282 if (numeric) 283 printf("0x%02X/0x%02X", mask, cmp); 284 else { 285 print_tcpf(mask); 286 printf("/"); 287 print_tcpf(cmp); 288 } 289 } 290 } 291 292 static void 293 tcp_print(const void *ip, const struct xt_entry_match *match, int numeric) 294 { 295 const struct xt_tcp *tcp = (struct xt_tcp *)match->data; 296 297 printf(" tcp"); 298 print_ports("spt", tcp->spts[0], tcp->spts[1], 299 tcp->invflags & XT_TCP_INV_SRCPT, 300 numeric); 301 print_ports("dpt", tcp->dpts[0], tcp->dpts[1], 302 tcp->invflags & XT_TCP_INV_DSTPT, 303 numeric); 304 print_option(tcp->option, 305 tcp->invflags & XT_TCP_INV_OPTION, 306 numeric); 307 print_flags(tcp->flg_mask, tcp->flg_cmp, 308 tcp->invflags & XT_TCP_INV_FLAGS, 309 numeric); 310 if (tcp->invflags & ~XT_TCP_INV_MASK) 311 printf(" Unknown invflags: 0x%X", 312 tcp->invflags & ~XT_TCP_INV_MASK); 313 } 314 315 static void tcp_save(const void *ip, const struct xt_entry_match *match) 316 { 317 const struct xt_tcp *tcpinfo = (struct xt_tcp *)match->data; 318 319 if (tcpinfo->spts[0] != 0 320 || tcpinfo->spts[1] != 0xFFFF) { 321 if (tcpinfo->invflags & XT_TCP_INV_SRCPT) 322 printf(" !"); 323 if (tcpinfo->spts[0] 324 != tcpinfo->spts[1]) 325 printf(" --sport %u:%u", 326 tcpinfo->spts[0], 327 tcpinfo->spts[1]); 328 else 329 printf(" --sport %u", 330 tcpinfo->spts[0]); 331 } 332 333 if (tcpinfo->dpts[0] != 0 334 || tcpinfo->dpts[1] != 0xFFFF) { 335 if (tcpinfo->invflags & XT_TCP_INV_DSTPT) 336 printf(" !"); 337 if (tcpinfo->dpts[0] 338 != tcpinfo->dpts[1]) 339 printf(" --dport %u:%u", 340 tcpinfo->dpts[0], 341 tcpinfo->dpts[1]); 342 else 343 printf(" --dport %u", 344 tcpinfo->dpts[0]); 345 } 346 347 if (tcpinfo->option 348 || (tcpinfo->invflags & XT_TCP_INV_OPTION)) { 349 if (tcpinfo->invflags & XT_TCP_INV_OPTION) 350 printf(" !"); 351 printf(" --tcp-option %u", tcpinfo->option); 352 } 353 354 if (tcpinfo->flg_mask 355 || (tcpinfo->invflags & XT_TCP_INV_FLAGS)) { 356 if (tcpinfo->invflags & XT_TCP_INV_FLAGS) 357 printf(" !"); 358 printf(" --tcp-flags "); 359 print_tcpf(tcpinfo->flg_mask); 360 printf(" "); 361 print_tcpf(tcpinfo->flg_cmp); 362 } 363 } 364 365 static struct xtables_match tcp_match = { 366 .family = NFPROTO_UNSPEC, 367 .name = "tcp", 368 .version = XTABLES_VERSION, 369 .size = XT_ALIGN(sizeof(struct xt_tcp)), 370 .userspacesize = XT_ALIGN(sizeof(struct xt_tcp)), 371 .help = tcp_help, 372 .init = tcp_init, 373 .parse = tcp_parse, 374 .print = tcp_print, 375 .save = tcp_save, 376 .extra_opts = tcp_opts, 377 }; 378 379 void 380 _init(void) 381 { 382 xtables_register_match(&tcp_match); 383 } 384