Skip to content

Commit 2aa2109

Browse files
kurotakakprovost
authored andcommitted
pf: Implement the NAT source port selection of MAP-E Customer Edge
MAP-E (RFC 7597) requires special care for selecting source ports in NAT operation on the Customer Edge because a part of bits of the port numbers are used by the Border Relay to distinguish another side of the IPv4-over-IPv6 tunnel. PR: 254577 Reviewed by: kp Differential Revision: https://reviews.freebsd.org/D29468
1 parent e6ab1e3 commit 2aa2109

File tree

11 files changed

+303
-10
lines changed

11 files changed

+303
-10
lines changed

lib/libpfctl/libpfctl.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,18 @@ pf_nvrule_addr_to_rule_addr(const nvlist_t *nvl, struct pf_rule_addr *addr)
196196
addr->port_op = nvlist_get_number(nvl, "port_op");
197197
}
198198

199+
static void
200+
pfctl_nv_add_mape(nvlist_t *nvparent, const char *name,
201+
const struct pf_mape_portset *mape)
202+
{
203+
nvlist_t *nvl = nvlist_create(0);
204+
205+
nvlist_add_number(nvl, "offset", mape->offset);
206+
nvlist_add_number(nvl, "psidlen", mape->psidlen);
207+
nvlist_add_number(nvl, "psid", mape->psid);
208+
nvlist_add_nvlist(nvparent, name, nvl);
209+
}
210+
199211
static void
200212
pfctl_nv_add_pool(nvlist_t *nvparent, const char *name,
201213
const struct pfctl_pool *pool)
@@ -211,10 +223,19 @@ pfctl_nv_add_pool(nvlist_t *nvparent, const char *name,
211223
ports[1] = pool->proxy_port[1];
212224
nvlist_add_number_array(nvl, "proxy_port", ports, 2);
213225
nvlist_add_number(nvl, "opts", pool->opts);
226+
pfctl_nv_add_mape(nvl, "mape", &pool->mape);
214227

215228
nvlist_add_nvlist(nvparent, name, nvl);
216229
}
217230

231+
static void
232+
pf_nvmape_to_mape(const nvlist_t *nvl, struct pf_mape_portset *mape)
233+
{
234+
mape->offset = nvlist_get_number(nvl, "offset");
235+
mape->psidlen = nvlist_get_number(nvl, "psidlen");
236+
mape->psid = nvlist_get_number(nvl, "psid");
237+
}
238+
218239
static void
219240
pf_nvpool_to_pool(const nvlist_t *nvl, struct pfctl_pool *pool)
220241
{
@@ -230,6 +251,9 @@ pf_nvpool_to_pool(const nvlist_t *nvl, struct pfctl_pool *pool)
230251
pool->tblidx = nvlist_get_number(nvl, "tblidx");
231252
pf_nvuint_16_array(nvl, "proxy_port", 2, pool->proxy_port, NULL);
232253
pool->opts = nvlist_get_number(nvl, "opts");
254+
255+
if (nvlist_exists_nvlist(nvl, "mape"))
256+
pf_nvmape_to_mape(nvlist_get_nvlist(nvl, "mape"), &pool->mape);
233257
}
234258

235259
static void

