Deduplicate the code.

Add generic function if_tunnel_check_nesting() that does check for
allowed nesting level for tunneling interfaces and also does loop
detection. Use it in gif(4), gre(4) and me(4) interfaces.

Differential Revision:	https://reviews.freebsd.org/D16162
This commit is contained in:
Andrey V. Elsukov 2018-07-09 11:03:28 +00:00
parent d50fbe3fe2
commit 98a8fdf6da
5 changed files with 48 additions and 100 deletions

View File

@ -3902,6 +3902,44 @@ if_requestencap_default(struct ifnet *ifp, struct if_encap_req *req)
return (0);
}
/*
* Tunnel interfaces can nest, also they may cause infinite recursion
* calls when misconfigured. We'll prevent this by detecting loops.
* High nesting level may cause stack exhaustion. We'll prevent this
* by introducing upper limit.
*
* Return 0, if tunnel nesting count is equal or less than limit.
*/
int
if_tunnel_check_nesting(struct ifnet *ifp, struct mbuf *m, uint32_t cookie,
int limit)
{
struct m_tag *mtag;
int count;
count = 1;
mtag = NULL;
while ((mtag = m_tag_locate(m, cookie, 0, mtag)) != NULL) {
if (*(struct ifnet **)(mtag + 1) == ifp) {
log(LOG_NOTICE, "%s: loop detected\n", if_name(ifp));
return (EIO);
}
count++;
}
if (count > limit) {
log(LOG_NOTICE,
"%s: if_output recursively called too many times(%d)\n",
if_name(ifp), count);
return (EIO);
}
mtag = m_tag_alloc(cookie, 0, sizeof(struct ifnet *), M_NOWAIT);
if (mtag == NULL)
return (ENOMEM);
*(struct ifnet **)(mtag + 1) = ifp;
m_tag_prepend(m, mtag);
return (0);
}
/*
* Get the link layer address that was read from the hardware at attach.
*

View File

@ -104,7 +104,6 @@ void (*ng_gif_input_orphan_p)(struct ifnet *ifp, struct mbuf *m, int af);
void (*ng_gif_attach_p)(struct ifnet *ifp);
void (*ng_gif_detach_p)(struct ifnet *ifp);
static int gif_check_nesting(struct ifnet *, struct mbuf *);
static void gif_delete_tunnel(struct gif_softc *);
static int gif_ioctl(struct ifnet *, u_long, caddr_t);
static int gif_transmit(struct ifnet *, struct mbuf *);
@ -256,6 +255,7 @@ gif_hashdestroy(struct gif_list *hash)
free(hash, M_GIF);
}
#define MTAG_GIF 1080679712
static int
gif_transmit(struct ifnet *ifp, struct mbuf *m)
{
@ -285,7 +285,8 @@ gif_transmit(struct ifnet *ifp, struct mbuf *m)
if ((ifp->if_flags & IFF_MONITOR) != 0 ||
(ifp->if_flags & IFF_UP) == 0 ||
sc->gif_family == 0 ||
(error = gif_check_nesting(ifp, m)) != 0) {
(error = if_tunnel_check_nesting(ifp, m, MTAG_GIF,
V_max_gif_nesting)) != 0) {
m_freem(m);
goto err;
}
@ -378,42 +379,6 @@ gif_qflush(struct ifnet *ifp __unused)
}
#define MTAG_GIF 1080679712
static int
gif_check_nesting(struct ifnet *ifp, struct mbuf *m)
{
struct m_tag *mtag;
int count;
/*
* gif may cause infinite recursion calls when misconfigured.
* We'll prevent this by detecting loops.
*
* High nesting level may cause stack exhaustion.
* We'll prevent this by introducing upper limit.
*/
count = 1;
mtag = NULL;
while ((mtag = m_tag_locate(m, MTAG_GIF, 0, mtag)) != NULL) {
if (*(struct ifnet **)(mtag + 1) == ifp) {
log(LOG_NOTICE, "%s: loop detected\n", if_name(ifp));
return (EIO);
}
count++;
}
if (count > V_max_gif_nesting) {
log(LOG_NOTICE,
"%s: if_output recursively called too many times(%d)\n",
if_name(ifp), count);
return (EIO);
}
mtag = m_tag_alloc(MTAG_GIF, 0, sizeof(struct ifnet *), M_NOWAIT);
if (mtag == NULL)
return (ENOMEM);
*(struct ifnet **)(mtag + 1) = ifp;
m_tag_prepend(m, mtag);
return (0);
}
int
gif_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,

