diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y --- a/sbin/pfctl/parse.y +++ b/sbin/pfctl/parse.y @@ -306,6 +306,7 @@ int type; int staticport; struct pf_poolhashkey *key; + struct pf_mape_portset mape; } pool_opts; @@ -461,7 +462,7 @@ %token SET OPTIMIZATION TIMEOUT LIMIT LOGINTERFACE BLOCKPOLICY FAILPOLICY %token RANDOMID REQUIREORDER SYNPROXY FINGERPRINTS NOSYNC DEBUG SKIP HOSTID %token ANTISPOOF FOR INCLUDE -%token BITMASK RANDOM SOURCEHASH ROUNDROBIN STATICPORT PROBABILITY +%token BITMASK RANDOM SOURCEHASH ROUNDROBIN STATICPORT PROBABILITY MAPEPORTSET %token ALTQ CBQ CODEL PRIQ HFSC FAIRQ BANDWIDTH TBRSIZE LINKSHARE REALTIME %token UPPERLIMIT QUEUE PRIORITY QLIMIT HOGS BUCKETS RTABLE TARGET INTERVAL %token LOAD RULESET_OPTIMIZATION PRIO @@ -4007,6 +4008,36 @@ pool_opts.marker |= POM_STICKYADDRESS; pool_opts.opts |= PF_POOL_STICKYADDR; } + | MAPEPORTSET number '/' number '/' number { + if (pool_opts.mape.offset) { + yyerror("map-e-portset cannot be redefined"); + YYERROR; + } + if (pool_opts.type) { + yyerror("map-e-portset cannot be used with " + "address pools"); + YYERROR; + } + if ($2 < 0 || $2 >= 16) { + yyerror("MAP-E PSID offset must be 1-15"); + YYERROR; + } + if ($4 < 0 || $4 >= 16 || $2 + $4 > 16) { + yyerror("Invalid MAP-E PSID length"); + YYERROR; + } else if ($4 == 0) { + yyerror("PSID Length = 0: this means" + " you do not need MAP-E"); + YYERROR; + } + if ($6 < 0 || $6 > 65535) { + yyerror("Invalid MAP-E PSID"); + YYERROR; + } + pool_opts.mape.offset = $2; + pool_opts.mape.psidlen = $4; + pool_opts.mape.psid = $6; + } ; redirection : /* empty */ { $$ = NULL; } @@ -4212,6 +4243,29 @@ r.rpool.proxy_port[1] = 0; } + if ($10.mape.offset) { + if (r.action != PF_NAT) { + yyerror("the 'map-e-portset' option is" + " only valid with nat rules"); + YYERROR; + } + if ($10.staticport) { + yyerror("the 'map-e-portset' option" + " can't be used 'static-port'"); + YYERROR; + } + if (r.rpool.proxy_port[0] != + PF_NAT_PROXY_PORT_LOW && + r.rpool.proxy_port[1] != + PF_NAT_PROXY_PORT_HIGH) { + yyerror("the 'map-e-portset' option" + " can't be used when specifying" + " a port range"); + YYERROR; + } + r.rpool.mape = $10.mape; + } + expand_rule(&r, $2, $9 == NULL ? NULL : $9->host, $4, $5.src_os, $5.src.host, $5.src.port, $5.dst.host, $5.dst.port, 0, 0, 0, ""); @@ -5537,6 +5591,7 @@ { "load", LOAD}, { "log", LOG}, { "loginterface", LOGINTERFACE}, + { "map-e-portset", MAPEPORTSET}, { "max", MAXIMUM}, { "max-mss", MAXMSS}, { "max-src-conn", MAXSRCCONN}, diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c --- a/sbin/pfctl/pfctl_parser.c +++ b/sbin/pfctl/pfctl_parser.c @@ -486,6 +486,9 @@ printf(" sticky-address"); if (id == PF_NAT && p1 == 0 && p2 == 0) printf(" static-port"); + if (pool->mape.offset > 0) + printf(" map-e-portset %u/%u/%u", + pool->mape.offset, pool->mape.psidlen, pool->mape.psid); } const char * const pf_reasons[PFRES_MAX+1] = PFRES_NAMES; diff --git a/share/man/man5/pf.conf.5 b/share/man/man5/pf.conf.5 --- a/share/man/man5/pf.conf.5 +++ b/share/man/man5/pf.conf.5 @@ -1997,6 +1997,26 @@ option prevents .Xr pf 4 from modifying the source port on TCP and UDP packets. +.It Xo Ar map-e-portset Aq Ar psid-offset +.No / Aq Ar psid-len +.No / Aq Ar psid +.Xc +With +.Ar nat +rules, the +.Ar map-e-portset +option enables the MAP-E (RFC 7597) source port translation. +Setting up tunneling interface and pass rules for encapsulated packets +are required in addition to the MAP-E nat rule. +.Pp +For example: +.Bd -literal -offset indent +nat on $gif_mape_if from $int_if:network to any \e + -> $ipv4_mape_src map-e-portset 6/8/0x34 +.Ed +.Pp +sets PSID offset 6, PSID length 8, PSID 0x34. +.Ed .El .Pp Additionally, the @@ -2892,7 +2912,8 @@ [ "on" ifspec ] [ af ] [ protospec ] hosts [ "tag" string ] [ "tagged" string ] [ "-\*(Gt" ( redirhost | "{" redirhost-list "}" ) - [ portspec ] [ pooltype ] [ "static-port" ] ] + [ portspec ] [ pooltype ] [ "static-port" ] + [ "map-e-portset" number "/" number "/" number ] ] binat-rule = [ "no" ] "binat" [ "pass" [ "log" [ "(" logopts ")" ] ] ] [ "on" interface-name ] [ af ] diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -308,6 +308,7 @@ struct pf_kpooladdr *cur; struct pf_poolhashkey key; struct pf_addr counter; + struct pf_mape_portset mape; int tblidx; u_int16_t proxy_port[2]; u_int8_t opts; diff --git a/sys/netpfil/pf/pf.h b/sys/netpfil/pf/pf.h --- a/sys/netpfil/pf/pf.h +++ b/sys/netpfil/pf/pf.h @@ -311,11 +311,18 @@ #define key32 pfk.key32 }; +struct pf_mape_portset { + u_int8_t offset; + u_int8_t psidlen; + u_int16_t psid; +}; + struct pf_pool { struct pf_palist list; struct pf_pooladdr *cur; struct pf_poolhashkey key; struct pf_addr counter; + struct pf_mape_portset mape; int tblidx; u_int16_t proxy_port[2]; u_int8_t opts; diff --git a/sys/netpfil/pf/pf_lb.c b/sys/netpfil/pf/pf_lb.c --- a/sys/netpfil/pf/pf_lb.c +++ b/sys/netpfil/pf/pf_lb.c @@ -224,11 +224,6 @@ if (pf_map_addr(af, r, saddr, naddr, &init_addr, sn)) return (1); - if (proto == IPPROTO_ICMP) { - low = 1; - high = 65535; - } - bzero(&key, sizeof(key)); key.af = af; key.proto = proto; @@ -306,6 +301,42 @@ return (1); /* none available */ } +static int +pf_get_mape_sport(sa_family_t af, u_int8_t proto, struct pf_krule *r, + struct pf_addr *saddr, uint16_t sport, struct pf_addr *daddr, + uint16_t dport, struct pf_addr *naddr, uint16_t *nport, + struct pf_ksrc_node **sn) +{ + uint16_t psmask, low, highmask; + uint16_t i, ahigh, cut; + int ashift, psidshift; + + ashift = 16 - r->rpool.mape.offset; + psidshift = ashift - r->rpool.mape.psidlen; + psmask = r->rpool.mape.psid & ((1U << r->rpool.mape.psidlen) - 1); + psmask = psmask << psidshift; + highmask = (1U << psidshift) - 1; + + ahigh = (1U << r->rpool.mape.offset) - 1; + cut = arc4random() & ahigh; + if (cut == 0) + cut = 1; + + for (i = cut; i < ahigh; i++) { + low = (i << ashift) | psmask; + if (!pf_get_sport(af, proto, r, saddr, sport, daddr, dport, + naddr, nport, low, low | highmask, sn)) + return (0); + } + for (i = cut - 1; i > 0; i--) { + low = (i << ashift) | psmask; + if (!pf_get_sport(af, proto, r, saddr, sport, daddr, dport, + naddr, nport, low, low | highmask, sn)) + return (0); + } + return (1); +} + int pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr, struct pf_addr *naddr, struct pf_addr *init_addr, struct pf_ksrc_node **sn) @@ -526,6 +557,7 @@ struct pf_krule *r = NULL; struct pf_addr *naddr; uint16_t *nport; + uint16_t low, high; PF_RULES_RASSERT(); KASSERT(*skp == NULL, ("*skp not NULL")); @@ -573,9 +605,26 @@ switch (r->action) { case PF_NAT: - if (pf_get_sport(pd->af, pd->proto, r, saddr, sport, daddr, - dport, naddr, nport, r->rpool.proxy_port[0], - r->rpool.proxy_port[1], sn)) { + if (pd->proto == IPPROTO_ICMP) { + low = 1; + high = 65535; + } else { + low = r->rpool.proxy_port[0]; + high = r->rpool.proxy_port[1]; + } + if (r->rpool.mape.offset > 0) { + if (pf_get_mape_sport(pd->af, pd->proto, r, saddr, + sport, daddr, dport, naddr, nport, sn)) { + DPFPRINTF(PF_DEBUG_MISC, + ("pf: MAP-E port allocation (%u/%u/%u)" + " failed\n", + r->rpool.mape.offset, + r->rpool.mape.psidlen, + r->rpool.mape.psid)); + goto notrans; + } + } else if (pf_get_sport(pd->af, pd->proto, r, saddr, sport, + daddr, dport, naddr, nport, low, high, sn)) { DPFPRINTF(PF_DEBUG_MISC, ("pf: NAT proxy port allocation (%u-%u) failed\n", r->rpool.proxy_port[0], r->rpool.proxy_port[1]));