freebsd-nq/sys/netatm/ipatm/ipatm_load.c
Robert Watson 0daccb9c94 In the current world order, solisten() implements the state transition of
a socket from a regular socket to a listening socket able to accept new
connections.  As part of this state transition, solisten() calls into the
protocol to update protocol-layer state.  There were several bugs in this
implementation that could result in a race wherein a TCP SYN received
in the interval between the protocol state transition and the shortly
following socket layer transition would result in a panic in the TCP code,
as the socket would be in the TCPS_LISTEN state, but the socket would not
have the SO_ACCEPTCONN flag set.

This change does the following:

- Pushes the socket state transition from the socket layer solisten() to
  to socket "library" routines called from the protocol.  This permits
  the socket routines to be called while holding the protocol mutexes,
  preventing a race exposing the incomplete socket state transition to TCP
  after the TCP state transition has completed.  The check for a socket
  layer state transition is performed by solisten_proto_check(), and the
  actual transition is performed by solisten_proto().

- Holds the socket lock for the duration of the socket state test and set,
  and over the protocol layer state transition, which is now possible as
  the socket lock is acquired by the protocol layer, rather than vice
  versa.  This prevents additional state related races in the socket
  layer.

This permits the dual transition of socket layer and protocol layer state
to occur while holding locks for both layers, making the two changes
atomic with respect to one another.  Similar changes are likely require
elsewhere in the socket/protocol code.

Reported by:		Peter Holm <peter@holm.cc>
Review and fixes from:	emax, Antoine Brodin <antoine.brodin@laposte.net>
Philosophical head nod:	gnn
2005-02-21 21:58:17 +00:00

814 lines
14 KiB
C

