summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/net/if_gre.c113
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;