Allow ng_nat to be attached to a ethernet interface directly via ng_ether(4)

or the likes. Add new control message types: setdlt and getdlt to switch
from default DLT_RAW (no encapsulation) to DLT_EN10MB (ethernet).

Approved by:	glebius
MFC after:	1 month
Differential Revision:	https://reviews.freebsd.org/D18535
This commit is contained in:
Maxim Sobolev 2018-12-17 16:00:35 +00:00
parent 83eb920c6f
commit b7841ae650
3 changed files with 134 additions and 9 deletions

View File

@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd March 21, 2013
.Dd December 12, 2018
.Dt NG_NAT 4
.Os
.Sh NAME
@ -264,6 +264,38 @@ from its
.Xr libalias
instance, the corresponding field is returned as
.Va UINT32_MAX .
.It Dv NGM_NAT_SET_DLT Pq Ic setdlt
Sets the data link type on the
.Va in
and
.Va out
hooks.
Currently, supported types are
.Cm DLT_RAW
(raw IP datagrams , no offset applied, the default) and
.Cm DLT_EN10MB
(Ethernet). DLT_ definitions can be found in
.In net/bpf.h .
If you want to work on the
.Xr ipfw 8
level you must use no additional offset by specifying
.Cm DLT_RAW .
If, however, you attach
.Nm
to a network interface directly and
.Cm EN10MB
is specified, then the extra offset will be applied to take into account
link-level header.
In this mode the
.Nm
would also inspect appropriate type field in the Ethernet header and
pass-through any datagrams that are not IP packets.
.It Dv NGM_NAT_GET_DLT Pq Ic getdlt
This control message returns the current data link type of the
.Va in
and
.Va out
hooks.
.El
.Pp
In all redirection messages
@ -336,11 +368,31 @@ serial line with HDLC encapsulation.
SEQ
ifconfig ng0 x.y.8.35 x.y.8.1
.Ed
.Pp
The
.Nm
node can also be attached directly to the physical interface
via
.Xr ng_ether 4
node in the graph.
In the following example, we perform masquerading on a
Ethernet interface connected to a public network.
.Bd -literal -offset indent
ifconfig igb0 inet x.y.8.35 netmask 0xfffff000
route add default x.y.0.1
/usr/sbin/ngctl -f- <<-SEQ
mkpeer igb0: nat lower in
name igb0:lower igb0_NAT
connect igb0: igb0_NAT: upper out
msg igb0_NAT: setdlt 1
msg igb0_NAT: setaliasaddr x.y.8.35
SEQ
.Sh SEE ALSO
.Xr libalias 3 ,
.Xr ng_ipfw 4 ,
.Xr natd 8 ,
.Xr ngctl 8
.Xr ngctl 8 ,
.Xr ng_ether 8
.Sh HISTORY
The
.Nm

View File

