@@ -12,9 +12,41 @@ enum {
O_TO_PORTS = 0,
O_RANDOM,
O_RANDOM_FULLY,
+ O_PSID,
};
-static void MASQUERADE_help(void)
+static unsigned int _log2(unsigned int x)
+{
+ unsigned int y = 0;
+
+ for (; x > 1; x >>= 1)
+ y++;
+ return y;
+}
+
+static void cpy_ipv4_range_to_range2(struct nf_nat_range2 *dst, const struct nf_nat_ipv4_range *src)
+{
+ memset(&dst->min_addr, 0, sizeof(dst->min_addr));
+ memset(&dst->max_addr, 0, sizeof(dst->max_addr));
+ memset(&dst->base_proto, 0, sizeof(dst->base_proto));
+
+ dst->flags = src->flags;
+ dst->min_addr.ip = src->min_ip;
+ dst->max_addr.ip = src->max_ip;
+ dst->min_proto = src->min;
+ dst->max_proto = src->max;
+}
+
+static void cpy_range2_to_ipv4_range(struct nf_nat_ipv4_range *dst, const struct nf_nat_range2 *src)
+{
+ dst->flags = src->flags;
+ dst->min_ip = src->min_addr.ip;
+ dst->max_ip = src->max_addr.ip;
+ dst->min = src->min_proto;
+ dst->max = src->max_proto;
+}
+
+static void MASQUERADE_help_v0(void)
{
printf(
"MASQUERADE target options:\n"
@@ -26,14 +58,30 @@ static void MASQUERADE_help(void)
" Fully randomize source port.\n");
}
-static const struct xt_option_entry MASQUERADE_opts[] = {
+static void MASQUERADE_help_v1(void)
+{
+ MASQUERADE_help_v0();
+ printf(
+" --psid <offset_length>:<psid>:<psid_length>\n"
+" Run in PSID mode with this PSID\n");
+}
+
+static const struct xt_option_entry MASQUERADE_opts_v0[] = {
{.name = "to-ports", .id = O_TO_PORTS, .type = XTTYPE_STRING},
{.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
{.name = "random-fully", .id = O_RANDOM_FULLY, .type = XTTYPE_NONE},
XTOPT_TABLEEND,
};
-static void MASQUERADE_init(struct xt_entry_target *t)
+static const struct xt_option_entry MASQUERADE_opts_v1[] = {
+ {.name = "to-ports", .id = O_TO_PORTS, .type = XTTYPE_STRING},
+ {.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
+ {.name = "random-fully", .id = O_RANDOM_FULLY, .type = XTTYPE_NONE},
+ {.name = "psid", .id = O_PSID, .type = XTTYPE_STRING},
+ XTOPT_TABLEEND,
+};
+
+static void MASQUERADE_init_v0(struct xt_entry_target *t)
{
struct nf_nat_ipv4_multi_range_compat *mr = (struct nf_nat_ipv4_multi_range_compat *)t->data;
@@ -42,21 +90,20 @@ static void MASQUERADE_init(struct xt_entry_target *t)
}
/* Parses ports */
-static void
-parse_ports(const char *arg, struct nf_nat_ipv4_multi_range_compat *mr)
+static void parse_ports(const char *arg, struct nf_nat_range2 *r)
{
char *end;
unsigned int port, maxport;
- mr->range[0].flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
+ r->flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
if (!xtables_strtoui(arg, &end, &port, 0, UINT16_MAX))
xtables_param_act(XTF_BAD_VALUE, "MASQUERADE", "--to-ports", arg);
switch (*end) {
case '\0':
- mr->range[0].min.tcp.port
- = mr->range[0].max.tcp.port
+ r->min_proto.tcp.port
+ = r->max_proto.tcp.port
= htons(port);
return;
case '-':
@@ -66,8 +113,8 @@ parse_ports(const char *arg, struct nf_nat_ipv4_multi_range_compat *mr)
if (maxport < port)
break;
- mr->range[0].min.tcp.port = htons(port);
- mr->range[0].max.tcp.port = htons(maxport);
+ r->min_proto.tcp.port = htons(port);
+ r->max_proto.tcp.port = htons(maxport);
return;
default:
break;
@@ -75,11 +122,64 @@ parse_ports(const char *arg, struct nf_nat_ipv4_multi_range_compat *mr)
xtables_param_act(XTF_BAD_VALUE, "MASQUERADE", "--to-ports", arg);
}
-static void MASQUERADE_parse(struct xt_option_call *cb)
+static void range_to_psid_args(const struct nf_nat_range2 *r, unsigned int *offset_len,
+ unsigned int *psid, unsigned int *psid_len)
+{
+ unsigned int offset, power_j, psid_mask;
+
+ power_j = ntohs(r->max_proto.all) - ntohs(r->min_proto.all) + 1;
+ offset = ntohs(r->base_proto.all);
+ if (offset == 0)
+ offset = 1 << 16;
+
+ *offset_len = 16 - _log2(offset);
+ *psid_len = _log2(offset / power_j);
+ psid_mask = ((1 << *psid_len) - 1) * power_j;
+ *psid = (ntohs(r->min_proto.all) & psid_mask) >> _log2(power_j);
+}
+
+static void parse_psid(const char *arg, struct nf_nat_range2 *r)
+{
+ char *end;
+ unsigned int offset_len, psid, psid_len, min, offset;
+
+ if (!xtables_strtoui(arg, &end, &offset_len, 0, 16) || *end != ':')
+ xtables_param_act(XTF_BAD_VALUE,
+ "MASQUERADE", "--psid <offset_length> invalid", arg);
+
+ if (!xtables_strtoui(end + 1, &end, &psid, 0, UINT16_MAX) || *end != ':')
+ xtables_param_act(XTF_BAD_VALUE, "MASQUERADE", "--psid <psid> invalid", arg);
+
+ if (!xtables_strtoui(end + 1, &end, &psid_len, 0, 16) || *end != '\0')
+ xtables_param_act(XTF_BAD_VALUE, "MASQUERADE", "--psid <psid_length> invalid", arg);
+
+ if (psid >= (1 << psid_len))
+ xtables_param_act(XTF_BAD_VALUE, "MASQUERADE",
+ "--psid <psid> too large for <psid_length>", arg);
+
+ if (psid_len + offset_len >= 16)
+ xtables_param_act(XTF_BAD_VALUE, "MASQUERADE",
+ "--psid <offset_length> and/or <psid_length> are too large", arg);
+
+ offset = (1 << (16 - offset_len));
+ psid = psid << (16 - offset_len - psid_len);
+
+ /* Handle the special case of no offset bits (a=0), so offset loops */
+ min = psid;
+ if (offset)
+ min += offset;
+
+ r->min_proto.all = htons(min);
+ r->max_proto.all = htons(min + ((1 << (16 - offset_len - psid_len)) - 1));
+ r->base_proto.all = htons(offset);
+ r->flags |= NF_NAT_RANGE_PSID;
+ r->flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
+}
+
+static void _MASQUERADE_parse(struct xt_option_call *cb, struct nf_nat_range2 *r, int rev)
{
const struct ipt_entry *entry = cb->xt_entry;
int portok;
- struct nf_nat_ipv4_multi_range_compat *mr = cb->data;
if (entry->ip.proto == IPPROTO_TCP
|| entry->ip.proto == IPPROTO_UDP
@@ -96,29 +196,50 @@ static void MASQUERADE_parse(struct xt_option_call *cb)
if (!portok)
xtables_error(PARAMETER_PROBLEM,
"Need TCP, UDP, SCTP or DCCP with port specification");
- parse_ports(cb->arg, mr);
+ parse_ports(cb->arg, r);
break;
case O_RANDOM:
- mr->range[0].flags |= NF_NAT_RANGE_PROTO_RANDOM;
+ r->flags |= NF_NAT_RANGE_PROTO_RANDOM;
break;
case O_RANDOM_FULLY:
- mr->range[0].flags |= NF_NAT_RANGE_PROTO_RANDOM_FULLY;
+ r->flags |= NF_NAT_RANGE_PROTO_RANDOM_FULLY;
+ break;
+ case O_PSID:
+ parse_psid(cb->arg, r);
break;
}
}
-static void
-MASQUERADE_print(const void *ip, const struct xt_entry_target *target,
- int numeric)
+static void MASQUERADE_parse_v0(struct xt_option_call *cb)
+{
+ struct nf_nat_ipv4_multi_range_compat *mr = (void *)cb->data;
+ struct nf_nat_range2 r = {};
+
+ cpy_ipv4_range_to_range2(&r, &mr->range[0]);
+ _MASQUERADE_parse(cb, &r, 0);
+ cpy_range2_to_ipv4_range(&mr->range[0], &r);
+}
+
+static void MASQUERADE_parse_v1(struct xt_option_call *cb)
+{
+ _MASQUERADE_parse(cb, (struct nf_nat_range2 *)cb->data, 1);
+}
+
+static void _MASQUERADE_print(const struct nf_nat_range2 *r, int rev)
{
- const struct nf_nat_ipv4_multi_range_compat *mr = (const void *)target->data;
- const struct nf_nat_ipv4_range *r = &mr->range[0];
if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
- printf(" masq ports: ");
- printf("%hu", ntohs(r->min.tcp.port));
- if (r->max.tcp.port != r->min.tcp.port)
- printf("-%hu", ntohs(r->max.tcp.port));
+ if (r->flags & NF_NAT_RANGE_PSID) {
+ unsigned int offset, psid, psid_length;
+
+ range_to_psid_args(r, &offset, &psid, &psid_length);
+ printf(" masq psid: %hu:%hu:%hu", offset, psid, psid_length);
+ } else {
+ printf(" masq ports: ");
+ printf("%hu", ntohs(r->min_proto.tcp.port));
+ if (r->max_proto.tcp.port != r->min_proto.tcp.port)
+ printf("-%hu", ntohs(r->max_proto.tcp.port));
+ }
}
if (r->flags & NF_NAT_RANGE_PROTO_RANDOM)
@@ -126,18 +247,37 @@ MASQUERADE_print(const void *ip, const struct xt_entry_target *target,
if (r->flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY)
printf(" random-fully");
+
+
}
-static void
-MASQUERADE_save(const void *ip, const struct xt_entry_target *target)
+static void MASQUERADE_print_v0(const void *ip, const struct xt_entry_target *target, int numeric)
{
const struct nf_nat_ipv4_multi_range_compat *mr = (const void *)target->data;
- const struct nf_nat_ipv4_range *r = &mr->range[0];
+ struct nf_nat_range2 r = {};
+
+ cpy_ipv4_range_to_range2(&r, &mr->range[0]);
+ _MASQUERADE_print(&r, 0);
+}
+
+static void MASQUERADE_print_v1(const void *ip, const struct xt_entry_target *target, int numeric)
+{
+ _MASQUERADE_print((const struct nf_nat_range2 *)target->data, 1);
+}
+static void _MASQUERADE_save(const struct nf_nat_range2 *r, int rev)
+{
if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
- printf(" --to-ports %hu", ntohs(r->min.tcp.port));
- if (r->max.tcp.port != r->min.tcp.port)
- printf("-%hu", ntohs(r->max.tcp.port));
+ if (r->flags & NF_NAT_RANGE_PSID) {
+ unsigned int offset, psid, psid_length;
+
+ range_to_psid_args(r, &offset, &psid, &psid_length);
+ printf(" --psid %hu:%hu:%hu", offset, psid, psid_length);
+ } else {
+ printf(" --to-ports %hu", ntohs(r->min_proto.tcp.port));
+ if (r->max_proto.tcp.port != r->min_proto.tcp.port)
+ printf("-%hu", ntohs(r->max_proto.tcp.port));
+ }
}
if (r->flags & NF_NAT_RANGE_PROTO_RANDOM)
@@ -147,44 +287,93 @@ MASQUERADE_save(const void *ip, const struct xt_entry_target *target)
printf(" --random-fully");
}
-static int MASQUERADE_xlate(struct xt_xlate *xl,
- const struct xt_xlate_tg_params *params)
+static void MASQUERADE_save_v0(const void *ip, const struct xt_entry_target *target)
{
- const struct nf_nat_ipv4_multi_range_compat *mr =
- (const void *)params->target->data;
- const struct nf_nat_ipv4_range *r = &mr->range[0];
+ const struct nf_nat_ipv4_multi_range_compat *mr = (const void *)target->data;
+ struct nf_nat_range2 r = {};
+
+ cpy_ipv4_range_to_range2(&r, &mr->range[0]);
+ _MASQUERADE_save(&r, 0);
+}
+
+static void MASQUERADE_save_v1(const void *ip, const struct xt_entry_target *target)
+{
+ _MASQUERADE_save((const struct nf_nat_range2 *)target->data, 1);
+}
+static void _MASQUERADE_xlate(struct xt_xlate *xl, const struct nf_nat_range2 *r, int rev)
+{
xt_xlate_add(xl, "masquerade");
if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
- xt_xlate_add(xl, " to :%hu", ntohs(r->min.tcp.port));
- if (r->max.tcp.port != r->min.tcp.port)
- xt_xlate_add(xl, "-%hu", ntohs(r->max.tcp.port));
- }
+ if (r->flags & NF_NAT_RANGE_PSID) {
+ unsigned int offset, psid, psid_length;
+
+ range_to_psid_args(r, &offset, &psid, &psid_length);
+ xt_xlate_add(xl, " psid %hu:%hu:%hu", offset, psid, psid_length);
+ } else {
+ xt_xlate_add(xl, " to :%hu", ntohs(r->min_proto.tcp.port));
+ if (r->max_proto.tcp.port != r->min_proto.tcp.port)
+ xt_xlate_add(xl, "-%hu", ntohs(r->max_proto.tcp.port));
+ }
+ }
xt_xlate_add(xl, " ");
if (r->flags & NF_NAT_RANGE_PROTO_RANDOM)
xt_xlate_add(xl, "random ");
+}
+static int MASQUERADE_xlate_v0(struct xt_xlate *xl, const struct xt_xlate_tg_params *params)
+{
+ const struct nf_nat_ipv4_multi_range_compat *mr =
+ (const void *)params->target->data;
+ struct nf_nat_range2 r = {};
+
+ cpy_ipv4_range_to_range2(&r, &mr->range[0]);
+ _MASQUERADE_xlate(xl, &r, 0);
+
+ return 1;
+}
+
+static int MASQUERADE_xlate_v1(struct xt_xlate *xl, const struct xt_xlate_tg_params *params)
+{
+ _MASQUERADE_xlate(xl, (const struct nf_nat_range2 *)params->target->data, 1);
return 1;
}
-static struct xtables_target masquerade_tg_reg = {
- .name = "MASQUERADE",
- .version = XTABLES_VERSION,
- .family = NFPROTO_IPV4,
- .size = XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
- .userspacesize = XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
- .help = MASQUERADE_help,
- .init = MASQUERADE_init,
- .x6_parse = MASQUERADE_parse,
- .print = MASQUERADE_print,
- .save = MASQUERADE_save,
- .x6_options = MASQUERADE_opts,
- .xlate = MASQUERADE_xlate,
+static struct xtables_target masquerade_tg_reg[] = {
+ {
+ .name = "MASQUERADE",
+ .version = XTABLES_VERSION,
+ .family = NFPROTO_IPV4,
+ .revision = 0,
+ .size = XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
+ .userspacesize = XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
+ .help = MASQUERADE_help_v0,
+ .init = MASQUERADE_init_v0,
+ .x6_parse = MASQUERADE_parse_v0,
+ .print = MASQUERADE_print_v0,
+ .save = MASQUERADE_save_v0,
+ .x6_options = MASQUERADE_opts_v0,
+ .xlate = MASQUERADE_xlate_v0,
+ },
+ {
+ .name = "MASQUERADE",
+ .version = XTABLES_VERSION,
+ .family = NFPROTO_IPV4,
+ .revision = 1,
+ .size = XT_ALIGN(sizeof(struct nf_nat_range2)),
+ .userspacesize = XT_ALIGN(sizeof(struct nf_nat_range2)),
+ .help = MASQUERADE_help_v1,
+ .x6_parse = MASQUERADE_parse_v1,
+ .print = MASQUERADE_print_v1,
+ .save = MASQUERADE_save_v1,
+ .x6_options = MASQUERADE_opts_v1,
+ .xlate = MASQUERADE_xlate_v1,
+ },
};
void _init(void)
{
- xtables_register_target(&masquerade_tg_reg);
+ xtables_register_targets(masquerade_tg_reg, ARRAY_SIZE(masquerade_tg_reg));
}
@@ -7,3 +7,11 @@
-p udp -j MASQUERADE --to-ports 1024-65535;=;OK
-p udp -j MASQUERADE --to-ports 1024-65536;;FAIL
-p udp -j MASQUERADE --to-ports -1;;FAIL
+-j MASQUERADE --psid 0:52:8;=;OK
+-j MASQUERADE --psid 6;;FAIL
+-j MASQUERADE --psid 6:0;;FAIL
+-j MASQUERADE --psid 6:0:8;=;OK
+-j MASQUERADE --psid -6:-52:-8;=;FAIL
+-j MASQUERADE --psid 6:52:8;=;OK
+-j MASQUERADE --psid 6:270:8;;FAIL
+-j MASQUERADE --psid 6:270:8;;FAIL
@@ -32,4 +32,8 @@ If option
\fB\-\-random-fully\fP
is used then port mapping will be fully randomized (kernel >= 3.13).
.TP
-IPv6 support available since Linux kernels >= 3.7.
+\fB\-\-psid\fP \fIoffset_length\fB:\fIpsid\fB:\fIpsid_length
+This specifies a range of source ports to use based on RFC-7597 PSID (kernel >= 5.13), overriding the source ports.
+ \fIoffset_length\fP : Excluded ports (0 to 2^(16 - \fIoffset_length\fP) - 1).
+ \fIpsid\fP : Selects the port ranges used by this rule.
+ \fIpsid_length\fP : Bit-length of the \fIpsid\fP field.
@@ -10,6 +10,8 @@
#define NF_NAT_RANGE_PERSISTENT (1 << 3)
#define NF_NAT_RANGE_PROTO_RANDOM_FULLY (1 << 4)
#define NF_NAT_RANGE_PROTO_OFFSET (1 << 5)
+#define NF_NAT_RANGE_NETMAP (1 << 6)
+#define NF_NAT_RANGE_PSID (1 << 7)
#define NF_NAT_RANGE_PROTO_RANDOM_ALL \
(NF_NAT_RANGE_PROTO_RANDOM | NF_NAT_RANGE_PROTO_RANDOM_FULLY)
@@ -17,7 +19,8 @@
#define NF_NAT_RANGE_MASK \
(NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED | \
NF_NAT_RANGE_PROTO_RANDOM | NF_NAT_RANGE_PERSISTENT | \
- NF_NAT_RANGE_PROTO_RANDOM_FULLY | NF_NAT_RANGE_PROTO_OFFSET)
+ NF_NAT_RANGE_PROTO_RANDOM_FULLY | NF_NAT_RANGE_PROTO_OFFSET | \
+ NF_NAT_RANGE_NETMAP | NF_NAT_RANGE_PSID)
struct nf_nat_ipv4_range {
unsigned int flags;
Added --psid option to masquerade extension to specify port ranges, as described in RFC-7597 section 5.1. The PSID option needs the base field in range2, so add version 1 of the masquerade extension. Signed-off-by: Cole Dishington <Cole.Dishington@alliedtelesis.co.nz> --- Notes: Thanks for your time reviewing. Changes in v3: - Add support for a=0 (A=2^16). Treat as special case for base_proto (=0) as 2^16 doesn't fit in u16. - Change --psid option to using offset_length rather than offset, due to above. - Add extra error handling cases for psid arguments. - Add extra test case for special case of a=0. extensions/libipt_MASQUERADE.c | 295 +++++++++++++++++++++++++------ extensions/libipt_MASQUERADE.t | 8 + extensions/libxt_MASQUERADE.man | 6 +- include/linux/netfilter/nf_nat.h | 5 +- 4 files changed, 259 insertions(+), 55 deletions(-)