lib/libpfctl/libpfctl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ struct pfctl_pool {
4343
struct pf_pooladdr *cur;
4444
struct pf_poolhashkey key;
4545
struct pf_addr counter;
46+
struct pf_mape_portset mape;
4647
int tblidx;
4748
u_int16_t proxy_port[2];
4849
u_int8_t opts;

sbin/pfctl/parse.y

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ static struct pool_opts {
306306
int type;
307307
int staticport;
308308
struct pf_poolhashkey *key;
309+
struct pf_mape_portset mape;
309310

310311
} pool_opts;
311312

@@ -461,7 +462,7 @@ int parseport(char *, struct range *r, int);
461462
%token SET OPTIMIZATION TIMEOUT LIMIT LOGINTERFACE BLOCKPOLICY FAILPOLICY
462463
%token RANDOMID REQUIREORDER SYNPROXY FINGERPRINTS NOSYNC DEBUG SKIP HOSTID
463464
%token ANTISPOOF FOR INCLUDE
464-
%token BITMASK RANDOM SOURCEHASH ROUNDROBIN STATICPORT PROBABILITY
465+
%token BITMASK RANDOM SOURCEHASH ROUNDROBIN STATICPORT PROBABILITY MAPEPORTSET
465466
%token ALTQ CBQ CODEL PRIQ HFSC FAIRQ BANDWIDTH TBRSIZE LINKSHARE REALTIME
466467
%token UPPERLIMIT QUEUE PRIORITY QLIMIT HOGS BUCKETS RTABLE TARGET INTERVAL
467468
%token LOAD RULESET_OPTIMIZATION PRIO
@@ -4015,6 +4016,36 @@ pool_opt : BITMASK {
40154016
pool_opts.marker |= POM_STICKYADDRESS;
40164017
pool_opts.opts |= PF_POOL_STICKYADDR;
40174018
}
4019+
| MAPEPORTSET number '/' number '/' number {
4020+
if (pool_opts.mape.offset) {
4021+
yyerror("map-e-portset cannot be redefined");
4022+
YYERROR;
4023+
}
4024+
if (pool_opts.type) {
4025+
yyerror("map-e-portset cannot be used with "
4026+
"address pools");
4027+
YYERROR;
4028+
}
4029+
if ($2 <= 0 || $2 >= 16) {
4030+
yyerror("MAP-E PSID offset must be 1-15");
4031+
YYERROR;
4032+
}
4033+
if ($4 < 0 || $4 >= 16 || $2 + $4 > 16) {
4034+
yyerror("Invalid MAP-E PSID length");
4035+
YYERROR;
4036+
} else if ($4 == 0) {
4037+
yyerror("PSID Length = 0: this means"
4038+
" you do not need MAP-E");
4039+
YYERROR;
4040+
}
4041+
if ($6 < 0 || $6 > 65535) {
4042+
yyerror("Invalid MAP-E PSID");
4043+
YYERROR;
4044+
}
4045+
pool_opts.mape.offset = $2;
4046+
pool_opts.mape.psidlen = $4;
4047+
pool_opts.mape.psid = $6;
4048+
}
40184049
;
40194050

40204051
redirection : /* empty */ { $$ = NULL; }
@@ -4220,6 +4251,29 @@ natrule : nataction interface af proto fromto tag tagged rtable
42204251
r.rpool.proxy_port[1] = 0;
42214252
}
42224253

4254+
if ($10.mape.offset) {
4255+
if (r.action != PF_NAT) {
4256+
yyerror("the 'map-e-portset' option is"
4257+
" only valid with nat rules");
4258+
YYERROR;
4259+
}
4260+
if ($10.staticport) {
4261+
yyerror("the 'map-e-portset' option"
4262+
" can't be used 'static-port'");
4263+
YYERROR;
4264+
}
4265+
if (r.rpool.proxy_port[0] !=
4266+
PF_NAT_PROXY_PORT_LOW &&
4267+
r.rpool.proxy_port[1] !=
4268+
PF_NAT_PROXY_PORT_HIGH) {
4269+
yyerror("the 'map-e-portset' option"
4270+
" can't be used when specifying"
4271+
" a port range");
4272+
YYERROR;
4273+
}
4274+
r.rpool.mape = $10.mape;
4275+
}
4276+
42234277
expand_rule(&r, $2, $9 == NULL ? NULL : $9->host, $4,
42244278
$5.src_os, $5.src.host, $5.src.port, $5.dst.host,
42254279
$5.dst.port, 0, 0, 0, "");
@@ -5545,6 +5599,7 @@ lookup(char *s)
55455599
{ "load", LOAD},
55465600
{ "log", LOG},
55475601
{ "loginterface", LOGINTERFACE},
5602+
{ "map-e-portset", MAPEPORTSET},
55485603
{ "max", MAXIMUM},
55495604
{ "max-mss", MAXMSS},
55505605
{ "max-src-conn", MAXSRCCONN},

sbin/pfctl/pfctl_parser.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,9 @@ print_pool(struct pfctl_pool *pool, u_int16_t p1, u_int16_t p2,
486486
printf(" sticky-address");
487487
if (id == PF_NAT && p1 == 0 && p2 == 0)
488488
printf(" static-port");
489+
if (pool->mape.offset > 0)
490+
printf(" map-e-portset %u/%u/%u",
491+
pool->mape.offset, pool->mape.psidlen, pool->mape.psid);
489492
}
490493

491494
const char * const pf_reasons[PFRES_MAX+1] = PFRES_NAMES;

