diff options
Diffstat (limited to 'freebsd/sys/net/if_gre.c')
-rw-r--r-- | freebsd/sys/net/if_gre.c | 102 |
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: |