@ -44,6 +44,9 @@
#include <netinet/tcp.h>
#include <machine/in_cksum.h>
#include <net/dlt.h>
#include <net/ethernet.h>
#include <netinet/libalias/alias.h>
#include <netinet/libalias/alias_local.h>
@ -241,6 +244,20 @@ static const struct ng_cmdlist ng_nat_cmdlist[] = {
NULL,
&ng_nat_libalias_info_type
},
{
NGM_NAT_COOKIE,
NGM_NAT_SET_DLT,
"setdlt",
&ng_parse_uint8_type,
NULL
},
{
NGM_NAT_COOKIE,
NGM_NAT_GET_DLT,
"getdlt",
NULL,
&ng_parse_uint8_type
},
{ 0 }
};
@ -277,6 +294,7 @@ struct ng_nat_priv {
uint32_t rdrcount; /* number or redirects in list */
uint32_t nextid; /* for next in turn in list */
struct rdrhead redirhead; /* redirect list header */
uint8_t dlt; /* DLT_XXX from bpf.h */
};
typedef struct ng_nat_priv *priv_p;
@ -302,6 +320,7 @@ ng_nat_constructor(node_p node)
/* Init redirects housekeeping. */
priv->rdrcount = 0;
priv->nextid = 1;
priv->dlt = DLT_RAW;
STAILQ_INIT(&priv->redirhead);
/* Link structs together. */
@ -694,11 +713,34 @@ ng_nat_rcvmsg(node_p node, item_p item, hook_p lasthook)
#undef COPY
}
break;
case NGM_NAT_SET_DLT:
if (msg->header.arglen != sizeof(uint8_t)) {
error = EINVAL;
break;
}
switch (*(uint8_t *) msg->data) {
case DLT_EN10MB:
case DLT_RAW:
priv->dlt = *(uint8_t *) msg->data;
break;
default:
error = EINVAL;
break;
}
break;
default:
error = EINVAL; /* unknown command */
break;
}
break;
case NGM_NAT_GET_DLT:
NG_MKRESPONSE(resp, msg, sizeof(uint8_t), M_WAITOK);
if (resp == NULL) {
error = ENOMEM;
break;
}
*((uint8_t *) resp->data) = priv->dlt;
break;
default:
error = EINVAL; /* unknown cookie type */
break;
@ -715,7 +757,7 @@ ng_nat_rcvdata(hook_p hook, item_p item )
const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
struct mbuf *m;
struct ip *ip;
int rval, error = 0;
int rval, ipofs, error = 0;
char *c;
/* We have no required hooks. */
@ -738,10 +780,37 @@ ng_nat_rcvdata(hook_p hook, item_p item )
NGI_M(item) = m;
c = mtod(m, char *);
ip = mtod(m, struct ip *);
switch (priv->dlt) {
case DLT_RAW:
ipofs = 0;
break;
case DLT_EN10MB:
{
struct ether_header *eh;
KASSERT(m->m_pkthdr.len == ntohs(ip->ip_len),
if (m->m_pkthdr.len < sizeof(struct ether_header)) {
NG_FREE_ITEM(item);
return (ENXIO);
}
eh = mtod(m, struct ether_header *);
switch (ntohs(eh->ether_type)) {
case ETHERTYPE_IP:
case ETHERTYPE_IPV6:
ipofs = sizeof(struct ether_header);
break;
default:
goto send;
}
break;
}
default:
panic("Corrupted priv->dlt: %u", priv->dlt);
}
c = (char *)mtodo(m, ipofs);
ip = (struct ip *)mtodo(m, ipofs);
KASSERT(m->m_pkthdr.len == ipofs + ntohs(ip->ip_len),
("ng_nat: ip_len != m_pkthdr.len"));
/*
@ -753,7 +822,8 @@ ng_nat_rcvdata(hook_p hook, item_p item )
* PKT_ALIAS_DENY_INCOMING flag is set.
*/
if (hook == priv->in) {
rval = LibAliasIn(priv->lib, c, m->m_len + M_TRAILINGSPACE(m));
rval = LibAliasIn(priv->lib, c, m->m_len - ipofs +
M_TRAILINGSPACE(m));
if (rval == PKT_ALIAS_ERROR ||
rval == PKT_ALIAS_UNRESOLVED_FRAGMENT ||
(rval == PKT_ALIAS_IGNORED &&
@ -763,7 +833,8 @@ ng_nat_rcvdata(hook_p hook, item_p item )
return (EINVAL);
}
} else if (hook == priv->out) {
rval = LibAliasOut(priv->lib, c, m->m_len + M_TRAILINGSPACE(m));
rval = LibAliasOut(priv->lib, c, m->m_len - ipofs +
M_TRAILINGSPACE(m));
if (rval == PKT_ALIAS_ERROR) {
NG_FREE_ITEM(item);
return (EINVAL);
@ -773,7 +844,7 @@ ng_nat_rcvdata(hook_p hook, item_p item )
if (rval == PKT_ALIAS_RESPOND)
m->m_flags |= M_SKIP_FIREWALL;
m->m_pkthdr.len = m->m_len = ntohs(ip->ip_len);
m->m_pkthdr.len = m->m_len = ntohs(ip->ip_len) + ipofs;
if ((ip->ip_off & htons(IP_OFFMASK)) == 0 &&
ip->ip_p == IPPROTO_TCP) {

View File

@ -205,6 +205,8 @@ enum {
NGM_NAT_SET_IPADDR = 1,
NGM_NAT_SET_MODE,
NGM_NAT_SET_TARGET,
NGM_NAT_SET_DLT,
NGM_NAT_GET_DLT,
NGM_NAT_REDIRECT_PORT,
NGM_NAT_REDIRECT_ADDR,
NGM_NAT_REDIRECT_PROTO,