View File

@ -503,36 +503,6 @@ gre_input(struct mbuf *m, int off, int proto, void *arg)
return (IPPROTO_DONE);
}
#define MTAG_GRE 1307983903
static int
gre_check_nesting(struct ifnet *ifp, struct mbuf *m)
{
struct m_tag *mtag;
int count;
count = 1;
mtag = NULL;
while ((mtag = m_tag_locate(m, MTAG_GRE, 0, mtag)) != NULL) {
if (*(struct ifnet **)(mtag + 1) == ifp) {
log(LOG_NOTICE, "%s: loop detected\n", ifp->if_xname);
return (EIO);
}
count++;
}
if (count > V_max_gre_nesting) {
log(LOG_NOTICE,
"%s: if_output recursively called too many times(%d)\n",
ifp->if_xname, count);
return (EIO);
}
mtag = m_tag_alloc(MTAG_GRE, 0, sizeof(struct ifnet *), M_NOWAIT);
if (mtag == NULL)
return (ENOMEM);
*(struct ifnet **)(mtag + 1) = ifp;
m_tag_prepend(m, mtag);
return (0);
}
static int
gre_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
struct route *ro)
@ -569,6 +539,7 @@ gre_setseqn(struct grehdr *gh, uint32_t seq)
*opts = htonl(seq);
}
#define MTAG_GRE 1307983903
static int
gre_transmit(struct ifnet *ifp, struct mbuf *m)
{
@ -592,7 +563,8 @@ gre_transmit(struct ifnet *ifp, struct mbuf *m)
if ((ifp->if_flags & IFF_MONITOR) != 0 ||
(ifp->if_flags & IFF_UP) == 0 ||
sc->gre_family == 0 ||
(error = gre_check_nesting(ifp, m)) != 0) {
(error = if_tunnel_check_nesting(ifp, m, MTAG_GRE,
V_max_gre_nesting)) != 0) {
m_freem(m);
goto drop;
}

View File

@ -455,36 +455,6 @@ me_input(struct mbuf *m, int off, int proto, void *arg)
return (IPPROTO_DONE);
}
#define MTAG_ME 1414491977
static int
me_check_nesting(struct ifnet *ifp, struct mbuf *m)
{
struct m_tag *mtag;
int count;
count = 1;
mtag = NULL;
while ((mtag = m_tag_locate(m, MTAG_ME, 0, mtag)) != NULL) {
if (*(struct ifnet **)(mtag + 1) == ifp) {
log(LOG_NOTICE, "%s: loop detected\n", ifp->if_xname);
return (EIO);
}
count++;
}
if (count > V_max_me_nesting) {
log(LOG_NOTICE,
"%s: if_output recursively called too many times(%d)\n",
ifp->if_xname, count);
return (EIO);
}
mtag = m_tag_alloc(MTAG_ME, 0, sizeof(struct ifnet *), M_NOWAIT);
if (mtag == NULL)
return (ENOMEM);
*(struct ifnet **)(mtag + 1) = ifp;
m_tag_prepend(m, mtag);
return (0);
}
static int
me_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
struct route *ro __unused)
@ -499,6 +469,7 @@ me_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
return (ifp->if_transmit(ifp, m));
}
#define MTAG_ME 1414491977
static int
me_transmit(struct ifnet *ifp, struct mbuf *m)
{
@ -519,7 +490,8 @@ me_transmit(struct ifnet *ifp, struct mbuf *m)
if (sc == NULL || !ME_READY(sc) ||
(ifp->if_flags & IFF_MONITOR) != 0 ||
(ifp->if_flags & IFF_UP) == 0 ||
(error = me_check_nesting(ifp, m) != 0)) {
(error = if_tunnel_check_nesting(ifp, m, MTAG_ME,
V_max_me_nesting)) != 0) {
m_freem(m);
goto drop;
}

View File

@ -644,6 +644,7 @@ int if_printf(struct ifnet *, const char *, ...) __printflike(2, 3);
void if_ref(struct ifnet *);
void if_rele(struct ifnet *);
int if_setlladdr(struct ifnet *, const u_char *, int);
int if_tunnel_check_nesting(struct ifnet *, struct mbuf *, uint32_t, int);
void if_up(struct ifnet *);
int ifioctl(struct socket *, u_long, caddr_t, struct thread *);
int ifpromisc(struct ifnet *, int);