/*-
* ===================================
* HARP | Host ATM Research Platform
* ===================================
*
*
* This Host ATM Research Platform ("HARP") file (the "Software") is
* made available by Network Computing Services, Inc. ("NetworkCS")
* "AS IS". NetworkCS does not provide maintenance, improvements or
* support of any kind.
*
* NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
* INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
* SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
* In no event shall NetworkCS be responsible for any damages, including
* but not limited to consequential damages, arising from or relating to
* any use of the Software or related support.
*
* Copyright 1994-1998 Network Computing Services, Inc.
*
* Copies of this Software may be made, however, the above copyright
* notice must be reproduced on all copies.
*/
/*
* IP Over ATM Support
* -------------------
*
* Support for running as a loadable kernel module
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#ifndef ATM_IP_MODULE
#include "opt_atm.h"
#endif
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/syslog.h>
#include <sys/kernel.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <netatm/port.h>
#include <netatm/queue.h>
#include <netatm/atm.h>
#include <netatm/atm_sys.h>
#include <netatm/atm_sap.h>
#include <netatm/atm_cm.h>
#include <netatm/atm_if.h>
#include <netatm/atm_stack.h>
#include <netatm/atm_pcb.h>
#include <netatm/atm_var.h>
#include <netatm/ipatm/ipatm.h>
#include <netatm/ipatm/ipatm_var.h>
#include <vm/uma.h>
/*
* Global variables
*/
int ipatm_vccnt = 0;
int ipatm_vcidle = IPATM_VCIDLE;
u_long last_map_ipdst = 0;
struct ipvcc* last_map_ipvcc = NULL;
struct ip_nif *ipatm_nif_head = NULL;
struct ipatm_stat ipatm_stat = {0};
struct atm_time ipatm_itimer = {0, 0}; /* VCC idle timer */
Atm_endpoint ipatm_endpt = {
NULL,
ENDPT_IP,
ipatm_ioctl,
ipatm_getname,
ipatm_connected,
ipatm_cleared,
ipatm_incoming,
NULL,
NULL,
NULL,
ipatm_cpcs_data,
NULL,
NULL,
NULL,
NULL
};
uma_zone_t ipatm_vc_zone;
/*
* net.harp.ip
*/
SYSCTL_NODE(_net_harp, OID_AUTO, ip, CTLFLAG_RW, 0, "IPv4 over ATM");
/*
* net.harp.ip.ipatm_print
*/
int ipatm_print = 0;
SYSCTL_INT(_net_harp_ip, OID_AUTO, ipatm_print, CTLFLAG_RW,
&ipatm_print, 0, "dump IPv4-over-ATM packets");
/*
* Local functions
*/
static int ipatm_start(void);
static int ipatm_stop(void);
/*
* Local variables
*/
static struct atm_ncm ipatm_ncm = {
NULL,
AF_INET,
ipatm_ifoutput,
ipatm_nifstat
};
static struct ipatm_listener {
Atm_attributes attr;
Atm_connection *conn;
} ipatm_listeners[] = {
{
{ NULL, /* nif */
CMAPI_CPCS, /* api */
0, /* api_init */
0, /* headin */
0, /* headout */
{ /* aal */
T_ATM_PRESENT,
ATM_AAL5
},
{ /* traffic */
T_ATM_PRESENT,
{
{
T_ATM_ABSENT,
0,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_NO
},
{
T_ATM_ABSENT,
0,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_NO
},
T_YES
},
},
{ /* bearer */
T_ATM_ANY
},
{ /* bhli */
T_ATM_ABSENT
},
{ /* blli */
T_ATM_PRESENT,
T_ATM_ABSENT,
{
{
T_ATM_SIMPLE_ID,
},
{
T_ATM_ABSENT
}
}
},
{ /* llc */
T_ATM_PRESENT,
{
T_ATM_LLC_SHARING,
IPATM_LLC_LEN,
IPATM_LLC_HDR
}
},
{ /* called */
T_ATM_ANY
},
{ /* calling */
T_ATM_ANY
},
{ /* qos */
T_ATM_PRESENT,
{
T_ATM_NETWORK_CODING,
{
T_ATM_QOS_CLASS_0,
},
{
T_ATM_QOS_CLASS_0
}
}
},
{ /* transit */
T_ATM_ANY
},
{ /* cause */
T_ATM_ABSENT
},
},
NULL
},
{
{ NULL, /* nif */
CMAPI_CPCS, /* api */
0, /* api_init */
0, /* headin */
0, /* headout */
{ /* aal */
T_ATM_PRESENT,
ATM_AAL5
},
{ /* traffic */
T_ATM_PRESENT,
{
{
T_ATM_ABSENT,
0,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_NO
},
{
T_ATM_ABSENT,
0,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_NO
},
T_YES
},
},
{ /* bearer */
T_ATM_ANY
},
{ /* bhli */
T_ATM_ABSENT
},
{ /* blli */
T_ATM_ABSENT,
T_ATM_ABSENT
},
{ /* llc */
T_ATM_ABSENT
},
{ /* called */
T_ATM_ANY
},
{ /* calling */
T_ATM_ANY
},
{ /* qos */
T_ATM_PRESENT,
{
T_ATM_NETWORK_CODING,
{
T_ATM_QOS_CLASS_0,
},
{
T_ATM_QOS_CLASS_0
}
}
},
{ /* transit */
T_ATM_ANY
},
{ /* cause */
T_ATM_ABSENT
},
},
NULL
},
{
{ NULL, /* nif */
CMAPI_CPCS, /* api */
0, /* api_init */
0, /* headin */
0, /* headout */
{ /* aal */
T_ATM_PRESENT,
ATM_AAL3_4
},
{ /* traffic */
T_ATM_PRESENT,
{
{
T_ATM_ABSENT,
0,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_NO
},
{
T_ATM_ABSENT,
0,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_ATM_ABSENT,
T_NO
},
T_YES
},
},
{ /* bearer */
T_ATM_ANY
},
{ /* bhli */
T_ATM_ABSENT
},
{ /* blli */
T_ATM_ABSENT,
T_ATM_ABSENT
},
{ /* llc */
T_ATM_ABSENT
},
{ /* called */
T_ATM_ANY
},
{ /* calling */
T_ATM_ANY
},
{ /* qos */
T_ATM_PRESENT,
{
T_ATM_NETWORK_CODING,
{
T_ATM_QOS_CLASS_0,
},
{
T_ATM_QOS_CLASS_0
}
}
},
{ /* transit */
T_ATM_ANY
},
{ /* cause */
T_ATM_ABSENT
},
},
NULL
},
};
static struct t_atm_cause ipatm_cause = {
T_ATM_ITU_CODING,
T_ATM_LOC_USER,
T_ATM_CAUSE_UNSPECIFIED_NORMAL,
{0, 0, 0, 0}
};
/*
* Initialize ipatm processing
*
* This will be called during module loading. We'll just register
* ourselves and wait for the packets to start flying.
*
* Arguments:
* none
*
* Returns:
* 0 startup was successful
* errno startup failed - reason indicated
*
*/
static int
ipatm_start()
{
struct atm_pif *pip;
struct atm_nif *nip;
int err, s, i;
/*
* Verify software version
*/
if (atm_version != ATM_VERSION) {
log(LOG_ERR, "version mismatch: ipatm=%d.%d kernel=%d.%d\n",
ATM_VERS_MAJ(ATM_VERSION), ATM_VERS_MIN(ATM_VERSION),
ATM_VERS_MAJ(atm_version), ATM_VERS_MIN(atm_version));
return (EINVAL);
}
ipatm_vc_zone = uma_zcreate("ipatm vc", sizeof(struct ipvcc), NULL,
NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
if (ipatm_vc_zone == NULL)
panic("ipatm_start: unable to create ipatm_vc_zone");
/*
* Register ourselves as a network convergence module
*/
err = atm_netconv_register(&ipatm_ncm);
if (err)
goto done;
/*
* Register ourselves as an ATM endpoint
*/
err = atm_endpoint_register(&ipatm_endpt);
if (err)
goto done;
/*
* Get current system configuration
*/
s = splnet();
for (pip = atm_interface_head; pip; pip = pip->pif_next) {
/*
* Process each network interface
*/
for (nip = pip->pif_nif; nip; nip = nip->nif_pnext) {
struct ifnet *ifp = (struct ifnet *)nip;
struct in_ifaddr *ia;
/*
* Attach interface
*/
err = ipatm_nifstat(NCM_ATTACH, nip, 0);
if (err) {
(void) splx(s);
goto done;
}
/*
* If IP address has been set, register it
*/
TAILQ_FOREACH(ia, &in_ifaddrhead, ia_link) {
if (ia->ia_ifp == ifp)
break;
}
if (ia) {
err = ipatm_nifstat(NCM_SETADDR, nip,
(intptr_t)ia);
if (err) {
(void) splx(s);
goto done;
}
}
}
}
(void) splx(s);
/*
* Fill in union fields
*/
ipatm_aal5llc.aal.v.aal5.forward_max_SDU_size =
ATM_NIF_MTU + IPATM_LLC_LEN;
ipatm_aal5llc.aal.v.aal5.backward_max_SDU_size =
ATM_NIF_MTU + IPATM_LLC_LEN;
ipatm_aal5llc.aal.v.aal5.SSCS_type = T_ATM_NULL;
ipatm_aal5llc.blli.v.layer_2_protocol.ID.simple_ID = T_ATM_BLLI2_I8802;
ipatm_aal5null.aal.v.aal5.forward_max_SDU_size = ATM_NIF_MTU;
ipatm_aal5null.aal.v.aal5.backward_max_SDU_size = ATM_NIF_MTU;
ipatm_aal5null.aal.v.aal5.SSCS_type = T_ATM_NULL;
ipatm_aal4null.aal.v.aal4.forward_max_SDU_size = ATM_NIF_MTU;
ipatm_aal4null.aal.v.aal4.backward_max_SDU_size = ATM_NIF_MTU;
ipatm_aal4null.aal.v.aal4.SSCS_type = T_ATM_NULL;
ipatm_aal4null.aal.v.aal4.mid_low = 0;
ipatm_aal4null.aal.v.aal4.mid_high = 1023;
/*
* Listen for incoming calls
*/
for (i = 0;
i < (sizeof(ipatm_listeners) / sizeof(struct ipatm_listener));
i++) {
struct attr_aal *aalp = &ipatm_listeners[i].attr.aal;
int maxsdu = ATM_NIF_MTU;
/*
* Fill in union fields
*/
if (ipatm_listeners[i].attr.blli.tag_l2 == T_ATM_PRESENT) {
struct t_atm_blli *bp = &ipatm_listeners[i].attr.blli.v;
bp->layer_2_protocol.ID.simple_ID = T_ATM_BLLI2_I8802;
maxsdu += IPATM_LLC_LEN;
}
if (aalp->type == ATM_AAL5) {
aalp->v.aal5.forward_max_SDU_size = maxsdu;
aalp->v.aal5.backward_max_SDU_size = maxsdu;
aalp->v.aal5.SSCS_type = T_ATM_NULL;
} else {
aalp->v.aal4.forward_max_SDU_size = maxsdu;
aalp->v.aal4.backward_max_SDU_size = maxsdu;
aalp->v.aal4.SSCS_type = T_ATM_NULL;
aalp->v.aal4.mid_low = 0;
aalp->v.aal4.mid_high = 1023;
}
/*
* Now start listening
*/
if ((err = atm_cm_listen(NULL, &ipatm_endpt,
(void *)(intptr_t)i, &ipatm_listeners[i].attr,
&ipatm_listeners[i].conn)) != 0)
goto done;
}
/*
* Start background VCC idle timer
*/
atm_timeout(&ipatm_itimer, IPATM_IDLE_TIME, ipatm_itimeout);
done:
return (err);
}
/*
* Halt ipatm processing
*
* This will be called just prior to unloading the module from
* memory. All IP VCCs must be terminated before the protocol can
* be shutdown.
*
* Arguments:
* none
*
* Returns:
* 0 shutdown was successful
* errno shutdown failed - reason indicated
*
*/
static int
ipatm_stop()
{
struct ip_nif *inp;
int err = 0, i;
int s = splnet();
/*
* Any VCCs still open??
*/
if (ipatm_vccnt) {
/* Yes, can't stop now */
err = EBUSY;
goto done;
}
/*
* Kill VCC idle timer
*/
(void) atm_untimeout(&ipatm_itimer);
/*
* Stop listening for incoming calls
*/
for (i = 0;
i < (sizeof(ipatm_listeners) / sizeof(struct ipatm_listener));
i++) {
if (ipatm_listeners[i].conn != NULL) {
(void) atm_cm_release(ipatm_listeners[i].conn,
&ipatm_cause);
}
}
/*
* Detach all our interfaces
*/
while ((inp = ipatm_nif_head) != NULL) {
(void) ipatm_nifstat(NCM_DETACH, inp->inf_nif, 0);
}
/*
* De-register from system
*/
(void) atm_netconv_deregister(&ipatm_ncm);
(void) atm_endpoint_deregister(&ipatm_endpt);
/*
* Free up our storage pools
*/
uma_zdestroy(ipatm_vc_zone);
done:
(void) splx(s);
return (err);
}
#ifdef ATM_IP_MODULE
/*
*******************************************************************
*
* Loadable Module Support
*
*******************************************************************
*/
static int ipatm_doload(void);
static int ipatm_dounload(void);
/*
* Generic module load processing
*
* This function is called by an OS-specific function when this
* module is being loaded.
*
* Arguments:
* none
*
* Returns:
* 0 load was successful
* errno load failed - reason indicated
*
*/
static int
ipatm_doload()
{
int err = 0;
/*
* Start us up
*/
err = ipatm_start();
if (err)
/* Problems, clean up */
(void)ipatm_stop();
return (err);
}
/*
* Generic module unload processing
*
* This function is called by an OS-specific function when this
* module is being unloaded.
*
* Arguments:
* none
*
* Returns:
* 0 unload was successful
* errno unload failed - reason indicated
*
*/
static int
ipatm_dounload()
{
int err = 0;
/*
* OK, try to clean up our mess
*/
err = ipatm_stop();
return (err);
}
#include <sys/exec.h>
#include <sys/sysent.h>
#include <sys/lkm.h>
/*
* Loadable miscellaneous module description
*/
MOD_MISC(ipatm);
/*
* Loadable module support "load" entry point
*
* This is the routine called by the lkm driver whenever the
* modload(1) command is issued for this module.
*
* Arguments:
* lkmtp pointer to lkm drivers's structure
* cmd lkm command code
*
* Returns:
* 0 command was successful
* errno command failed - reason indicated
*
*/
static int
ipatm_load(lkmtp, cmd)
struct lkm_table *lkmtp;
int cmd;
{
return(ipatm_doload());
}
/*
* Loadable module support "unload" entry point
*
* This is the routine called by the lkm driver whenever the
* modunload(1) command is issued for this module.
*
* Arguments:
* lkmtp pointer to lkm drivers's structure
* cmd lkm command code
*
* Returns:
* 0 command was successful
* errno command failed - reason indicated
*
*/
static int
ipatm_unload(lkmtp, cmd)
struct lkm_table *lkmtp;
int cmd;
{
return(ipatm_dounload());
}
/*
* Loadable module support entry point
*
* This is the routine called by the lkm driver for all loadable module
* functions for this driver. This routine name must be specified
* on the modload(1) command. This routine will be called whenever the
* modload(1), modunload(1) or modstat(1) commands are issued for this
* module.
*
* Arguments:
* lkmtp pointer to lkm drivers's structure
* cmd lkm command code
* ver lkm version
*
* Returns:
* 0 command was successful
* errno command failed - reason indicated
*
*/
int
ipatm_mod(lkmtp, cmd, ver)
struct lkm_table *lkmtp;
int cmd;
int ver;
{
MOD_DISPATCH(ipatm, lkmtp, cmd, ver,
ipatm_load, ipatm_unload, lkm_nullcmd);
}
#else /* !ATM_IP_MODULE */
/*
*******************************************************************
*
* Kernel Compiled Module Support
*
*******************************************************************
*/
static void ipatm_doload(void *);
SYSINIT(atmipatm, SI_SUB_PROTO_END, SI_ORDER_ANY, ipatm_doload, NULL)
/*
* Kernel initialization
*
* Arguments:
* arg Not used
*
* Returns:
* none
*
*/
static void
ipatm_doload(void *arg)
{
int err = 0;
/*
* Start us up
*/
err = ipatm_start();
if (err) {
/* Problems, clean up */
(void)ipatm_stop();
log(LOG_ERR, "IP over ATM unable to initialize (%d)!!\n", err);
}
return;
}
#endif /* ATM_IP_MODULE */