/*      $NetBSD: mpls_proto.c,v 1.33 2022/09/03 02:24:59 thorpej Exp $ */

/*
* Copyright (c) 2010 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Mihai Chelaru <[email protected]>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: mpls_proto.c,v 1.33 2022/09/03 02:24:59 thorpej Exp $");

#ifdef _KERNEL_OPT
#include "opt_inet.h"
#include "opt_mbuftrace.h"
#endif

#include <sys/param.h>
#include <sys/socket.h>
#include <sys/protosw.h>
#include <sys/domain.h>
#include <sys/socketvar.h>
#include <sys/sysctl.h>

#include <net/route.h>

#include <netmpls/mpls.h>
#include <netmpls/mpls_var.h>

#define MPLS_MAXQLEN    256
pktqueue_t *            mpls_pktq               __read_mostly;

static int mpls_attach(struct socket *, int);
static void sysctl_net_mpls_setup(struct sysctllog **);

#ifdef MBUFTRACE
struct mowner mpls_owner = MOWNER_INIT("MPLS", "");
#endif

int mpls_defttl = 255;
int mpls_mapttl_inet = 1;
int mpls_mapttl_inet6 = 1;
int mpls_icmp_respond = 0;
int mpls_forwarding = 0;
int mpls_frame_accept = 0;
int mpls_mapprec_inet = 1;
int mpls_mapclass_inet6 = 1;
int mpls_rfc4182 = 1;

void mpls_init(void)
{
#ifdef MBUFTRACE
       MOWNER_ATTACH(&mpls_owner);
#endif
       mpls_pktq = pktq_create(MPLS_MAXQLEN, mplsintr, NULL);
       KASSERT(mpls_pktq != NULL);

       sysctl_net_mpls_setup(NULL);
}

static int
mpls_attach(struct socket *so, int proto)
{
       int error = EOPNOTSUPP;

       sosetlock(so);
       if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
               error = soreserve(so, 8192, 8192);
       }
       return error;
}

static void
mpls_detach(struct socket *so)
{
}

static int
mpls_accept(struct socket *so, struct sockaddr *nam)
{
       KASSERT(solocked(so));

       return EOPNOTSUPP;
}

static int
mpls_bind(struct socket *so, struct sockaddr *nam, struct lwp *l)
{
       KASSERT(solocked(so));

       return EOPNOTSUPP;
}

static int
mpls_listen(struct socket *so, struct lwp *l)
{
       KASSERT(solocked(so));

       return EOPNOTSUPP;
}

static int
mpls_connect(struct socket *so, struct sockaddr *nam, struct lwp *l)
{
       KASSERT(solocked(so));

       return EOPNOTSUPP;
}

static int
mpls_connect2(struct socket *so, struct socket *so2)
{
       KASSERT(solocked(so));

       return EOPNOTSUPP;
}

static int
mpls_disconnect(struct socket *so)
{
       KASSERT(solocked(so));

       return EOPNOTSUPP;
}

static int
mpls_shutdown(struct socket *so)
{
       KASSERT(solocked(so));

       return EOPNOTSUPP;
}

static int
mpls_abort(struct socket *so)
{
       KASSERT(solocked(so));

       return EOPNOTSUPP;
}

static int
mpls_ioctl(struct socket *so, u_long cmd, void *nam, struct ifnet *ifp)
{
       return EOPNOTSUPP;
}

static int
mpls_stat(struct socket *so, struct stat *ub)
{
       KASSERT(solocked(so));

       return EOPNOTSUPP;
}

static int
mpls_peeraddr(struct socket *so, struct sockaddr *nam)
{
       KASSERT(solocked(so));

       return EOPNOTSUPP;
}

static int
mpls_sockaddr(struct socket *so, struct sockaddr *nam)
{
       KASSERT(solocked(so));

       return EOPNOTSUPP;
}

static int
mpls_rcvd(struct socket *so, int flags, struct lwp *l)
{
       KASSERT(solocked(so));

       return EOPNOTSUPP;
}

static int
mpls_recvoob(struct socket *so, struct mbuf *m, int flags)
{
       KASSERT(solocked(so));

       return EOPNOTSUPP;
}

static int
mpls_send(struct socket *so, struct mbuf *m, struct sockaddr *nam,
   struct mbuf *control, struct lwp *l)
{
       KASSERT(solocked(so));

       return EOPNOTSUPP;
}

static int
mpls_sendoob(struct socket *so, struct mbuf *m, struct mbuf *control)
{
       KASSERT(solocked(so));

       m_freem(m);
       m_freem(control);

       return EOPNOTSUPP;
}

static int
mpls_purgeif(struct socket *so, struct ifnet *ifp)
{

       return EOPNOTSUPP;
}

/*
* Sysctl for MPLS variables.
*/
static void
sysctl_net_mpls_setup(struct sysctllog **clog)
{
       const struct sysctlnode *mpls_node;

       sysctl_createv(clog, 0, NULL, &mpls_node,
                      CTLFLAG_PERMANENT,
                      CTLTYPE_NODE, "mpls", NULL,
                      NULL, 0, NULL, 0,
                      CTL_NET, PF_MPLS, CTL_EOL);

       sysctl_createv(clog, 0, NULL, NULL,
                      CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
                      CTLTYPE_INT, "ttl",
                      SYSCTL_DESCR("Default TTL"),
                      NULL, 0, &mpls_defttl, 0,
                      CTL_NET, PF_MPLS, CTL_CREATE, CTL_EOL);
       sysctl_createv(clog, 0, NULL, NULL,
                      CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
                      CTLTYPE_INT, "forwarding",
                      SYSCTL_DESCR("MPLS forwarding"),
                      NULL, 0, &mpls_forwarding, 0,
                      CTL_NET, PF_MPLS, CTL_CREATE, CTL_EOL);
       sysctl_createv(clog, 0, NULL, NULL,
                      CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
                      CTLTYPE_INT, "accept",
                      SYSCTL_DESCR("Accept MPLS Frames"),
                      NULL, 0, &mpls_frame_accept, 0,
                      CTL_NET, PF_MPLS, CTL_CREATE, CTL_EOL);

       pktq_sysctl_setup(mpls_pktq, clog, mpls_node, CTL_CREATE);

       sysctl_createv(clog, 0, NULL, NULL,
                      CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
                      CTLTYPE_INT, "rfc4182",
                      SYSCTL_DESCR("RFC 4182 conformance"),
                      NULL, 0, &mpls_rfc4182, 0,
                      CTL_NET, PF_MPLS, CTL_CREATE, CTL_EOL);
#ifdef INET
       sysctl_createv(clog, 0, NULL, NULL,
                      CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
                      CTLTYPE_INT, "inet_mapttl",
                      SYSCTL_DESCR("Map IP TTL"),
                      NULL, 0, &mpls_mapttl_inet, 0,
                      CTL_NET, PF_MPLS, CTL_CREATE, CTL_EOL);
       sysctl_createv(clog, 0, NULL, NULL,
                      CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
                      CTLTYPE_INT, "inet_map_prec",
                      SYSCTL_DESCR("Map IP Prec"),
                      NULL, 0, &mpls_mapprec_inet, 0,
                      CTL_NET, PF_MPLS, CTL_CREATE, CTL_EOL);
       sysctl_createv(clog, 0, NULL, NULL,
                      CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
                      CTLTYPE_INT, "icmp_respond",
                      SYSCTL_DESCR("Emit ICMP packets on errors"),
                      NULL, 0, &mpls_icmp_respond, 0,
                      CTL_NET, PF_MPLS, CTL_CREATE, CTL_EOL);
#endif
#ifdef INET6
       sysctl_createv(clog, 0, NULL, NULL,
                      CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
                      CTLTYPE_INT, "inet6_mapttl",
                      SYSCTL_DESCR("Map IP6 TTL"),
                      NULL, 0, &mpls_mapttl_inet6, 0,
                      CTL_NET, PF_MPLS, CTL_CREATE, CTL_EOL);
       sysctl_createv(clog, 0, NULL, NULL,
                      CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
                      CTLTYPE_INT, "inet6_map_prec",
                      SYSCTL_DESCR("Map IP6 class"),
                      NULL, 0, &mpls_mapclass_inet6, 0,
                      CTL_NET, PF_MPLS, CTL_CREATE, CTL_EOL);
#endif
}

DOMAIN_DEFINE(mplsdomain);

PR_WRAP_USRREQS(mpls)
#define mpls_attach     mpls_attach_wrapper
#define mpls_detach     mpls_detach_wrapper
#define mpls_accept     mpls_accept_wrapper
#define mpls_bind       mpls_bind_wrapper
#define mpls_listen     mpls_listen_wrapper
#define mpls_connect    mpls_connect_wrapper
#define mpls_connect2   mpls_connect2_wrapper
#define mpls_disconnect mpls_disconnect_wrapper
#define mpls_shutdown   mpls_shutdown_wrapper
#define mpls_abort      mpls_abort_wrapper
#define mpls_ioctl      mpls_ioctl_wrapper
#define mpls_stat       mpls_stat_wrapper
#define mpls_peeraddr   mpls_peeraddr_wrapper
#define mpls_sockaddr   mpls_sockaddr_wrapper
#define mpls_rcvd       mpls_rcvd_wrapper
#define mpls_recvoob    mpls_recvoob_wrapper
#define mpls_send       mpls_send_wrapper
#define mpls_sendoob    mpls_sendoob_wrapper
#define mpls_purgeif    mpls_purgeif_wrapper

static const struct pr_usrreqs mpls_usrreqs = {
       .pr_attach      = mpls_attach,
       .pr_detach      = mpls_detach,
       .pr_accept      = mpls_accept,
       .pr_bind        = mpls_bind,
       .pr_listen      = mpls_listen,
       .pr_connect     = mpls_connect,
       .pr_connect2    = mpls_connect2,
       .pr_disconnect  = mpls_disconnect,
       .pr_shutdown    = mpls_shutdown,
       .pr_abort       = mpls_abort,
       .pr_ioctl       = mpls_ioctl,
       .pr_stat        = mpls_stat,
       .pr_peeraddr    = mpls_peeraddr,
       .pr_sockaddr    = mpls_sockaddr,
       .pr_rcvd        = mpls_rcvd,
       .pr_recvoob     = mpls_recvoob,
       .pr_send        = mpls_send,
       .pr_sendoob     = mpls_sendoob,
       .pr_purgeif     = mpls_purgeif,
};

const struct protosw mplssw[] = {
       {       .pr_domain = &mplsdomain,
               .pr_init = mpls_init,
       },
       {
               .pr_type = SOCK_DGRAM,
               .pr_domain = &mplsdomain,
               .pr_flags = PR_ATOMIC | PR_ADDR,
               .pr_usrreqs = &mpls_usrreqs,
       },
       {
               .pr_type = SOCK_RAW,
               .pr_domain = &mplsdomain,
               .pr_flags = PR_ATOMIC | PR_ADDR,
               .pr_usrreqs = &mpls_usrreqs,
       },
};

struct domain mplsdomain = {
       .dom_family = PF_MPLS,
       .dom_name = "MPLS",
       .dom_init = NULL,
       .dom_externalize = NULL,
       .dom_dispose = NULL,
       .dom_protosw = mplssw,
       .dom_protoswNPROTOSW = &mplssw[__arraycount(mplssw)],
       .dom_rtattach = rt_inithead,
       .dom_rtoffset = offsetof(struct sockaddr_mpls, smpls_addr) << 3,
       .dom_maxrtkey = sizeof(union mpls_shim),
       .dom_ifattach = NULL,
       .dom_ifdetach = NULL,
       .dom_link = { NULL },
       .dom_mowner = MOWNER_INIT("MPLS", ""),
       .dom_sa_cmpofs = offsetof(struct sockaddr_mpls, smpls_addr),
       .dom_sa_cmplen = sizeof(union mpls_shim),
};