|  | /* Shared library add-on to iptables for ECN, $Version$ | 
|  | * | 
|  | * (C) 2002 by Harald Welte <laforge@gnumonks.org> | 
|  | * | 
|  | * This program is distributed under the terms of GNU GPL v2, 1991 | 
|  | * | 
|  | * libipt_ECN.c borrowed heavily from libipt_DSCP.c | 
|  | * | 
|  | * $Id$ | 
|  | */ | 
|  | #include <stdio.h> | 
|  | #include <string.h> | 
|  | #include <stdlib.h> | 
|  | #include <getopt.h> | 
|  |  | 
|  | #include <xtables.h> | 
|  | #include <linux/netfilter_ipv4/ipt_ECN.h> | 
|  |  | 
|  | static void ECN_help(void) | 
|  | { | 
|  | printf( | 
|  | "ECN target options\n" | 
|  | "  --ecn-tcp-remove		Remove all ECN bits from TCP header\n"); | 
|  | } | 
|  |  | 
|  | #if 0 | 
|  | "ECN target v%s EXPERIMENTAL options (use with extreme care!)\n" | 
|  | "  --ecn-ip-ect			Set the IPv4 ECT codepoint (0 to 3)\n" | 
|  | "  --ecn-tcp-cwr		Set the IPv4 CWR bit (0 or 1)\n" | 
|  | "  --ecn-tcp-ece		Set the IPv4 ECE bit (0 or 1)\n", | 
|  | #endif | 
|  |  | 
|  |  | 
|  | static const struct option ECN_opts[] = { | 
|  | { "ecn-tcp-remove", 0, NULL, 'F' }, | 
|  | { "ecn-tcp-cwr", 1, NULL, 'G' }, | 
|  | { "ecn-tcp-ece", 1, NULL, 'H' }, | 
|  | { "ecn-ip-ect", 1, NULL, '9' }, | 
|  | { .name = NULL } | 
|  | }; | 
|  |  | 
|  | static int ECN_parse(int c, char **argv, int invert, unsigned int *flags, | 
|  | const void *entry, struct xt_entry_target **target) | 
|  | { | 
|  | unsigned int result; | 
|  | struct ipt_ECN_info *einfo | 
|  | = (struct ipt_ECN_info *)(*target)->data; | 
|  |  | 
|  | switch (c) { | 
|  | case 'F': | 
|  | if (*flags) | 
|  | xtables_error(PARAMETER_PROBLEM, | 
|  | "ECN target: Only use --ecn-tcp-remove ONCE!"); | 
|  | einfo->operation = IPT_ECN_OP_SET_ECE | IPT_ECN_OP_SET_CWR; | 
|  | einfo->proto.tcp.ece = 0; | 
|  | einfo->proto.tcp.cwr = 0; | 
|  | *flags = 1; | 
|  | break; | 
|  | case 'G': | 
|  | if (*flags & IPT_ECN_OP_SET_CWR) | 
|  | xtables_error(PARAMETER_PROBLEM, | 
|  | "ECN target: Only use --ecn-tcp-cwr ONCE!"); | 
|  | if (!xtables_strtoui(optarg, NULL, &result, 0, 1)) | 
|  | xtables_error(PARAMETER_PROBLEM, | 
|  | "ECN target: Value out of range"); | 
|  | einfo->operation |= IPT_ECN_OP_SET_CWR; | 
|  | einfo->proto.tcp.cwr = result; | 
|  | *flags |= IPT_ECN_OP_SET_CWR; | 
|  | break; | 
|  | case 'H': | 
|  | if (*flags & IPT_ECN_OP_SET_ECE) | 
|  | xtables_error(PARAMETER_PROBLEM, | 
|  | "ECN target: Only use --ecn-tcp-ece ONCE!"); | 
|  | if (!xtables_strtoui(optarg, NULL, &result, 0, 1)) | 
|  | xtables_error(PARAMETER_PROBLEM, | 
|  | "ECN target: Value out of range"); | 
|  | einfo->operation |= IPT_ECN_OP_SET_ECE; | 
|  | einfo->proto.tcp.ece = result; | 
|  | *flags |= IPT_ECN_OP_SET_ECE; | 
|  | break; | 
|  | case '9': | 
|  | if (*flags & IPT_ECN_OP_SET_IP) | 
|  | xtables_error(PARAMETER_PROBLEM, | 
|  | "ECN target: Only use --ecn-ip-ect ONCE!"); | 
|  | if (!xtables_strtoui(optarg, NULL, &result, 0, 3)) | 
|  | xtables_error(PARAMETER_PROBLEM, | 
|  | "ECN target: Value out of range"); | 
|  | einfo->operation |= IPT_ECN_OP_SET_IP; | 
|  | einfo->ip_ect = result; | 
|  | *flags |= IPT_ECN_OP_SET_IP; | 
|  | break; | 
|  | default: | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static void ECN_check(unsigned int flags) | 
|  | { | 
|  | if (!flags) | 
|  | xtables_error(PARAMETER_PROBLEM, | 
|  | "ECN target: Parameter --ecn-tcp-remove is required"); | 
|  | } | 
|  |  | 
|  | static void ECN_print(const void *ip, const struct xt_entry_target *target, | 
|  | int numeric) | 
|  | { | 
|  | const struct ipt_ECN_info *einfo = | 
|  | (const struct ipt_ECN_info *)target->data; | 
|  |  | 
|  | printf("ECN "); | 
|  |  | 
|  | if (einfo->operation == (IPT_ECN_OP_SET_ECE|IPT_ECN_OP_SET_CWR) | 
|  | && einfo->proto.tcp.ece == 0 | 
|  | && einfo->proto.tcp.cwr == 0) | 
|  | printf("TCP remove "); | 
|  | else { | 
|  | if (einfo->operation & IPT_ECN_OP_SET_ECE) | 
|  | printf("ECE=%u ", einfo->proto.tcp.ece); | 
|  |  | 
|  | if (einfo->operation & IPT_ECN_OP_SET_CWR) | 
|  | printf("CWR=%u ", einfo->proto.tcp.cwr); | 
|  |  | 
|  | if (einfo->operation & IPT_ECN_OP_SET_IP) | 
|  | printf("ECT codepoint=%u ", einfo->ip_ect); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void ECN_save(const void *ip, const struct xt_entry_target *target) | 
|  | { | 
|  | const struct ipt_ECN_info *einfo = | 
|  | (const struct ipt_ECN_info *)target->data; | 
|  |  | 
|  | if (einfo->operation == (IPT_ECN_OP_SET_ECE|IPT_ECN_OP_SET_CWR) | 
|  | && einfo->proto.tcp.ece == 0 | 
|  | && einfo->proto.tcp.cwr == 0) | 
|  | printf("--ecn-tcp-remove "); | 
|  | else { | 
|  |  | 
|  | if (einfo->operation & IPT_ECN_OP_SET_ECE) | 
|  | printf("--ecn-tcp-ece %d ", einfo->proto.tcp.ece); | 
|  |  | 
|  | if (einfo->operation & IPT_ECN_OP_SET_CWR) | 
|  | printf("--ecn-tcp-cwr %d ", einfo->proto.tcp.cwr); | 
|  |  | 
|  | if (einfo->operation & IPT_ECN_OP_SET_IP) | 
|  | printf("--ecn-ip-ect %d ", einfo->ip_ect); | 
|  | } | 
|  | } | 
|  |  | 
|  | static struct xtables_target ecn_tg_reg = { | 
|  | .name		= "ECN", | 
|  | .version	= XTABLES_VERSION, | 
|  | .family		= NFPROTO_IPV4, | 
|  | .size		= XT_ALIGN(sizeof(struct ipt_ECN_info)), | 
|  | .userspacesize	= XT_ALIGN(sizeof(struct ipt_ECN_info)), | 
|  | .help		= ECN_help, | 
|  | .parse		= ECN_parse, | 
|  | .final_check	= ECN_check, | 
|  | .print		= ECN_print, | 
|  | .save		= ECN_save, | 
|  | .extra_opts	= ECN_opts, | 
|  | }; | 
|  |  | 
|  | void _init(void) | 
|  | { | 
|  | xtables_register_target(&ecn_tg_reg); | 
|  | } |