share/man/man5/pf.conf.5

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1998,6 +1998,27 @@ rules, the
19981998
option prevents
19991999
.Xr pf 4
20002000
from modifying the source port on TCP and UDP packets.
2001+
.It Xo Ar map-e-portset Aq Ar psid-offset
2002+
.No / Aq Ar psid-len
2003+
.No / Aq Ar psid
2004+
.Xc
2005+
With
2006+
.Ar nat
2007+
rules, the
2008+
.Ar map-e-portset
2009+
option enables the source port translation of MAP-E (RFC 7597) Customer Edge.
2010+
In order to make the host act as a MAP-E Customer Edge, setting up a tunneling
2011+
interface and pass rules for encapsulated packets are required in addition
2012+
to the map-e-portset nat rule.
2013+
.Pp
2014+
For example:
2015+
.Bd -literal -offset indent
2016+
nat on $gif_mape_if from $int_if:network to any \e
2017+
-> $ipv4_mape_src map-e-portset 6/8/0x34
2018+
.Ed
2019+
.Pp
2020+
sets PSID offset 6, PSID length 8, PSID 0x34.
2021+
.Ed
20012022
.El
20022023
.Pp
20032024
Additionally, the
@@ -2893,7 +2914,8 @@ nat-rule = [ "no" ] "nat" [ "pass" [ "log" [ "(" logopts ")" ] ] ]
28932914
[ "on" ifspec ] [ af ]
28942915
[ protospec ] hosts [ "tag" string ] [ "tagged" string ]
28952916
[ "-\*(Gt" ( redirhost | "{" redirhost-list "}" )
2896-
[ portspec ] [ pooltype ] [ "static-port" ] ]
2917+
[ portspec ] [ pooltype ] [ "static-port" ]
2918+
[ "map-e-portset" number "/" number "/" number ] ]
28972919

28982920
binat-rule = [ "no" ] "binat" [ "pass" [ "log" [ "(" logopts ")" ] ] ]
28992921
[ "on" interface-name ] [ af ]

sys/net/pfvar.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ struct pf_kpool {
309309
struct pf_kpooladdr *cur;
310310
struct pf_poolhashkey key;
311311
struct pf_addr counter;
312+
struct pf_mape_portset mape;
312313
int tblidx;
313314
u_int16_t proxy_port[2];
314315
u_int8_t opts;

sys/netpfil/pf/pf.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,12 @@ struct pf_poolhashkey {
317317
#define key32 pfk.key32
318318
};
319319

320+
struct pf_mape_portset {
321+
u_int8_t offset;
322+
u_int8_t psidlen;
323+
u_int16_t psid;
324+
};
325+
320326
struct pf_pool {
321327
struct pf_palist list;
322328
struct pf_pooladdr *cur;

sys/netpfil/pf/pf_ioctl.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1644,6 +1644,36 @@ pf_addr_to_nvaddr(const struct pf_addr *paddr)
16441644
return (nvl);
16451645
}
16461646

1647+
static int
1648+
pf_nvmape_to_mape(const nvlist_t *nvl, struct pf_mape_portset *mape)
1649+
{
1650+
int error = 0;
1651+
1652+
bzero(mape, sizeof(*mape));
1653+
PFNV_CHK(pf_nvuint8(nvl, "offset", &mape->offset));
1654+
PFNV_CHK(pf_nvuint8(nvl, "psidlen", &mape->psidlen));
1655+
PFNV_CHK(pf_nvuint16(nvl, "psid", &mape->psid));
1656+
1657+
errout:
1658+
return (error);
1659+
}
1660+
1661+
static nvlist_t *
1662+
pf_mape_to_nvmape(const struct pf_mape_portset *mape)
1663+
{
1664+
nvlist_t *nvl;
1665+
1666+
nvl = nvlist_create(0);
1667+
if (nvl == NULL)
1668+
return (NULL);
1669+
1670+
nvlist_add_number(nvl, "offset", mape->offset);
1671+
nvlist_add_number(nvl, "psidlen", mape->psidlen);
1672+
nvlist_add_number(nvl, "psid", mape->psid);
1673+
1674+
return (nvl);
1675+
}
1676+
16471677
static int
16481678
pf_nvpool_to_pool(const nvlist_t *nvl, struct pf_kpool *kpool)
16491679
{
@@ -1663,6 +1693,11 @@ pf_nvpool_to_pool(const nvlist_t *nvl, struct pf_kpool *kpool)
16631693
NULL));
16641694
PFNV_CHK(pf_nvuint8(nvl, "opts", &kpool->opts));
16651695

1696+
if (nvlist_exists_nvlist(nvl, "mape")) {
1697+
PFNV_CHK(pf_nvmape_to_mape(nvlist_get_nvlist(nvl, "mape"),
1698+
&kpool->mape));
1699+
}
1700+
16661701
errout:
16671702
return (error);
16681703
}
@@ -1687,6 +1722,11 @@ pf_pool_to_nvpool(const struct pf_kpool *pool)
16871722
pf_uint16_array_nv(nvl, "proxy_port", pool->proxy_port, 2);
16881723
nvlist_add_number(nvl, "opts", pool->opts);
16891724

1725+
tmp = pf_mape_to_nvmape(&pool->mape);
1726+
if (tmp == NULL)
1727+
goto error;
1728+
nvlist_add_nvlist(nvl, "mape", tmp);
1729+
16901730
return (nvl);
16911731

