diff options
-rw-r--r-- | sys/net/if_gre.c | 113 |
1 files changed, 111 insertions, 2 deletions
diff --git a/sys/net/if_gre.c b/sys/net/if_gre.c index dbe96c8fb8e..6e44d4a78c5 100644 --- a/sys/net/if_gre.c +++ b/sys/net/if_gre.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_gre.c,v 1.118 2018/02/27 09:24:56 benno Exp $ */ +/* $OpenBSD: if_gre.c,v 1.119 2018/02/27 22:36:38 dlg Exp $ */ /* $NetBSD: if_gre.c,v 1.9 1999/10/25 19:18:11 drochner Exp $ */ /* @@ -307,6 +307,8 @@ static int mgre_output(struct ifnet *, struct mbuf *, struct sockaddr *, static void mgre_start(struct ifnet *); static int mgre_ioctl(struct ifnet *, u_long, caddr_t); +static int mgre_set_tunnel(struct mgre_softc *, struct if_laddrreq *); +static int mgre_get_tunnel(struct mgre_softc *, struct if_laddrreq *); static int mgre_up(struct mgre_softc *); static int mgre_down(struct mgre_softc *); @@ -2037,7 +2039,16 @@ mgre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) break; case SIOCSLIFPHYADDR: - /* XXX */ + if (ISSET(ifp->if_flags, IFF_RUNNING)) { + error = EBUSY; + break; + } + error = mgre_set_tunnel(sc, (struct if_laddrreq *)data); + break; + case SIOCGLIFPHYADDR: + error = mgre_get_tunnel(sc, (struct if_laddrreq *)data); + break; + case SIOCSVNETID: case SIOCDVNETID: case SIOCDIFPHYADDR: @@ -2057,6 +2068,104 @@ mgre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) } static int +mgre_set_tunnel(struct mgre_softc *sc, struct if_laddrreq *req) +{ + struct gre_tunnel *tunnel = &sc->sc_tunnel; + struct sockaddr *addr = (struct sockaddr *)&req->addr; + struct sockaddr *dstaddr = (struct sockaddr *)&req->dstaddr; + struct sockaddr_in *addr4; +#ifdef INET6 + struct sockaddr_in6 *addr6; + int error; +#endif + + if (dstaddr->sa_family != AF_UNSPEC) + return (EINVAL); + + /* validate */ + switch (addr->sa_family) { + case AF_INET: + if (addr->sa_len != sizeof(*addr4)) + return (EINVAL); + + addr4 = (struct sockaddr_in *)addr; + if (in_nullhost(addr4->sin_addr) || + IN_MULTICAST(addr4->sin_addr.s_addr)) + return (EINVAL); + + tunnel->t_src4 = addr4->sin_addr; + tunnel->t_dst4.s_addr = INADDR_ANY; + + break; +#ifdef INET6 + case AF_INET6: + if (addr->sa_len != sizeof(*addr6)) + return (EINVAL); + + addr6 = (struct sockaddr_in6 *)addr; + if (IN6_IS_ADDR_UNSPECIFIED(&addr6->sin6_addr) || + IN6_IS_ADDR_MULTICAST(&addr6->sin6_addr)) + return (EINVAL); + + error = in6_embedscope(&tunnel->t_src6, addr6, NULL); + if (error != 0) + return (error); + + memset(&tunnel->t_dst6, 0, sizeof(tunnel->t_dst6)); + + break; +#endif + default: + return (EAFNOSUPPORT); + } + + /* commit */ + tunnel->t_af = addr->sa_family; + + return (0); +} + +static int +mgre_get_tunnel(struct mgre_softc *sc, struct if_laddrreq *req) +{ + struct gre_tunnel *tunnel = &sc->sc_tunnel; + struct sockaddr *dstaddr = (struct sockaddr *)&req->dstaddr; + struct sockaddr_in *sin; +#ifdef INET6 + struct sockaddr_in6 *sin6; +#endif + + switch (tunnel->t_af) { + case AF_UNSPEC: + return (EADDRNOTAVAIL); + case AF_INET: + sin = (struct sockaddr_in *)&req->addr; + memset(sin, 0, sizeof(*sin)); + sin->sin_family = AF_INET; + sin->sin_len = sizeof(*sin); + sin->sin_addr = tunnel->t_src4; + break; + +#ifdef INET6 + case AF_INET6: + sin6 = (struct sockaddr_in6 *)&req->addr; + memset(sin6, 0, sizeof(*sin6)); + sin6->sin6_family = AF_INET6; + sin6->sin6_len = sizeof(*sin6); + in6_recoverscope(sin6, &tunnel->t_src6); + break; +#endif + default: + unhandled_af(tunnel->t_af); + } + + dstaddr->sa_len = 2; + dstaddr->sa_family = AF_UNSPEC; + + return (0); +} + +static int egre_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct egre_softc *sc = ifp->if_softc; |