Skip to content

Commit

Permalink
Allow ng_nat to be attached to a ethernet interface directly via ng_e…
Browse files Browse the repository at this point in the history
…ther(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
  • Loading branch information
sobomax authored and sobomax committed Dec 17, 2018
1 parent 0d25e05 commit 53dba18
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 9 deletions.
56 changes: 54 additions & 2 deletions share/man/man4/ng_nat.4
Expand Up @@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd March 21, 2013
.Dd December 12, 2018
.Dt NG_NAT 4
.Os
.Sh NAME
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
85 changes: 78 additions & 7 deletions sys/netgraph/ng_nat.c
Expand Up @@ -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>

Expand Down Expand Up @@ -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 }
};

Expand Down Expand Up @@ -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;

Expand All @@ -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. */
Expand Down Expand Up @@ -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;
Expand All @@ -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. */
Expand All @@ -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;

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 == ntohs(ip->ip_len),
KASSERT(m->m_pkthdr.len == ipofs + ntohs(ip->ip_len),
("ng_nat: ip_len != m_pkthdr.len"));

/*
Expand All @@ -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 &&
Expand All @@ -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);
Expand All @@ -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) {
Expand Down
2 changes: 2 additions & 0 deletions sys/netgraph/ng_nat.h
Expand Up @@ -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,
Expand Down

0 comments on commit 53dba18

Please sign in to comment.