16921732
error:

sys/netpfil/pf/pf_lb.c

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -224,11 +224,6 @@ pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_krule *r,
224224
if (pf_map_addr(af, r, saddr, naddr, &init_addr, sn))
225225
return (1);
226226

227-
if (proto == IPPROTO_ICMP) {
228-
low = 1;
229-
high = 65535;
230-
}
231-
232227
bzero(&key, sizeof(key));
233228
key.af = af;
234229
key.proto = proto;
@@ -310,6 +305,42 @@ pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_krule *r,
310305
return (1); /* none available */
311306
}
312307

308+
static int
309+
pf_get_mape_sport(sa_family_t af, u_int8_t proto, struct pf_krule *r,
310+
struct pf_addr *saddr, uint16_t sport, struct pf_addr *daddr,
311+
uint16_t dport, struct pf_addr *naddr, uint16_t *nport,
312+
struct pf_ksrc_node **sn)
313+
{
314+
uint16_t psmask, low, highmask;
315+
uint16_t i, ahigh, cut;
316+
int ashift, psidshift;
317+
318+
ashift = 16 - r->rpool.mape.offset;
319+
psidshift = ashift - r->rpool.mape.psidlen;
320+
psmask = r->rpool.mape.psid & ((1U << r->rpool.mape.psidlen) - 1);
321+
psmask = psmask << psidshift;
322+
highmask = (1U << psidshift) - 1;
323+
324+
ahigh = (1U << r->rpool.mape.offset) - 1;
325+
cut = arc4random() & ahigh;
326+
if (cut == 0)
327+
cut = 1;
328+
329+
for (i = cut; i <= ahigh; i++) {
330+
low = (i << ashift) | psmask;
331+
if (!pf_get_sport(af, proto, r, saddr, sport, daddr, dport,
332+
naddr, nport, low, low | highmask, sn))
333+
return (0);
334+
}
335+
for (i = cut - 1; i > 0; i--) {
336+
low = (i << ashift) | psmask;
337+
if (!pf_get_sport(af, proto, r, saddr, sport, daddr, dport,
338+
naddr, nport, low, low | highmask, sn))
339+
return (0);
340+
}
341+
return (1);
342+
}
343+
313344
int
314345
pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
315346
struct pf_addr *naddr, struct pf_addr *init_addr, struct pf_ksrc_node **sn)
@@ -530,6 +561,7 @@ pf_get_translation(struct pf_pdesc *pd, struct mbuf *m, int off, int direction,
530561
struct pf_krule *r = NULL;
531562
struct pf_addr *naddr;
532563
uint16_t *nport;
564+
uint16_t low, high;
533565

534566
PF_RULES_RASSERT();
535567
KASSERT(*skp == NULL, ("*skp not NULL"));
@@ -577,9 +609,26 @@ pf_get_translation(struct pf_pdesc *pd, struct mbuf *m, int off, int direction,
577609

578610
switch (r->action) {
579611
case PF_NAT:
580-
if (pf_get_sport(pd->af, pd->proto, r, saddr, sport, daddr,
581-
dport, naddr, nport, r->rpool.proxy_port[0],
582-
r->rpool.proxy_port[1], sn)) {
612+
if (pd->proto == IPPROTO_ICMP) {
613+
low = 1;
614+
high = 65535;
615+
} else {
616+
low = r->rpool.proxy_port[0];
617+
high = r->rpool.proxy_port[1];
618+
}
619+
if (r->rpool.mape.offset > 0) {
620+
if (pf_get_mape_sport(pd->af, pd->proto, r, saddr,
621+
sport, daddr, dport, naddr, nport, sn)) {
622+
DPFPRINTF(PF_DEBUG_MISC,
623+
("pf: MAP-E port allocation (%u/%u/%u)"
624+
" failed\n",
625+
r->rpool.mape.offset,
626+
r->rpool.mape.psidlen,
627+
r->rpool.mape.psid));
628+
goto notrans;
629+
}
630+
} else if (pf_get_sport(pd->af, pd->proto, r, saddr, sport,
631+
daddr, dport, naddr, nport, low, high, sn)) {
583632
DPFPRINTF(PF_DEBUG_MISC,
584633
("pf: NAT proxy port allocation (%u-%u) failed\n",
585634
r->rpool.proxy_port[0], r->rpool.proxy_port[1]));

tests/sys/netpfil/pf/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ ATF_TESTS_SH+= altq \
1212
forward \
1313
fragmentation \
1414
icmp \
15+
map_e \
1516
names \
1617
nat \
1718
pass_block \

0 commit comments

Comments
 (0)