summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/net/if_gre.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/net/if_gre.c')
-rw-r--r--freebsd/sys/net/if_gre.c102
1 files changed, 91 insertions, 11 deletions
diff --git a/freebsd/sys/net/if_gre.c b/freebsd/sys/net/if_gre.c
index a75e52a4..21f39eb2 100644
--- a/freebsd/sys/net/if_gre.c
+++ b/freebsd/sys/net/if_gre.c
@@ -55,7 +55,9 @@
#include <rtems/bsd/local/opt_inet6.h>
#include <rtems/bsd/sys/param.h>
+#include <sys/jail.h>
#include <sys/kernel.h>
+#include <sys/libkern.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/mbuf.h>
@@ -99,6 +101,14 @@
#define GRENAME "gre"
+#define MTAG_COOKIE_GRE 1307983903
+#define MTAG_GRE_NESTING 1
+struct mtag_gre_nesting {
+ uint16_t count;
+ uint16_t max;
+ struct ifnet *ifp[];
+};
+
/*
* gre_mtx protects all global variables in if_gre.c.
* XXX: gre_softc data not protected yet.
@@ -204,7 +214,6 @@ gre_clone_create(ifc, unit, params)
sc->g_proto = IPPROTO_GRE;
GRE2IFP(sc)->if_flags |= IFF_LINK0;
sc->encap = NULL;
- sc->called = 0;
#ifndef __rtems__
sc->gre_fibnum = curthread->td_proc->p_fibnum;
#else /* __rtems__ */
@@ -252,23 +261,77 @@ gre_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
struct gre_softc *sc = ifp->if_softc;
struct greip *gh;
struct ip *ip;
+ struct m_tag *mtag;
+ struct mtag_gre_nesting *gt;
+ size_t len;
u_short gre_ip_id = 0;
uint8_t gre_ip_tos = 0;
u_int16_t etype = 0;
struct mobile_h mob_h;
u_int32_t af;
- int extra = 0;
+ int extra = 0, max;
/*
- * gre may cause infinite recursion calls when misconfigured.
- * We'll prevent this by introducing upper limit.
+ * gre may cause infinite recursion calls when misconfigured. High
+ * nesting level may cause stack exhaustion. We'll prevent this by
+ * detecting loops and by introducing upper limit.
*/
- if (++(sc->called) > max_gre_nesting) {
- printf("%s: gre_output: recursively called too many "
- "times(%d)\n", if_name(GRE2IFP(sc)), sc->called);
- m_freem(m);
- error = EIO; /* is there better errno? */
- goto end;
+ mtag = m_tag_locate(m, MTAG_COOKIE_GRE, MTAG_GRE_NESTING, NULL);
+ if (mtag != NULL) {
+ struct ifnet **ifp2;
+
+ gt = (struct mtag_gre_nesting *)(mtag + 1);
+ gt->count++;
+ if (gt->count > min(gt->max,max_gre_nesting)) {
+ printf("%s: hit maximum recursion limit %u on %s\n",
+ __func__, gt->count - 1, ifp->if_xname);
+ m_freem(m);
+ error = EIO; /* is there better errno? */
+ goto end;
+ }
+
+ ifp2 = gt->ifp;
+ for (max = gt->count - 1; max > 0; max--) {
+ if (*ifp2 == ifp)
+ break;
+ ifp2++;
+ }
+ if (*ifp2 == ifp) {
+ printf("%s: detected loop with nexting %u on %s\n",
+ __func__, gt->count-1, ifp->if_xname);
+ m_freem(m);
+ error = EIO; /* is there better errno? */
+ goto end;
+ }
+ *ifp2 = ifp;
+
+ } else {
+ /*
+ * Given that people should NOT increase max_gre_nesting beyond
+ * their real needs, we allocate once per packet rather than
+ * allocating an mtag once per passing through gre.
+ *
+ * Note: the sysctl does not actually check for saneness, so we
+ * limit the maximum numbers of possible recursions here.
+ */
+ max = imin(max_gre_nesting, 256);
+ /* If someone sets the sysctl <= 0, we want at least 1. */
+ max = imax(max, 1);
+ len = sizeof(struct mtag_gre_nesting) +
+ max * sizeof(struct ifnet *);
+ mtag = m_tag_alloc(MTAG_COOKIE_GRE, MTAG_GRE_NESTING, len,
+ M_NOWAIT);
+ if (mtag == NULL) {
+ m_freem(m);
+ error = ENOMEM;
+ goto end;
+ }
+ gt = (struct mtag_gre_nesting *)(mtag + 1);
+ bzero(gt, len);
+ gt->count = 1;
+ gt->max = max;
+ *gt->ifp = ifp;
+ m_tag_prepend(m, mtag);
}
if (!((ifp->if_flags & IFF_UP) &&
@@ -456,7 +519,6 @@ gre_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
error = ip_output(m, NULL, &sc->route, IP_FORWARDING,
(struct ip_moptions *)NULL, (struct inpcb *)NULL);
end:
- sc->called = 0;
if (error)
ifp->if_oerrors++;
return (error);
@@ -649,6 +711,9 @@ gre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
si.sin_len = sizeof(struct sockaddr_in);
si.sin_addr.s_addr = sc->g_src.s_addr;
sa = sintosa(&si);
+ error = prison_if(curthread->td_ucred, sa);
+ if (error != 0)
+ break;
ifr->ifr_addr = *sa;
break;
case GREGADDRD:
@@ -657,6 +722,9 @@ gre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
si.sin_len = sizeof(struct sockaddr_in);
si.sin_addr.s_addr = sc->g_dst.s_addr;
sa = sintosa(&si);
+ error = prison_if(curthread->td_ucred, sa);
+ if (error != 0)
+ break;
ifr->ifr_addr = *sa;
break;
case SIOCSIFPHYADDR:
@@ -720,8 +788,14 @@ gre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
si.sin_family = AF_INET;
si.sin_len = sizeof(struct sockaddr_in);
si.sin_addr.s_addr = sc->g_src.s_addr;
+ error = prison_if(curthread->td_ucred, (struct sockaddr *)&si);
+ if (error != 0)
+ break;
memcpy(&lifr->addr, &si, sizeof(si));
si.sin_addr.s_addr = sc->g_dst.s_addr;
+ error = prison_if(curthread->td_ucred, (struct sockaddr *)&si);
+ if (error != 0)
+ break;
memcpy(&lifr->dstaddr, &si, sizeof(si));
break;
case SIOCGIFPSRCADDR:
@@ -736,6 +810,9 @@ gre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
si.sin_family = AF_INET;
si.sin_len = sizeof(struct sockaddr_in);
si.sin_addr.s_addr = sc->g_src.s_addr;
+ error = prison_if(curthread->td_ucred, (struct sockaddr *)&si);
+ if (error != 0)
+ break;
bcopy(&si, &ifr->ifr_addr, sizeof(ifr->ifr_addr));
break;
case SIOCGIFPDSTADDR:
@@ -750,6 +827,9 @@ gre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
si.sin_family = AF_INET;
si.sin_len = sizeof(struct sockaddr_in);
si.sin_addr.s_addr = sc->g_dst.s_addr;
+ error = prison_if(curthread->td_ucred, (struct sockaddr *)&si);
+ if (error != 0)
+ break;
bcopy(&si, &ifr->ifr_addr, sizeof(ifr->ifr_addr));
break;
case GRESKEY: