root/sys/netinet6/dccp6_usrreq.c
/*      $KAME: dccp6_usrreq.c,v 1.13 2005/07/27 08:42:56 nishida Exp $  */
/*      $NetBSD: dccp6_usrreq.c,v 1.15 2022/11/04 09:01:53 ozaki-r Exp $ */

/*
 * Copyright (C) 2003 WIDE Project.
 * All rights reserved.
 *
 * 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.
 * 3. Neither the name of the project nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE PROJECT 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 PROJECT 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: dccp6_usrreq.c,v 1.15 2022/11/04 09:01:53 ozaki-r Exp $");

#ifdef _KERNEL_OPT
#include "opt_inet.h"
#include "opt_dccp.h"
#include "opt_net_mpsafe.h"
#endif

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/domain.h>
#include <sys/kernel.h>
#include <sys/pool.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/proc.h>
#include <sys/protosw.h>
#include <sys/signalvar.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/mutex.h>
#include <sys/sysctl.h>
#include <sys/syslog.h>
#include <sys/queue.h>

#include <net/if.h>

#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
#include <netinet/in_var.h>
#include <netinet/ip6.h>
#include <netinet/ip_icmp.h>
#include <netinet/icmp_var.h>
#include <netinet/ip_var.h>
#include <netinet6/in6_pcb.h>
#include <netinet6/ip6_var.h>
#include <netinet/dccp.h>
#include <netinet/dccp_var.h>
#include <netinet6/dccp6_var.h>
#include <netinet/dccp_cc_sw.h>

#if !defined(__FreeBSD__) || __FreeBSD_version < 500000
#define INP_INFO_LOCK_INIT(x,y)
#define INP_INFO_WLOCK(x)
#define INP_INFO_WUNLOCK(x)
#define INP_INFO_RLOCK(x)
#define INP_INFO_RUNLOCK(x)
#define INP_LOCK(x)
#define INP_UNLOCK(x)
#endif

#define PULLDOWN_TEST

int
dccp6_input(struct mbuf **mp, int *offp, int proto)
{
        struct mbuf *m = *mp;
        DCCP_DEBUG((LOG_INFO, "In dccp6_input!\n"));
#ifndef PULLDOWN_TEST
        IP6_EXTHDR_CHECK(m, *offp, sizeof(struct dccphdr), IPPROTO_DONE);
#endif

        dccp_input(m, *offp, proto);
        return IPPROTO_DONE;
}

void *
dccp6_ctlinput(int cmd, const struct sockaddr *sa, void *d)
{
        if (sa->sa_family != AF_INET6 ||
            sa->sa_len != sizeof(struct sockaddr_in6))
                return NULL;
        
        /* FIX LATER */
        return NULL;
}

int
dccp6_bind(struct socket *so, struct sockaddr *nam, struct lwp *td)
{
        struct inpcb *inp;
        int error;
        struct sockaddr_in6 *sinp = (struct sockaddr_in6 *)nam;

        DCCP_DEBUG((LOG_INFO, "Entering dccp6_bind!\n"));
        INP_INFO_WLOCK(&dccpbinfo);
        inp = sotoinpcb(so);
        if (inp == 0) {
                INP_INFO_WUNLOCK(&dccpbinfo);
                DCCP_DEBUG((LOG_INFO, "dccp6_bind: inp == 0!\n"));
                return EINVAL;
        }
        /* Do not bind to multicast addresses! */
        if (sinp->sin6_family == AF_INET6 &&
            IN6_IS_ADDR_MULTICAST(&sinp->sin6_addr)) {
                INP_INFO_WUNLOCK(&dccpbinfo);
                return EAFNOSUPPORT;
        }
        INP_LOCK(inp);

        intodccpcb(inp)->inp_vflag &= ~INP_IPV4;
        intodccpcb(inp)->inp_vflag |= INP_IPV6;
        
        error = in6pcb_bind(inp, sinp, td);
        INP_UNLOCK(inp);
        INP_INFO_WUNLOCK(&dccpbinfo);
        return error;
}

int
dccp6_connect(struct socket *so, struct sockaddr *nam, struct lwp *l)
{
        struct inpcb *inp;
        struct dccpcb *dp;
        int error;
        struct sockaddr_in6 *sin6;
        char test[2];

        DCCP_DEBUG((LOG_INFO, "Entering dccp6_connect!\n"));

#ifndef __NetBSD__
        INP_INFO_WLOCK(&dccpbinfo);
        inp = sotoinpcb(so);
        if (inp == 0) {
                INP_INFO_WUNLOCK(&dccpbinfo);
                return EINVAL;
        }
        INP_LOCK(inp);
        if (in6p_faddr(inp).s_addr != INADDR_ANY) {
                INP_UNLOCK(inp);
                INP_INFO_WUNLOCK(&dccpbinfo);
                return EISCONN;
        }

        dp = (struct dccpcb *)inp->inp_ppcb;
#else
        inp = sotoinpcb(so);
        if (inp == 0) {
                return EINVAL;
        }
        if (!IN6_IS_ADDR_UNSPECIFIED(&in6p_faddr(inp))) {
                return EISCONN;
        }

        dp = (struct dccpcb *)inp->inp_ppcb;
#endif
        if (dp->state == DCCPS_ESTAB) {
                DCCP_DEBUG((LOG_INFO, "Why are we in connect when we already have a established connection?\n"));
        }

        dp->who = DCCP_CLIENT;
        dp->seq_snd = (((u_int64_t)random() << 32) | random()) % 281474976710656LL;

        sin6 = (struct sockaddr_in6 *)nam;
        if (sin6->sin6_family == AF_INET6
            && IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
                error = EAFNOSUPPORT;
                goto bad;
        }

        dp->inp_vflag &= ~INP_IPV4;
        dp->inp_vflag |= INP_IPV6;

        error = dccp_doconnect(so, nam, l, 1);

        if (error != 0)
                goto bad;

        callout_reset(&dp->retrans_timer, dp->retrans, dccp_retrans_t, dp);
        callout_reset(&dp->connect_timer, DCCP_CONNECT_TIMER, dccp_connect_t, dp);

        test[0] = dp->pref_cc;
#if 0
        /* FIX THIS LATER */
        if (dp->pref_cc == 2) {
                test[1] = 3;
        } else {
                test[1] = 2;
        }
        dccp_add_feature(dp, DCCP_OPT_CHANGE, DCCP_FEATURE_CC, test, 2);
        dccp_add_feature(dp, DCCP_OPT_PREFER, DCCP_FEATURE_CC, test, 2);
#else
         /* we only support CCID2 at this moment */
        dccp_add_feature(dp, DCCP_OPT_CHANGE_R, DCCP_FEATURE_CC, test, 1);
#endif

        error = dccp_output(dp, 0);

bad:
        INP_UNLOCK(inp);
        INP_INFO_WUNLOCK(&dccpbinfo);
        return error;
}


int
dccp6_listen(struct socket *so, struct lwp *l)
{
        struct inpcb *inp;
        struct dccpcb *dp;
        int error = 0;

        DCCP_DEBUG((LOG_INFO, "Entering dccp6_listen!\n"));

        INP_INFO_RLOCK(&dccpbinfo);
        inp = sotoinpcb(so);
        if (inp == 0) {
                INP_INFO_RUNLOCK(&dccpbinfo);
                return EINVAL;
        }
        INP_LOCK(inp);
        INP_INFO_RUNLOCK(&dccpbinfo);
        dp = intodccpcb(inp);
        DCCP_DEBUG((LOG_INFO, "Checking inp->inp_lport!\n"));
        if (inp->inp_lport == 0) {
                error = in6pcb_bind(inp, NULL, l);
        }
        if (error == 0) {
                dp->state = DCCPS_LISTEN;
                dp->who = DCCP_LISTENER;
                dp->seq_snd = 512;
        }
        INP_UNLOCK(inp);
        return error;
}

int
dccp6_accept(struct socket *so, struct sockaddr *nam)
{
        struct inpcb *inp = NULL;
        int error = 0;

        DCCP_DEBUG((LOG_INFO, "Entering dccp6_accept!\n"));
        if (nam == NULL) {
                return EINVAL;
        }
        if (so->so_state & SS_ISDISCONNECTED) {
                DCCP_DEBUG((LOG_INFO, "so_state && SS_ISDISCONNECTED!, so->state = %i\n", so->so_state));
                return ECONNABORTED;
        }

        INP_INFO_RLOCK(&dccpbinfo);
        inp = sotoinpcb(so);
        if (inp == 0) {
                INP_INFO_RUNLOCK(&dccpbinfo);
                return EINVAL;
        }
        INP_LOCK(inp);
        INP_INFO_RUNLOCK(&dccpbinfo);
        in6pcb_fetch_peeraddr(inp, (struct sockaddr_in6 *)nam);

        INP_UNLOCK(inp);
        return error;
}

static int
dccp6_ioctl(struct socket *so, u_long cmd, void *nam, struct ifnet *ifp)
{
        int error = 0;
        int family;

        family = so->so_proto->pr_domain->dom_family;
        switch (family) {
        case PF_INET6:
                error = in6_control(so, cmd, nam, ifp);
                break;
        default:
                error =  EAFNOSUPPORT;
        }
        return (error);
}

static int
dccp6_stat(struct socket *so, struct stat *ub)
{
        return 0;
}

static int
dccp6_purgeif(struct socket *so, struct ifnet *ifp)
{
        int s;

        s = splsoftnet();
        mutex_enter(softnet_lock);
        in6pcb_purgeif0(&dccpbtable, ifp);
#ifdef NET_MPSAFE
        mutex_exit(softnet_lock);
#endif
        in6_purgeif(ifp);
#ifdef NET_MPSAFE
        mutex_enter(softnet_lock);
#endif
        in6pcb_purgeif(&dccpbtable, ifp);
        mutex_exit(softnet_lock);
        splx(s);

        return 0;
}

static int
dccp6_attach(struct socket *so, int proto)
{
        return dccp_attach(so, proto);
}

static int
dccp6_detach(struct socket *so)
{
        return dccp_detach(so);
}

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

        return EOPNOTSUPP;
}

static int
dccp6_disconnect(struct socket *so)
{
        return dccp_disconnect(so);
}

static int
dccp6_shutdown(struct socket *so)
{
        return dccp_shutdown(so);
}

static int
dccp6_abort(struct socket *so)
{
        return dccp_abort(so);
}


static int
dccp6_peeraddr(struct socket *so, struct sockaddr *nam)
{
        KASSERT(solocked(so));
        KASSERT(sotoinpcb(so) != NULL);
        KASSERT(nam != NULL);

        in6pcb_fetch_peeraddr(sotoinpcb(so), (struct sockaddr_in6 *)nam);
        return 0;
}

static int
dccp6_sockaddr(struct socket *so, struct sockaddr *nam)
{
        KASSERT(solocked(so));
        KASSERT(sotoinpcb(so) != NULL);
        KASSERT(nam != NULL);

        in6pcb_fetch_sockaddr(sotoinpcb(so), (struct sockaddr_in6 *)nam);
        return 0;
}

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

        return EOPNOTSUPP;
}

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

        return EOPNOTSUPP;
}

static int
dccp6_send(struct socket *so, struct mbuf *m, struct sockaddr *nam,
    struct mbuf *control, struct lwp *l)
{
        return dccp_send(so, m, nam, control, l);
}

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

        m_freem(m);
        m_freem(control);

        return EOPNOTSUPP;
}


PR_WRAP_USRREQS(dccp6)
#define dccp6_attach            dccp6_attach_wrapper
#define dccp6_detach            dccp6_detach_wrapper
#define dccp6_accept            dccp6_accept_wrapper
#define dccp6_bind              dccp6_bind_wrapper
#define dccp6_listen            dccp6_listen_wrapper
#define dccp6_connect           dccp6_connect_wrapper
#define dccp6_connect2          dccp6_connect2_wrapper
#define dccp6_disconnect        dccp6_disconnect_wrapper
#define dccp6_shutdown          dccp6_shutdown_wrapper
#define dccp6_abort             dccp6_abort_wrapper
#define dccp6_ioctl             dccp6_ioctl_wrapper
#define dccp6_stat              dccp6_stat_wrapper
#define dccp6_peeraddr          dccp6_peeraddr_wrapper
#define dccp6_sockaddr          dccp6_sockaddr_wrapper
#define dccp6_rcvd              dccp6_rcvd_wrapper
#define dccp6_recvoob           dccp6_recvoob_wrapper
#define dccp6_send              dccp6_send_wrapper
#define dccp6_sendoob           dccp6_sendoob_wrapper
#define dccp6_purgeif           dccp6_purgeif_wrapper

const struct pr_usrreqs dccp6_usrreqs = {
        .pr_attach      = dccp6_attach,
        .pr_detach      = dccp6_detach,
        .pr_accept      = dccp6_accept,
        .pr_bind        = dccp6_bind,
        .pr_listen      = dccp6_listen,
        .pr_connect     = dccp6_connect,
        .pr_connect2    = dccp6_connect2,
        .pr_disconnect  = dccp6_disconnect,
        .pr_shutdown    = dccp6_shutdown,
        .pr_abort       = dccp6_abort,
        .pr_ioctl       = dccp6_ioctl,
        .pr_stat        = dccp6_stat,
        .pr_peeraddr    = dccp6_peeraddr,
        .pr_sockaddr    = dccp6_sockaddr,
        .pr_rcvd        = dccp6_rcvd,
        .pr_recvoob     = dccp6_recvoob,
        .pr_send        = dccp6_send,
        .pr_sendoob     = dccp6_sendoob,
        .pr_purgeif     = dccp6_purgeif,
};