Module Name: src Committed By: knakahara Date: Tue Dec 13 00:35:11 UTC 2016
Modified Files: src/sys/net: if_pppoe.c if_spppsubr.c if_spppvar.h Log Message: MP-safe pppoe(4). Nearly all parts is implemented by Shoichi YAMAGUCHI<s-yamaguchi@IIJ>, thanks. To generate a diff of this commit: cvs rdiff -u -r1.119 -r1.120 src/sys/net/if_pppoe.c cvs rdiff -u -r1.162 -r1.163 src/sys/net/if_spppsubr.c cvs rdiff -u -r1.19 -r1.20 src/sys/net/if_spppvar.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/net/if_pppoe.c diff -u src/sys/net/if_pppoe.c:1.119 src/sys/net/if_pppoe.c:1.120 --- src/sys/net/if_pppoe.c:1.119 Fri Nov 18 08:13:02 2016 +++ src/sys/net/if_pppoe.c Tue Dec 13 00:35:11 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: if_pppoe.c,v 1.119 2016/11/18 08:13:02 knakahara Exp $ */ +/* $NetBSD: if_pppoe.c,v 1.120 2016/12/13 00:35:11 knakahara Exp $ */ /*- * Copyright (c) 2002, 2008 The NetBSD Foundation, Inc. @@ -30,11 +30,12 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: if_pppoe.c,v 1.119 2016/11/18 08:13:02 knakahara Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if_pppoe.c,v 1.120 2016/12/13 00:35:11 knakahara Exp $"); #ifdef _KERNEL_OPT #include "pppoe.h" #include "opt_pppoe.h" +#include "opt_net_mpsafe.h" #endif #include <sys/param.h> @@ -52,6 +53,8 @@ __KERNEL_RCSID(0, "$NetBSD: if_pppoe.c,v #include <sys/device.h> #include <sys/module.h> #include <sys/sysctl.h> +#include <sys/rwlock.h> +#include <sys/mutex.h> #include <net/if.h> #include <net/if_types.h> @@ -64,6 +67,10 @@ __KERNEL_RCSID(0, "$NetBSD: if_pppoe.c,v #include "ioconf.h" +#ifdef NET_MPSAFE +#define PPPOE_MPSAFE 1 +#endif + struct pppoehdr { uint8_t vertype; uint8_t code; @@ -125,12 +132,39 @@ struct pppoetag { #define IFF_PASSIVE IFF_LINK0 /* wait passively for connection */ #endif +#define PPPOE_SESSION_LOCK(_sc, _op) rw_enter(&(_sc)->sc_session_lock, (_op)) +#define PPPOE_SESSION_UNLOCK(_sc) rw_exit(&(_sc)->sc_session_lock) +#define PPPOE_SESSION_LOCKED(_sc) rw_lock_held(&(_sc)->sc_session_lock) +#define PPPOE_SESSION_WLOCKED(_sc) rw_write_held(&(_sc)->sc_session_lock) +#define PPPOE_SESSION_RLOCKED(_sc) rw_read_held(&(_sc)->sc_session_lock) + +#define PPPOE_PARAM_LOCK(_sc) if ((_sc)->sc_lock) \ + mutex_enter((_sc)->sc_lock) +#define PPPOE_PARAM_UNLOCK(_sc) if ((_sc)->sc_lock) \ + mutex_exit((_sc)->sc_lock) +#define PPPOE_PARAM_LOCKED(_sc) (!(_sc)->sc_lock || \ + mutex_owned((_sc)->sc_lock)) +#ifdef PPPOE_MPSAFE +#define DECLARE_SPLNET_VARIABLE +#define ACQUIRE_SPLNET() do { } while (0) +#define RELEASE_SPLNET() do { } while (0) +#else +#define DECLARE_SPLNET_VARIABLE int __s +#define ACQUIRE_SPLNET() do { \ + __s = splnet(); \ + } while (0) +#define RELEASE_SPLNET() do { \ + splx(__s); \ + } while (0) +#endif + struct pppoe_softc { struct sppp sc_sppp; /* contains a struct ifnet as first element */ LIST_ENTRY(pppoe_softc) sc_list; struct ifnet *sc_eth_if; /* ethernet interface we are using */ int sc_state; /* discovery phase or session connected */ + bool sc_state_updating; /* state update in other components */ struct ether_addr sc_dest; /* hardware address of concentrator */ uint16_t sc_session; /* PPPoE session id */ @@ -147,6 +181,8 @@ struct pppoe_softc { callout_t sc_timeout; /* timeout while not in session state */ int sc_padi_retried; /* number of PADI retries already done */ int sc_padr_retried; /* number of PADR retries already done */ + krwlock_t sc_session_lock; /* lock of sc_state, sc_session, and sc_eth_if */ + kmutex_t *sc_lock; /* lock of other parameters */ }; /* incoming traffic will be queued here */ @@ -173,6 +209,9 @@ static int pppoe_ioctl(struct ifnet *, u static void pppoe_tls(struct sppp *); static void pppoe_tlf(struct sppp *); static void pppoe_start(struct ifnet *); +#ifdef PPPOE_MPSAFE +static int pppoe_transmit(struct ifnet *, struct mbuf *); +#endif static void pppoe_clear_softc(struct pppoe_softc *, const char *); /* internal timeout handling */ @@ -191,13 +230,15 @@ static int pppoe_send_padt(struct ifnet static int pppoe_output(struct pppoe_softc *, struct mbuf *); /* internal helper functions */ -static struct pppoe_softc * pppoe_find_softc_by_session(u_int, struct ifnet *); -static struct pppoe_softc * pppoe_find_softc_by_hunique(uint8_t *, size_t, struct ifnet *); +static struct pppoe_softc * pppoe_find_softc_by_session(u_int, struct ifnet *, krw_t); +static struct pppoe_softc * pppoe_find_softc_by_hunique(uint8_t *, size_t, + struct ifnet *, krw_t); static struct mbuf *pppoe_get_mbuf(size_t len); static int pppoe_ifattach_hook(void *, struct mbuf **, struct ifnet *, int); static LIST_HEAD(pppoe_softc_head, pppoe_softc) pppoe_softc_list; +static krwlock_t pppoe_softc_list_lock; static int pppoe_clone_create(struct if_clone *, int); static int pppoe_clone_destroy(struct ifnet *); @@ -226,10 +267,11 @@ pppoeinit(void) { LIST_INIT(&pppoe_softc_list); + rw_init(&pppoe_softc_list_lock); if_clone_attach(&pppoe_cloner); - pppoe_softintr = softint_establish(SOFTINT_NET, pppoe_softintr_handler, - NULL); + pppoe_softintr = softint_establish(SOFTINT_MPSAFE|SOFTINT_NET, + pppoe_softintr_handler, NULL); sysctl_net_pppoe_setup(&pppoe_sysctl_clog); IFQ_LOCK_INIT(&ppoediscinq); @@ -265,6 +307,9 @@ pppoe_clone_create(struct if_clone *ifc, sc->sc_sppp.pp_if.if_softc = sc; sc->sc_sppp.pp_if.if_mtu = PPPOE_MAXMTU; sc->sc_sppp.pp_if.if_flags = IFF_SIMPLEX|IFF_POINTOPOINT|IFF_MULTICAST; +#ifdef PPPOE_MPSAFE + sc->sc_sppp.pp_if.if_extflags = IFEF_OUTPUT_MPSAFE; +#endif sc->sc_sppp.pp_if.if_type = IFT_PPP; sc->sc_sppp.pp_if.if_hdrlen = sizeof(struct ether_header) + PPPOE_HEADERLEN; sc->sc_sppp.pp_if.if_dlt = DLT_PPP_ETHER; @@ -280,6 +325,9 @@ pppoe_clone_create(struct if_clone *ifc, callout_init(&sc->sc_timeout, 0); sc->sc_sppp.pp_if.if_start = pppoe_start; +#ifdef PPPOE_MPSAFE + sc->sc_sppp.pp_if.if_transmit = pppoe_transmit; +#endif sc->sc_sppp.pp_tls = pppoe_tls; sc->sc_sppp.pp_tlf = pppoe_tlf; sc->sc_sppp.pp_framebytes = PPPOE_HEADERLEN; /* framing added to ppp packets */ @@ -293,7 +341,13 @@ pppoe_clone_create(struct if_clone *ifc, if (LIST_EMPTY(&pppoe_softc_list)) { pfil_add_hook(pppoe_ifattach_hook, NULL, PFIL_IFNET, if_pfil); } + + sc->sc_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_SOFTNET); + rw_init(&sc->sc_session_lock); + + rw_enter(&pppoe_softc_list_lock, RW_WRITER); LIST_INSERT_HEAD(&pppoe_softc_list, sc, sc_list); + rw_exit(&pppoe_softc_list_lock); return 0; } @@ -302,11 +356,20 @@ pppoe_clone_destroy(struct ifnet *ifp) { struct pppoe_softc * sc = ifp->if_softc; - callout_stop(&sc->sc_timeout); + rw_enter(&pppoe_softc_list_lock, RW_WRITER); + + PPPOE_PARAM_LOCK(sc); + callout_halt(&sc->sc_timeout, NULL); + LIST_REMOVE(sc, sc_list); + if (LIST_EMPTY(&pppoe_softc_list)) { pfil_remove_hook(pppoe_ifattach_hook, NULL, PFIL_IFNET, if_pfil); } + rw_exit(&pppoe_softc_list_lock); + + PPPOE_SESSION_LOCK(sc, RW_WRITER); + bpf_detach(ifp); sppp_detach(&sc->sc_sppp.pp_if); if_detach(ifp); @@ -319,6 +382,13 @@ pppoe_clone_destroy(struct ifnet *ifp) if (sc->sc_relay_sid) free(sc->sc_relay_sid, M_DEVBUF); callout_destroy(&sc->sc_timeout); + + PPPOE_PARAM_UNLOCK(sc); + if (sc->sc_lock) + mutex_obj_free(sc->sc_lock); + PPPOE_SESSION_UNLOCK(sc); + rw_destroy(&sc->sc_session_lock); + free(sc, M_DEVBUF); return (0); @@ -331,26 +401,32 @@ pppoe_clone_destroy(struct ifnet *ifp) * be 1. */ static struct pppoe_softc * -pppoe_find_softc_by_session(u_int session, struct ifnet *rcvif) +pppoe_find_softc_by_session(u_int session, struct ifnet *rcvif, krw_t lock) { - struct pppoe_softc *sc; + struct pppoe_softc *sc = NULL; if (session == 0) return NULL; - + rw_enter(&pppoe_softc_list_lock, RW_READER); LIST_FOREACH(sc, &pppoe_softc_list, sc_list) { - if (sc->sc_state == PPPOE_STATE_SESSION + PPPOE_SESSION_LOCK(sc, lock); + if (!sc->sc_state_updating + && sc->sc_state == PPPOE_STATE_SESSION && sc->sc_session == session && sc->sc_eth_if == rcvif) - return sc; + break; + + PPPOE_SESSION_UNLOCK(sc); } - return NULL; + rw_exit(&pppoe_softc_list_lock); + return sc; } /* Check host unique token passed and return appropriate softc pointer, * or NULL if token is bogus. */ static struct pppoe_softc * -pppoe_find_softc_by_hunique(uint8_t *token, size_t len, struct ifnet *rcvif) +pppoe_find_softc_by_hunique(uint8_t *token, size_t len, + struct ifnet *rcvif, krw_t lock) { struct pppoe_softc *sc, *t; @@ -361,8 +437,14 @@ pppoe_find_softc_by_hunique(uint8_t *tok return NULL; memcpy(&t, token, len); - LIST_FOREACH(sc, &pppoe_softc_list, sc_list) - if (sc == t) break; + rw_enter(&pppoe_softc_list_lock, RW_READER); + LIST_FOREACH(sc, &pppoe_softc_list, sc_list) { + if (sc == t) { + PPPOE_SESSION_LOCK(sc, lock); + break; + } + } + rw_exit(&pppoe_softc_list_lock); if (sc == NULL) { #ifdef PPPOE_DEBUG @@ -371,15 +453,26 @@ pppoe_find_softc_by_hunique(uint8_t *tok return NULL; } + if (sc->sc_state_updating) { +#ifdef PPPOE_DEBUG + printf("%s: host unique tag found, but its state is updating\n", + sc->sc_sppp.pp_if.if_xname); +#endif + PPPOE_SESSION_UNLOCK(sc); + return NULL; + } + /* should be safe to access *sc now */ if (sc->sc_state < PPPOE_STATE_PADI_SENT || sc->sc_state >= PPPOE_STATE_SESSION) { printf("%s: host unique tag found, but it belongs to a connection in state %d\n", sc->sc_sppp.pp_if.if_xname, sc->sc_state); + PPPOE_SESSION_UNLOCK(sc); return NULL; } if (sc->sc_eth_if != rcvif) { printf("%s: wrong interface, not accepting host unique\n", sc->sc_sppp.pp_if.if_xname); + PPPOE_SESSION_UNLOCK(sc); return NULL; } return sc; @@ -389,9 +482,7 @@ static void pppoe_softintr_handler(void *dummy) { /* called at splsoftnet() */ - mutex_enter(softnet_lock); pppoeintr(); - mutex_exit(softnet_lock); } /* called at appropriate protection level */ @@ -401,6 +492,10 @@ pppoeintr(void) struct mbuf *m; int disc_done, data_done; +#ifndef PPPOE_MPSAFE + mutex_enter(softnet_lock); +#endif + do { disc_done = 0; data_done = 0; @@ -422,6 +517,9 @@ pppoeintr(void) pppoe_data_input(m); } } while (disc_done || data_done); +#ifndef PPPOE_MPSAFE + mutex_exit(softnet_lock); +#endif } /* analyze and handle a single received packet while not in session state */ @@ -431,7 +529,8 @@ pppoe_dispatch_disc_pkt(struct mbuf *m, uint16_t tag, len; uint16_t session, plen; struct pppoe_softc *sc; - const char *err_msg, *devname; + const char *err_msg; + char devname[IF_NAMESIZE]; char *error; uint8_t *ac_cookie; size_t ac_cookie_len; @@ -447,7 +546,9 @@ pppoe_dispatch_disc_pkt(struct mbuf *m, int noff, err, errortag; struct ether_header *eh; - devname = "pppoe"; /* as long as we don't know which instance */ + /* as long as we don't know which instance */ + strlcpy(devname, "pppoe", sizeof(devname)); + err_msg = NULL; errortag = 0; if (m->m_len < sizeof(*eh)) { @@ -552,10 +653,13 @@ pppoe_dispatch_disc_pkt(struct mbuf *m, #endif rcvif = m_get_rcvif(m, &s); sc = pppoe_find_softc_by_hunique(mtod(n, char *) + noff, - len, rcvif); + len, rcvif, RW_READER); m_put_rcvif(rcvif, &s); - if (sc != NULL) - devname = sc->sc_sppp.pp_if.if_xname; + if (sc != NULL) { + strlcpy(devname, sc->sc_sppp.pp_if.if_xname, + sizeof(devname)); + PPPOE_SESSION_UNLOCK(sc); + } break; } case PPPOE_TAG_ACCOOKIE: @@ -629,31 +733,50 @@ breakbreak:; */ if (LIST_EMPTY(&pppoe_softc_list)) goto done; + rw_enter(&pppoe_softc_list_lock, RW_READER); LIST_FOREACH(sc, &pppoe_softc_list, sc_list) { - if (!(sc->sc_sppp.pp_if.if_flags & IFF_UP)) + PPPOE_SESSION_LOCK(sc, RW_WRITER); + if (!(sc->sc_sppp.pp_if.if_flags & IFF_UP)) { + PPPOE_SESSION_UNLOCK(sc); continue; - if (!(sc->sc_sppp.pp_if.if_flags & IFF_PASSIVE)) + } + if (!(sc->sc_sppp.pp_if.if_flags & IFF_PASSIVE)) { + PPPOE_SESSION_UNLOCK(sc); continue; - if (sc->sc_state == PPPOE_STATE_INITIAL) + } + + if (!sc->sc_state_updating + && sc->sc_state == PPPOE_STATE_INITIAL) break; + + PPPOE_SESSION_UNLOCK(sc); } + rw_exit(&pppoe_softc_list_lock); + if (sc == NULL) { /* printf("pppoe: free passive interface is not found\n");*/ goto done; } + + PPPOE_PARAM_LOCK(sc); if (hunique) { if (sc->sc_hunique) free(sc->sc_hunique, M_DEVBUF); sc->sc_hunique = malloc(hunique_len, M_DEVBUF, M_DONTWAIT); - if (sc->sc_hunique == NULL) + if (sc->sc_hunique == NULL) { + PPPOE_PARAM_UNLOCK(sc); + PPPOE_SESSION_UNLOCK(sc); goto done; + } sc->sc_hunique_len = hunique_len; memcpy(sc->sc_hunique, hunique, hunique_len); } memcpy(&sc->sc_dest, eh->ether_shost, sizeof sc->sc_dest); sc->sc_state = PPPOE_STATE_PADO_SENT; pppoe_send_pado(sc); + PPPOE_PARAM_UNLOCK(sc); + PPPOE_SESSION_UNLOCK(sc); break; #endif /* PPPOE_SERVER */ case PPPOE_CODE_PADR: @@ -668,31 +791,44 @@ breakbreak:; } sc = pppoe_find_softc_by_hunique(ac_cookie, ac_cookie_len, - m_get_rcvif_NOMPSAFE(m)); + m_get_rcvif_NOMPSAFE(m), + RW_WRITER); if (sc == NULL) { /* be quiet if there is not a single pppoe instance */ if (!LIST_EMPTY(&pppoe_softc_list)) printf("pppoe: received PADR but could not find request for it\n"); goto done; } - if (sc->sc_state != PPPOE_STATE_PADO_SENT) { + + if (sc->sc_state_updating + || sc->sc_state != PPPOE_STATE_PADO_SENT) { printf("%s: received unexpected PADR\n", sc->sc_sppp.pp_if.if_xname); + PPPOE_SESSION_UNLOCK(sc); goto done; } + PPPOE_PARAM_LOCK(sc); if (hunique) { if (sc->sc_hunique) free(sc->sc_hunique, M_DEVBUF); sc->sc_hunique = malloc(hunique_len, M_DEVBUF, M_DONTWAIT); - if (sc->sc_hunique == NULL) + if (sc->sc_hunique == NULL) { + PPPOE_PARAM_UNLOCK(sc); + PPPOE_SESSION_UNLOCK(sc); goto done; + } sc->sc_hunique_len = hunique_len; memcpy(sc->sc_hunique, hunique, hunique_len); } pppoe_send_pads(sc); sc->sc_state = PPPOE_STATE_SESSION; + PPPOE_PARAM_UNLOCK(sc); + PPPOE_SESSION_UNLOCK(sc); + + sppp_lock_enter(&sc->sc_sppp); sc->sc_sppp.pp_up(&sc->sc_sppp); + sppp_lock_exit(&sc->sc_sppp); break; #else /* ignore, we are no access concentrator */ @@ -705,11 +841,18 @@ breakbreak:; printf("pppoe: received PADO but could not find request for it\n"); goto done; } - if (sc->sc_state != PPPOE_STATE_PADI_SENT) { + + PPPOE_SESSION_LOCK(sc, RW_WRITER); + + if (sc->sc_state_updating + || sc->sc_state != PPPOE_STATE_PADI_SENT) { printf("%s: received unexpected PADO\n", sc->sc_sppp.pp_if.if_xname); + PPPOE_SESSION_UNLOCK(sc); goto done; } + + PPPOE_PARAM_LOCK(sc); if (ac_cookie) { if (sc->sc_ac_cookie) free(sc->sc_ac_cookie, M_DEVBUF); @@ -719,6 +862,8 @@ breakbreak:; printf("%s: FATAL: could not allocate memory " "for AC cookie\n", sc->sc_sppp.pp_if.if_xname); + PPPOE_PARAM_UNLOCK(sc); + PPPOE_SESSION_UNLOCK(sc); goto done; } sc->sc_ac_cookie_len = ac_cookie_len; @@ -733,6 +878,8 @@ breakbreak:; printf("%s: FATAL: could not allocate memory " "for relay SID\n", sc->sc_sppp.pp_if.if_xname); + PPPOE_PARAM_UNLOCK(sc); + PPPOE_SESSION_UNLOCK(sc); goto done; } sc->sc_relay_sid_len = relay_sid_len; @@ -751,34 +898,63 @@ breakbreak:; callout_reset(&sc->sc_timeout, PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried), pppoe_timeout, sc); + + PPPOE_PARAM_UNLOCK(sc); + PPPOE_SESSION_UNLOCK(sc); break; case PPPOE_CODE_PADS: if (sc == NULL) goto done; + + PPPOE_SESSION_LOCK(sc, RW_WRITER); + PPPOE_PARAM_LOCK(sc); + + if (sc->sc_state_updating) { + PPPOE_PARAM_UNLOCK(sc); + PPPOE_SESSION_UNLOCK(sc); + goto done; + } + sc->sc_session = session; callout_stop(&sc->sc_timeout); if (sc->sc_sppp.pp_if.if_flags & IFF_DEBUG) printf("%s: session 0x%x connected\n", sc->sc_sppp.pp_if.if_xname, session); sc->sc_state = PPPOE_STATE_SESSION; + PPPOE_PARAM_UNLOCK(sc); + PPPOE_SESSION_UNLOCK(sc); + + sppp_lock_enter(&sc->sc_sppp); sc->sc_sppp.pp_up(&sc->sc_sppp); /* notify upper layers */ + sppp_lock_exit(&sc->sc_sppp); break; case PPPOE_CODE_PADT: { struct ifnet *rcvif; int s; rcvif = m_get_rcvif(m, &s); - sc = pppoe_find_softc_by_session(session, rcvif); + sc = pppoe_find_softc_by_session(session, rcvif, RW_WRITER); m_put_rcvif(rcvif, &s); if (sc == NULL) goto done; + + PPPOE_PARAM_LOCK(sc); pppoe_clear_softc(sc, "received PADT"); + PPPOE_PARAM_UNLOCK(sc); + PPPOE_SESSION_UNLOCK(sc); break; } default: + if (sc != NULL) { + PPPOE_SESSION_LOCK(sc, RW_READER); + strlcpy(devname, sc->sc_sppp.pp_if.if_xname, + sizeof(devname)); + PPPOE_SESSION_UNLOCK(sc); + } else + strlcpy(devname, "pppoe", sizeof(devname)); + printf("%s: unknown code (0x%04x) session = 0x%04x\n", - sc? sc->sc_sppp.pp_if.if_xname : "pppoe", - ph->code, session); + devname, ph->code, session); break; } @@ -843,7 +1019,7 @@ pppoe_data_input(struct mbuf *m) rcvif = m_get_rcvif_psref(m, &psref); if (__predict_false(rcvif == NULL)) goto drop; - sc = pppoe_find_softc_by_session(session, rcvif); + sc = pppoe_find_softc_by_session(session, rcvif, RW_READER); if (sc == NULL) { if (pppoe_term_unknown) { printf("pppoe: input for unknown session %#x, " @@ -853,6 +1029,7 @@ pppoe_data_input(struct mbuf *m) m_put_rcvif_psref(rcvif, &psref); goto drop; } + m_put_rcvif_psref(rcvif, &psref); plen = ntohs(ph->plen); @@ -876,6 +1053,7 @@ pppoe_data_input(struct mbuf *m) printf("\n"); } #endif + PPPOE_SESSION_UNLOCK(sc); if (m->m_pkthdr.len < plen) goto drop; @@ -899,6 +1077,9 @@ pppoe_output(struct pppoe_softc *sc, str struct ether_header *eh; uint16_t etype; + KASSERT(PPPOE_SESSION_LOCKED(sc)); + KASSERT(PPPOE_PARAM_LOCKED(sc)); + if (sc->sc_eth_if == NULL) { m_freem(m); return EIO; @@ -942,9 +1123,11 @@ pppoe_ioctl(struct ifnet *ifp, unsigned if (parms->eth_ifname[0] != 0) { struct ifnet *eth_if; + PPPOE_SESSION_LOCK(sc, RW_WRITER); eth_if = ifunit(parms->eth_ifname); if (eth_if == NULL || eth_if->if_dlt != DLT_EN10MB) { sc->sc_eth_if = NULL; + PPPOE_SESSION_UNLOCK(sc); return ENXIO; } @@ -954,6 +1137,7 @@ pppoe_ioctl(struct ifnet *ifp, unsigned PPPOE_OVERHEAD; } sc->sc_eth_if = eth_if; + PPPOE_SESSION_UNLOCK(sc); } if (parms->ac_name != NULL) { size_t s; @@ -971,9 +1155,12 @@ pppoe_ioctl(struct ifnet *ifp, unsigned free(b, M_DEVBUF); return EINVAL; } + + PPPOE_PARAM_LOCK(sc); if (sc->sc_concentrator_name) free(sc->sc_concentrator_name, M_DEVBUF); sc->sc_concentrator_name = b; + PPPOE_PARAM_UNLOCK(sc); } if (parms->service_name != NULL) { size_t s; @@ -991,9 +1178,12 @@ pppoe_ioctl(struct ifnet *ifp, unsigned free(b, M_DEVBUF); return EINVAL; } + + PPPOE_PARAM_LOCK(sc); if (sc->sc_service_name) free(sc->sc_service_name, M_DEVBUF); sc->sc_service_name = b; + PPPOE_PARAM_UNLOCK(sc); } return 0; } @@ -1002,19 +1192,25 @@ pppoe_ioctl(struct ifnet *ifp, unsigned { struct pppoediscparms *parms = (struct pppoediscparms*)data; memset(parms, 0, sizeof *parms); + PPPOE_SESSION_LOCK(sc, RW_READER); if (sc->sc_eth_if) strlcpy(parms->ifname, sc->sc_eth_if->if_xname, sizeof(parms->ifname)); + PPPOE_SESSION_UNLOCK(sc); return 0; } break; case PPPOEGETSESSION: { struct pppoeconnectionstate *state = (struct pppoeconnectionstate*)data; + PPPOE_SESSION_LOCK(sc, RW_READER); + PPPOE_PARAM_LOCK(sc); state->state = sc->sc_state; state->session_id = sc->sc_session; state->padi_retry_no = sc->sc_padi_retried; state->padr_retry_no = sc->sc_padr_retried; + PPPOE_PARAM_UNLOCK(sc); + PPPOE_SESSION_UNLOCK(sc); return 0; } break; @@ -1023,6 +1219,9 @@ pppoe_ioctl(struct ifnet *ifp, unsigned * Prevent running re-establishment timers overriding * administrators choice. */ + PPPOE_SESSION_LOCK(sc, RW_WRITER); + PPPOE_PARAM_LOCK(sc); + if ((ifr->ifr_flags & IFF_UP) == 0 && sc->sc_state >= PPPOE_STATE_PADI_SENT && sc->sc_state < PPPOE_STATE_SESSION) { @@ -1032,8 +1231,19 @@ pppoe_ioctl(struct ifnet *ifp, unsigned sc->sc_padr_retried = 0; memcpy(&sc->sc_dest, etherbroadcastaddr, sizeof(sc->sc_dest)); + sc->sc_state_updating = 1; } - return sppp_ioctl(ifp, cmd, data); + + PPPOE_PARAM_UNLOCK(sc); + PPPOE_SESSION_UNLOCK(sc); + + error = sppp_ioctl(ifp, cmd, data); + + PPPOE_SESSION_LOCK(sc, RW_WRITER); + sc->sc_state_updating = 0; + PPPOE_SESSION_UNLOCK(sc); + + return error; case SIOCSIFMTU: if (ifr->ifr_mtu > (sc->sc_eth_if == NULL ? PPPOE_MAXMTU : (sc->sc_eth_if->if_mtu - PPPOE_OVERHEAD))) { @@ -1081,6 +1291,9 @@ pppoe_send_padi(struct pppoe_softc *sc) int len, l1 = 0, l2 = 0; /* XXX: gcc */ uint8_t *p; + KASSERT(PPPOE_SESSION_LOCKED(sc)); + KASSERT(PPPOE_PARAM_LOCKED(sc)); + if (sc->sc_state >PPPOE_STATE_PADI_SENT) panic("pppoe_send_padi in state %d", sc->sc_state); @@ -1145,13 +1358,16 @@ pppoe_send_padi(struct pppoe_softc *sc) static void pppoe_timeout(void *arg) { - int x, retry_wait, err; + int retry_wait, err; struct pppoe_softc *sc = (struct pppoe_softc*)arg; + DECLARE_SPLNET_VARIABLE; #ifdef PPPOE_DEBUG printf("%s: timeout\n", sc->sc_sppp.pp_if.if_xname); #endif + PPPOE_SESSION_LOCK(sc, RW_WRITER); + PPPOE_PARAM_LOCK(sc); switch (sc->sc_state) { case PPPOE_STATE_INITIAL: /* delayed connect from pppoe_tls() */ @@ -1171,7 +1387,7 @@ pppoe_timeout(void *arg) /* initialize for quick retry mode */ retry_wait = PPPOE_DISC_TIMEOUT * (1 + sc->sc_padi_retried); - x = splnet(); + ACQUIRE_SPLNET(); sc->sc_padi_retried++; if (sc->sc_padi_retried >= PPPOE_DISC_MAXPADI) { if ((sc->sc_sppp.pp_if.if_flags & IFF_LINK1) == 0) { @@ -1179,7 +1395,9 @@ pppoe_timeout(void *arg) retry_wait = PPPOE_SLOW_RETRY; } else { pppoe_abort_connect(sc); - splx(x); + PPPOE_PARAM_UNLOCK(sc); + PPPOE_SESSION_UNLOCK(sc); + RELEASE_SPLNET(); return; } } @@ -1192,11 +1410,11 @@ pppoe_timeout(void *arg) } callout_reset(&sc->sc_timeout, retry_wait, pppoe_timeout, sc); - splx(x); + RELEASE_SPLNET(); break; case PPPOE_STATE_PADR_SENT: - x = splnet(); + ACQUIRE_SPLNET(); sc->sc_padr_retried++; if (sc->sc_padr_retried >= PPPOE_DISC_MAXPADR) { memcpy(&sc->sc_dest, etherbroadcastaddr, @@ -1213,7 +1431,9 @@ pppoe_timeout(void *arg) callout_reset(&sc->sc_timeout, PPPOE_DISC_TIMEOUT * (1 + sc->sc_padi_retried), pppoe_timeout, sc); - splx(x); + RELEASE_SPLNET(); + PPPOE_PARAM_UNLOCK(sc); + PPPOE_SESSION_UNLOCK(sc); return; } if ((err = pppoe_send_padr(sc)) != 0) { @@ -1226,21 +1446,29 @@ pppoe_timeout(void *arg) callout_reset(&sc->sc_timeout, PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried), pppoe_timeout, sc); - splx(x); + RELEASE_SPLNET(); break; case PPPOE_STATE_CLOSING: pppoe_disconnect(sc); break; default: + PPPOE_PARAM_UNLOCK(sc); + PPPOE_SESSION_UNLOCK(sc); return; /* all done, work in peace */ } + PPPOE_PARAM_UNLOCK(sc); + PPPOE_SESSION_UNLOCK(sc); } /* Start a connection (i.e. initiate discovery phase) */ static int pppoe_connect(struct pppoe_softc *sc) { - int x, err; + int err; + DECLARE_SPLNET_VARIABLE; + + KASSERT(PPPOE_SESSION_WLOCKED(sc)); + KASSERT(PPPOE_PARAM_LOCKED(sc)); if (sc->sc_state != PPPOE_STATE_INITIAL) return EBUSY; @@ -1250,7 +1478,7 @@ pppoe_connect(struct pppoe_softc *sc) if ((sc->sc_sppp.pp_if.if_flags & IFF_PASSIVE)) return 0; #endif - x = splnet(); + ACQUIRE_SPLNET(); /* save state, in case we fail to send PADI */ sc->sc_state = PPPOE_STATE_PADI_SENT; sc->sc_padr_retried = 0; @@ -1259,7 +1487,7 @@ pppoe_connect(struct pppoe_softc *sc) printf("%s: failed to send PADI, error=%d\n", sc->sc_sppp.pp_if.if_xname, err); callout_reset(&sc->sc_timeout, PPPOE_DISC_TIMEOUT, pppoe_timeout, sc); - splx(x); + RELEASE_SPLNET(); return err; } @@ -1267,9 +1495,13 @@ pppoe_connect(struct pppoe_softc *sc) static int pppoe_disconnect(struct pppoe_softc *sc) { - int err, x; + int err; + DECLARE_SPLNET_VARIABLE; + + KASSERT(PPPOE_SESSION_WLOCKED(sc)); + KASSERT(PPPOE_PARAM_LOCKED(sc)); - x = splnet(); + ACQUIRE_SPLNET(); if (sc->sc_state < PPPOE_STATE_SESSION) err = EBUSY; @@ -1282,6 +1514,8 @@ pppoe_disconnect(struct pppoe_softc *sc) /* cleanup softc */ sc->sc_state = PPPOE_STATE_INITIAL; + sc->sc_state_updating = 1; + memcpy(&sc->sc_dest, etherbroadcastaddr, sizeof(sc->sc_dest)); if (sc->sc_ac_cookie) { free(sc->sc_ac_cookie, M_DEVBUF); @@ -1302,11 +1536,19 @@ pppoe_disconnect(struct pppoe_softc *sc) #endif sc->sc_session = 0; + PPPOE_PARAM_UNLOCK(sc); + PPPOE_SESSION_UNLOCK(sc); + /* notify upper layer */ + sppp_lock_enter(&sc->sc_sppp); sc->sc_sppp.pp_down(&sc->sc_sppp); + sppp_lock_exit(&sc->sc_sppp); - splx(x); + PPPOE_SESSION_LOCK(sc, RW_WRITER); + PPPOE_PARAM_LOCK(sc); + sc->sc_state_updating = 0; + RELEASE_SPLNET(); return err; } @@ -1314,12 +1556,26 @@ pppoe_disconnect(struct pppoe_softc *sc) static void pppoe_abort_connect(struct pppoe_softc *sc) { + KASSERT(PPPOE_SESSION_WLOCKED(sc)); + KASSERT(PPPOE_PARAM_LOCKED(sc)); + printf("%s: could not establish connection\n", sc->sc_sppp.pp_if.if_xname); sc->sc_state = PPPOE_STATE_CLOSING; + sc->sc_state_updating = 1; + + PPPOE_PARAM_UNLOCK(sc); + PPPOE_SESSION_UNLOCK(sc); /* notify upper layer */ + sppp_lock_enter(&sc->sc_sppp); sc->sc_sppp.pp_down(&sc->sc_sppp); + sppp_lock_exit(&sc->sc_sppp); + + PPPOE_SESSION_LOCK(sc, RW_WRITER); + PPPOE_PARAM_LOCK(sc); + + sc->sc_state_updating = 0; /* clear connection state */ memcpy(&sc->sc_dest, etherbroadcastaddr, sizeof(sc->sc_dest)); @@ -1334,6 +1590,9 @@ pppoe_send_padr(struct pppoe_softc *sc) uint8_t *p; size_t len, l1 = 0; /* XXX: gcc */ + KASSERT(PPPOE_SESSION_LOCKED(sc)); + KASSERT(PPPOE_PARAM_LOCKED(sc)); + if (sc->sc_state != PPPOE_STATE_PADR_SENT) return EIO; @@ -1428,6 +1687,9 @@ pppoe_send_pado(struct pppoe_softc *sc) uint8_t *p; size_t len; + KASSERT(PPPOE_SESSION_LOCKED(sc)); /* required by pppoe_output(). */ + KASSERT(PPPOE_PARAM_LOCKED(sc)); + if (sc->sc_state != PPPOE_STATE_PADO_SENT) return EIO; @@ -1460,6 +1722,9 @@ pppoe_send_pads(struct pppoe_softc *sc) uint8_t *p; size_t len, l1 = 0; /* XXX: gcc */ + KASSERT(PPPOE_SESSION_WLOCKED(sc)); + KASSERT(PPPOE_PARAM_LOCKED(sc)); + if (sc->sc_state != PPPOE_STATE_PADO_SENT) return EIO; @@ -1499,8 +1764,17 @@ pppoe_tls(struct sppp *sp) struct pppoe_softc *sc = (void *)sp; int wtime; - if (sc->sc_state != PPPOE_STATE_INITIAL) + KASSERT(!PPPOE_SESSION_LOCKED(sc)); + KASSERT(!PPPOE_PARAM_LOCKED(sc)); + + PPPOE_SESSION_LOCK(sc, RW_READER); + + if (sc->sc_state != PPPOE_STATE_INITIAL) { + PPPOE_SESSION_UNLOCK(sc); return; + } + + PPPOE_PARAM_LOCK(sc); if (sc->sc_sppp.pp_phase == SPPP_PHASE_ESTABLISH && sc->sc_sppp.pp_auth_failures > 0) { @@ -1515,21 +1789,37 @@ pppoe_tls(struct sppp *sp) wtime = PPPOE_RECON_IMMEDIATE; } callout_reset(&sc->sc_timeout, wtime, pppoe_timeout, sc); + + PPPOE_PARAM_UNLOCK(sc); + PPPOE_SESSION_UNLOCK(sc); } static void pppoe_tlf(struct sppp *sp) { struct pppoe_softc *sc = (void *)sp; - if (sc->sc_state < PPPOE_STATE_SESSION) + + KASSERT(!PPPOE_SESSION_LOCKED(sc)); + KASSERT(!PPPOE_PARAM_LOCKED(sc)); + + PPPOE_SESSION_LOCK(sc, RW_WRITER); + + if (sc->sc_state < PPPOE_STATE_SESSION) { + PPPOE_SESSION_UNLOCK(sc); return; + } /* * Do not call pppoe_disconnect here, the upper layer state * machine gets confused by this. We must return from this * function and defer disconnecting to the timeout handler. */ sc->sc_state = PPPOE_STATE_CLOSING; + PPPOE_PARAM_LOCK(sc); + callout_reset(&sc->sc_timeout, hz/50, pppoe_timeout, sc); + + PPPOE_PARAM_UNLOCK(sc); + PPPOE_SESSION_UNLOCK(sc); } static void @@ -1557,37 +1847,89 @@ pppoe_start(struct ifnet *ifp) continue; } p = mtod(m, uint8_t *); + PPPOE_SESSION_LOCK(sc, RW_READER); PPPOE_ADD_HEADER(p, 0, sc->sc_session, len); bpf_mtap(&sc->sc_sppp.pp_if, m); + PPPOE_PARAM_LOCK(sc); pppoe_output(sc, m); + PPPOE_PARAM_UNLOCK(sc); + PPPOE_SESSION_UNLOCK(sc); } } +#ifdef PPPOE_MPSAFE +static int +pppoe_transmit(struct ifnet *ifp, struct mbuf *m) +{ + struct pppoe_softc *sc = (void *)ifp; + uint8_t *p; + size_t len; + + if (m == NULL) + return EINVAL; + + /* are we ready to process data yet? */ + PPPOE_SESSION_LOCK(sc, RW_READER); + if (sc->sc_state < PPPOE_STATE_SESSION) { + PPPOE_SESSION_UNLOCK(sc); + m_free(m); + return ENOBUFS; + } + + len = m->m_pkthdr.len; + M_PREPEND(m, PPPOE_HEADERLEN, M_DONTWAIT); + if (m == NULL) { + PPPOE_SESSION_UNLOCK(sc); + ifp->if_oerrors++; + return ENETDOWN; + } + p = mtod(m, uint8_t *); + PPPOE_ADD_HEADER(p, 0, sc->sc_session, len); + + bpf_mtap(&sc->sc_sppp.pp_if, m); + + PPPOE_PARAM_LOCK(sc); + pppoe_output(sc, m); + PPPOE_PARAM_UNLOCK(sc); + PPPOE_SESSION_UNLOCK(sc); + return 0; +} +#endif /* PPPOE_MPSAFE */ static int pppoe_ifattach_hook(void *arg, struct mbuf **mp, struct ifnet *ifp, int dir) { struct pppoe_softc *sc; - int s; + DECLARE_SPLNET_VARIABLE; if (mp != (struct mbuf **)PFIL_IFNET_DETACH) return 0; - s = splnet(); + ACQUIRE_SPLNET(); + rw_enter(&pppoe_softc_list_lock, RW_READER); LIST_FOREACH(sc, &pppoe_softc_list, sc_list) { - if (sc->sc_eth_if != ifp) + PPPOE_SESSION_LOCK(sc, RW_WRITER); + if (sc->sc_eth_if != ifp) { + PPPOE_SESSION_UNLOCK(sc); continue; + } + sppp_lock_enter(&sc->sc_sppp); if (sc->sc_sppp.pp_if.if_flags & IFF_UP) { sc->sc_sppp.pp_if.if_flags &= ~(IFF_UP|IFF_RUNNING); printf("%s: ethernet interface detached, going down\n", sc->sc_sppp.pp_if.if_xname); } + sppp_lock_exit(&sc->sc_sppp); sc->sc_eth_if = NULL; + PPPOE_PARAM_LOCK(sc); pppoe_clear_softc(sc, "ethernet interface detached"); + PPPOE_PARAM_UNLOCK(sc); + PPPOE_SESSION_UNLOCK(sc); } - splx(s); + rw_exit(&pppoe_softc_list_lock); + RELEASE_SPLNET(); return 0; } @@ -1595,6 +1937,9 @@ pppoe_ifattach_hook(void *arg, struct mb static void pppoe_clear_softc(struct pppoe_softc *sc, const char *message) { + KASSERT(PPPOE_SESSION_WLOCKED(sc)); + KASSERT(PPPOE_PARAM_LOCKED(sc)); + /* stop timer */ callout_stop(&sc->sc_timeout); if (sc->sc_sppp.pp_if.if_flags & IFF_DEBUG) @@ -1603,9 +1948,20 @@ pppoe_clear_softc(struct pppoe_softc *sc /* fix our state */ sc->sc_state = PPPOE_STATE_INITIAL; + sc->sc_state_updating = 1; + + PPPOE_PARAM_UNLOCK(sc); + PPPOE_SESSION_UNLOCK(sc); /* signal upper layer */ + sppp_lock_enter(&sc->sc_sppp); sc->sc_sppp.pp_down(&sc->sc_sppp); + sppp_lock_exit(&sc->sc_sppp); + + PPPOE_SESSION_LOCK(sc, RW_WRITER); + PPPOE_PARAM_LOCK(sc); + + sc->sc_state_updating = 0; /* clean up softc */ memcpy(&sc->sc_dest, etherbroadcastaddr, sizeof(sc->sc_dest)); Index: src/sys/net/if_spppsubr.c diff -u src/sys/net/if_spppsubr.c:1.162 src/sys/net/if_spppsubr.c:1.163 --- src/sys/net/if_spppsubr.c:1.162 Tue Dec 6 07:01:47 2016 +++ src/sys/net/if_spppsubr.c Tue Dec 13 00:35:11 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: if_spppsubr.c,v 1.162 2016/12/06 07:01:47 knakahara Exp $ */ +/* $NetBSD: if_spppsubr.c,v 1.163 2016/12/13 00:35:11 knakahara Exp $ */ /* * Synchronous PPP/Cisco link level subroutines. @@ -41,7 +41,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: if_spppsubr.c,v 1.162 2016/12/06 07:01:47 knakahara Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if_spppsubr.c,v 1.163 2016/12/13 00:35:11 knakahara Exp $"); #if defined(_KERNEL_OPT) #include "opt_inet.h" @@ -91,6 +91,10 @@ __KERNEL_RCSID(0, "$NetBSD: if_spppsubr. #include <net/if_sppp.h> #include <net/if_spppvar.h> +#ifdef NET_MPSAFE +#define SPPPSUBR_MPSAFE 1 +#endif + #define LCP_KEEPALIVE_INTERVAL 10 /* seconds between checks */ #define LOOPALIVECNT 3 /* loopback detection tries */ #define DEFAULT_MAXALIVECNT 3 /* max. missed alive packets */ @@ -242,9 +246,16 @@ struct cp { }; static struct sppp *spppq; +static kmutex_t *spppq_lock = NULL; static callout_t keepalive_ch; +#define SPPPQ_LOCK() if (spppq_lock) \ + mutex_enter(spppq_lock); +#define SPPPQ_UNLOCK() if (spppq_lock) \ + mutex_exit(spppq_lock); + #ifdef INET +#ifndef SPPPSUBR_MPSAFE /* * The following disgusting hack gets around the problem that IP TOS * can't be set yet. We want to put "interactive" traffic on a high @@ -258,6 +269,7 @@ static u_short interactive_ports[8] = { 0, 21, 0, 23, }; #define INTERACTIVE(p) (interactive_ports[(p) & 7] == (p)) +#endif /* SPPPSUBR_MPSAFE */ #endif /* almost every function needs these */ @@ -457,6 +469,8 @@ sppp_change_phase(struct sppp *sp, int p { STDDCL; + KASSERT(sppp_locked(sp)); + if (sp->pp_phase == phase) return; @@ -492,6 +506,10 @@ sppp_input(struct ifnet *ifp, struct mbu int debug = ifp->if_flags & IFF_DEBUG; int isr = 0; + KASSERT(!sppp_locked(sp)); + + sppp_lock_enter(sp); + if (ifp->if_flags & IFF_UP) { /* Count received bytes, add hardware framing */ ifp->if_ibytes += m->m_pkthdr.len + sp->pp_framebytes; @@ -509,6 +527,7 @@ sppp_input(struct ifnet *ifp, struct mbu ++ifp->if_ierrors; ++ifp->if_iqdrops; m_freem(m); + sppp_lock_exit(sp); return; } @@ -553,6 +572,7 @@ sppp_input(struct ifnet *ifp, struct mbu ++ifp->if_noproto; goto invalid; case CISCO_KEEPALIVE: + sppp_lock_exit(sp); sppp_cisco_input((struct sppp *) ifp, m); m_freem(m); return; @@ -596,23 +616,33 @@ sppp_input(struct ifnet *ifp, struct mbu ++ifp->if_noproto; goto drop; case PPP_LCP: + sppp_lock_exit(sp); sppp_cp_input(&lcp, sp, m); m_freem(m); return; case PPP_PAP: - if (sp->pp_phase >= SPPP_PHASE_AUTHENTICATE) + if (sp->pp_phase >= SPPP_PHASE_AUTHENTICATE) { + sppp_lock_exit(sp); sppp_pap_input(sp, m); + } else + sppp_lock_exit(sp); m_freem(m); return; case PPP_CHAP: - if (sp->pp_phase >= SPPP_PHASE_AUTHENTICATE) + if (sp->pp_phase >= SPPP_PHASE_AUTHENTICATE) { + sppp_lock_exit(sp); sppp_chap_input(sp, m); + } else + sppp_lock_exit(sp); m_freem(m); return; #ifdef INET case PPP_IPCP: - if (sp->pp_phase == SPPP_PHASE_NETWORK) + if (sp->pp_phase == SPPP_PHASE_NETWORK) { + sppp_lock_exit(sp); sppp_cp_input(&ipcp, sp, m); + } else + sppp_lock_exit(sp); m_freem(m); return; case PPP_IP: @@ -624,8 +654,11 @@ sppp_input(struct ifnet *ifp, struct mbu #endif #ifdef INET6 case PPP_IPV6CP: - if (sp->pp_phase == SPPP_PHASE_NETWORK) + if (sp->pp_phase == SPPP_PHASE_NETWORK) { + sppp_lock_exit(sp); sppp_cp_input(&ipv6cp, sp, m); + } else + sppp_lock_exit(sp); m_freem(m); return; @@ -643,9 +676,11 @@ queue_pkt: goto drop; } + sppp_lock_exit(sp); /* Check queue. */ if (__predict_true(pktq)) { if (__predict_false(!pktq_enqueue(pktq, m, 0))) { + sppp_lock_enter(sp); goto drop; } return; @@ -659,6 +694,7 @@ queue_pkt: if (debug) log(LOG_DEBUG, "%s: protocol queue overflow\n", ifp->if_xname); + sppp_lock_enter(sp); goto drop; } IF_ENQUEUE(inq, m); @@ -675,16 +711,20 @@ sppp_output(struct ifnet *ifp, struct mb { struct sppp *sp = (struct sppp *) ifp; struct ppp_header *h = NULL; +#ifndef SPPPSUBR_MPSAFE struct ifqueue *ifq = NULL; /* XXX */ +#endif int s, error = 0; uint16_t protocol; s = splnet(); + sppp_lock_enter(sp); sp->pp_last_activity = time_uptime; if ((ifp->if_flags & IFF_UP) == 0 || (ifp->if_flags & (IFF_RUNNING | IFF_AUTO)) == 0) { + sppp_lock_exit(sp); m_freem(m); splx(s); return (ENETDOWN); @@ -710,16 +750,20 @@ sppp_output(struct ifnet *ifp, struct mb #ifdef INET if (dst->sa_family == AF_INET) { struct ip *ip = NULL; +#ifndef SPPPSUBR_MPSAFE struct tcphdr *th = NULL; +#endif if (m->m_len >= sizeof(struct ip)) { ip = mtod(m, struct ip *); +#ifndef SPPPSUBR_MPSAFE if (ip->ip_p == IPPROTO_TCP && m->m_len >= sizeof(struct ip) + (ip->ip_hl << 2) + sizeof(struct tcphdr)) { th = (struct tcphdr *) ((char *)ip + (ip->ip_hl << 2)); } +#endif } else ip = NULL; @@ -736,6 +780,7 @@ sppp_output(struct ifnet *ifp, struct mb if (ip && ip->ip_src.s_addr == INADDR_ANY) { uint8_t proto = ip->ip_p; + sppp_lock_exit(sp); m_freem(m); splx(s); if (proto == IPPROTO_TCP) @@ -744,16 +789,17 @@ sppp_output(struct ifnet *ifp, struct mb return (0); } +#ifndef SPPPSUBR_MPSAFE /* * Put low delay, telnet, rlogin and ftp control packets * in front of the queue. */ - if (!IF_QFULL(&sp->pp_fastq) && ((ip && (ip->ip_tos & IPTOS_LOWDELAY)) || (th && (INTERACTIVE(ntohs(th->th_sport)) || INTERACTIVE(ntohs(th->th_dport)))))) ifq = &sp->pp_fastq; +#endif /* !SPPPSUBR_MPSAFE */ } #endif @@ -773,6 +819,7 @@ sppp_output(struct ifnet *ifp, struct mb log(LOG_DEBUG, "%s: no memory for transmit header\n", ifp->if_xname); ++ifp->if_oerrors; + sppp_lock_exit(sp); splx(s); return (ENOBUFS); } @@ -834,6 +881,7 @@ sppp_output(struct ifnet *ifp, struct mb default: m_freem(m); ++ifp->if_oerrors; + sppp_lock_exit(sp); splx(s); return (EAFNOSUPPORT); } @@ -845,6 +893,7 @@ sppp_output(struct ifnet *ifp, struct mb log(LOG_DEBUG, "%s: no memory for transmit header\n", ifp->if_xname); ++ifp->if_oerrors; + sppp_lock_exit(sp); splx(s); return (ENOBUFS); } @@ -854,6 +903,11 @@ sppp_output(struct ifnet *ifp, struct mb } +#ifdef SPPPSUBR_MPSAFE + error = if_transmit_lock(ifp, m); + if (error == 0) + ifp->if_obytes += m->m_pkthdr.len + sp->pp_framebytes; +#else /* !SPPPSUBR_MPSAFE */ error = ifq_enqueue2(ifp, ifq, m); if (error == 0) { @@ -862,10 +916,15 @@ sppp_output(struct ifnet *ifp, struct mb * The packet length includes header + additional hardware * framing according to RFC 1333. */ - if (!(ifp->if_flags & IFF_OACTIVE)) + if (!(ifp->if_flags & IFF_OACTIVE)) { + sppp_lock_exit(sp); if_start_lock(ifp); + sppp_lock_enter(sp); + } ifp->if_obytes += m->m_pkthdr.len + sp->pp_framebytes; } +#endif /* !SPPPSUBR_MPSAFE */ + sppp_lock_exit(sp); splx(s); return error; } @@ -880,7 +939,10 @@ sppp_mediachange(struct ifnet *ifp) static void sppp_mediastatus(struct ifnet *ifp, struct ifmediareq *imr) { + struct sppp *sp = (struct sppp *)ifp; + KASSERT(!sppp_locked(sp)); + sppp_lock_enter(sp); switch (ifp->if_link_state) { case LINK_STATE_UP: imr->ifm_status = IFM_AVALID | IFM_ACTIVE; @@ -893,6 +955,7 @@ sppp_mediastatus(struct ifnet *ifp, stru imr->ifm_status = 0; break; } + sppp_lock_exit(sp); } void @@ -906,6 +969,9 @@ sppp_attach(struct ifnet *ifp) callout_reset(&keepalive_ch, hz * LCP_KEEPALIVE_INTERVAL, sppp_keepalive, NULL); } + if (! spppq_lock) + spppq_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_SOFTNET); + /* Insert new entry into the keepalive list. */ sp->pp_next = spppq; spppq = sp; @@ -928,6 +994,7 @@ sppp_attach(struct ifnet *ifp) sp->pp_phase = SPPP_PHASE_DEAD; sp->pp_up = lcp.Up; sp->pp_down = lcp.Down; + sp->pp_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_SOFTNET); if_alloc_sadl(ifp); @@ -950,7 +1017,9 @@ sppp_detach(struct ifnet *ifp) { struct sppp **q, *p, *sp = (struct sppp *) ifp; + sppp_lock_enter(sp); /* Remove the entry from the keepalive list. */ + SPPPQ_LOCK(); for (q = &spppq; (p = *q); q = &p->pp_next) if (p == sp) { *q = p->pp_next; @@ -966,6 +1035,7 @@ sppp_detach(struct ifnet *ifp) if (! spppq) { callout_stop(&keepalive_ch); } + SPPPQ_UNLOCK(); callout_stop(&sp->ch[IDX_LCP]); callout_stop(&sp->ch[IDX_IPCP]); @@ -984,6 +1054,11 @@ sppp_detach(struct ifnet *ifp) /* Safety - shouldn't be needed as there is no media to set. */ ifmedia_delete_instance(&sp->pp_im, IFM_INST_ANY); + + sppp_lock_exit(sp); + if (sp->pp_lock) + mutex_obj_free(sp->pp_lock); + } /* @@ -1008,9 +1083,13 @@ sppp_isempty(struct ifnet *ifp) struct sppp *sp = (struct sppp *) ifp; int empty, s; + KASSERT(!sppp_locked(sp)); + s = splnet(); + sppp_lock_enter(sp); empty = IF_IS_EMPTY(&sp->pp_fastq) && IF_IS_EMPTY(&sp->pp_cpq) && IFQ_IS_EMPTY(&sp->pp_if.if_snd); + sppp_lock_exit(sp); splx(s); return (empty); } @@ -1026,6 +1105,7 @@ sppp_dequeue(struct ifnet *ifp) int s; s = splnet(); + sppp_lock_enter(sp); /* * Process only the control protocol queue until we have at * least one NCP open. @@ -1039,6 +1119,7 @@ sppp_dequeue(struct ifnet *ifp) if (m == NULL) IFQ_DEQUEUE(&sp->pp_if.if_snd, m); } + sppp_lock_exit(sp); splx(s); return m; } @@ -1064,6 +1145,8 @@ sppp_ioctl(struct ifnet *ifp, u_long cmd case SIOCSIFFLAGS: if ((error = ifioctl_common(ifp, cmd, data)) != 0) break; + + sppp_lock_enter(sp); going_up = ifp->if_flags & IFF_UP && (ifp->if_flags & IFF_RUNNING) == 0; going_down = (ifp->if_flags & IFF_UP) == 0 && @@ -1075,8 +1158,9 @@ sppp_ioctl(struct ifnet *ifp, u_long cmd ifp->if_flags &= ~IFF_AUTO; } - if (going_up || going_down) + if (going_up || going_down) { lcp.Close(sp); + } if (going_up && newmode == 0) { /* neither auto-dial nor passive */ ifp->if_flags |= IFF_RUNNING; @@ -1086,7 +1170,7 @@ sppp_ioctl(struct ifnet *ifp, u_long cmd sppp_flush(ifp); ifp->if_flags &= ~IFF_RUNNING; } - + sppp_lock_exit(sp); break; case SIOCSIFMTU: @@ -1177,11 +1261,16 @@ sppp_cisco_input(struct sppp *sp, struct uint32_t me, mymask = 0; /* XXX: GCC */ #endif + KASSERT(!sppp_locked(sp)); + + sppp_lock_enter(sp); + if (m->m_pkthdr.len < CISCO_PACKET_LEN) { if (debug) log(LOG_DEBUG, "%s: cisco invalid packet length: %d bytes\n", ifp->if_xname, m->m_pkthdr.len); + sppp_lock_exit(sp); return; } h = mtod(m, struct cisco_packet *); @@ -1212,7 +1301,9 @@ sppp_cisco_input(struct sppp *sp, struct ifp->if_xname); sp->pp_loopcnt = 0; if (ifp->if_flags & IFF_UP) { + sppp_lock_exit(sp); if_down(ifp); + sppp_lock_enter(sp); IF_PURGE(&sp->pp_cpq); } } @@ -1225,17 +1316,22 @@ sppp_cisco_input(struct sppp *sp, struct sp->pp_loopcnt = 0; if (! (ifp->if_flags & IFF_UP) && (ifp->if_flags & IFF_RUNNING)) { + sppp_lock_exit(sp); if_up(ifp); + sppp_lock_enter(sp); } break; case CISCO_ADDR_REQ: #ifdef INET + sppp_lock_exit(sp); sppp_get_ip_addrs(sp, &me, 0, &mymask); if (me != 0L) sppp_cisco_send(sp, CISCO_ADDR_REPLY, me, mymask); #endif + sppp_lock_enter(sp); break; } + sppp_lock_exit(sp); } /* @@ -1250,6 +1346,8 @@ sppp_cisco_send(struct sppp *sp, int typ struct mbuf *m; uint32_t t; + KASSERT(sppp_locked(sp)); + t = time_uptime * 1000; MGETHDR(m, M_DONTWAIT, MT_DATA); if (! m) @@ -1286,8 +1384,11 @@ sppp_cisco_send(struct sppp *sp, int typ return; } else IF_ENQUEUE(&sp->pp_cpq, m); - if (! (ifp->if_flags & IFF_OACTIVE)) + if (! (ifp->if_flags & IFF_OACTIVE)) { + sppp_lock_exit(sp); if_start_lock(ifp); + sppp_lock_enter(sp); + } ifp->if_obytes += m->m_pkthdr.len + sp->pp_framebytes; } @@ -1307,6 +1408,8 @@ sppp_cp_send(struct sppp *sp, u_short pr struct mbuf *m; size_t pkthdrlen; + KASSERT(sppp_locked(sp)); + pkthdrlen = (sp->pp_flags & PP_NOFRAMING) ? 2 : PPP_HEADER_LEN; if (len > MHLEN - pkthdrlen - LCP_HEADER_LEN) @@ -1351,8 +1454,13 @@ sppp_cp_send(struct sppp *sp, u_short pr return; } else IF_ENQUEUE(&sp->pp_cpq, m); - if (! (ifp->if_flags & IFF_OACTIVE)) + + if (! (ifp->if_flags & IFF_OACTIVE)) { + sppp_lock_exit(sp); if_start_lock(ifp); + sppp_lock_enter(sp); + } + ifp->if_obytes += m->m_pkthdr.len + sp->pp_framebytes; } @@ -1369,6 +1477,7 @@ sppp_cp_input(const struct cp *cp, struc u_char *p; uint32_t u32; + KASSERT(!sppp_locked(sp)); if (len < 4) { if (debug) log(LOG_DEBUG, @@ -1400,17 +1509,22 @@ sppp_cp_input(const struct cp *cp, struc addlog("%s: %s invalid conf-req length %d\n", ifp->if_xname, cp->name, len); + sppp_lock_enter(sp); ++ifp->if_ierrors; + sppp_lock_exit(sp); break; } /* handle states where RCR doesn't get a SCA/SCN */ + sppp_lock_enter(sp); switch (sp->state[cp->protoidx]) { case STATE_CLOSING: case STATE_STOPPING: + sppp_lock_exit(sp); return; case STATE_CLOSED: sppp_cp_send(sp, cp->proto, TERM_ACK, h->ident, 0, 0); + sppp_lock_exit(sp); return; } rv = (cp->RCR)(sp, h, len); @@ -1418,6 +1532,7 @@ sppp_cp_input(const struct cp *cp, struc /* fatal error, shut down */ (cp->tld)(sp); sppp_lcp_tlf(sp); + sppp_lock_exit(sp); return; } switch (sp->state[cp->protoidx]) { @@ -1448,20 +1563,24 @@ sppp_cp_input(const struct cp *cp, struc sppp_cp_change_state(cp, sp, STATE_ACK_RCVD); break; default: + sppp_lock_enter(sp); printf("%s: %s illegal %s in state %s\n", ifp->if_xname, cp->name, sppp_cp_type_name(h->type), sppp_state_name(sp->state[cp->protoidx])); ++ifp->if_ierrors; } + sppp_lock_exit(sp); break; case CONF_ACK: + sppp_lock_enter(sp); if (h->ident != sp->confid[cp->protoidx]) { if (debug) addlog("%s: %s id mismatch 0x%x != 0x%x\n", ifp->if_xname, cp->name, h->ident, sp->confid[cp->protoidx]); ++ifp->if_ierrors; + sppp_lock_exit(sp); break; } switch (sp->state[cp->protoidx]) { @@ -1498,15 +1617,18 @@ sppp_cp_input(const struct cp *cp, struc sppp_state_name(sp->state[cp->protoidx])); ++ifp->if_ierrors; } + sppp_lock_exit(sp); break; case CONF_NAK: case CONF_REJ: + sppp_lock_enter(sp); if (h->ident != sp->confid[cp->protoidx]) { if (debug) addlog("%s: %s id mismatch 0x%x != 0x%x\n", ifp->if_xname, cp->name, h->ident, sp->confid[cp->protoidx]); ++ifp->if_ierrors; + sppp_lock_exit(sp); break; } if (h->type == CONF_NAK) @@ -1541,9 +1663,11 @@ sppp_cp_input(const struct cp *cp, struc sppp_state_name(sp->state[cp->protoidx])); ++ifp->if_ierrors; } + sppp_lock_exit(sp); break; case TERM_REQ: + sppp_lock_enter(sp); switch (sp->state[cp->protoidx]) { case STATE_ACK_RCVD: case STATE_ACK_SENT: @@ -1573,8 +1697,10 @@ sppp_cp_input(const struct cp *cp, struc sppp_state_name(sp->state[cp->protoidx])); ++ifp->if_ierrors; } + sppp_lock_exit(sp); break; case TERM_ACK: + sppp_lock_enter(sp); switch (sp->state[cp->protoidx]) { case STATE_CLOSED: case STATE_STOPPED: @@ -1606,9 +1732,11 @@ sppp_cp_input(const struct cp *cp, struc sppp_state_name(sp->state[cp->protoidx])); ++ifp->if_ierrors; } + sppp_lock_exit(sp); break; case CODE_REJ: /* XXX catastrophic rejects (RXJ-) aren't handled yet. */ + sppp_lock_enter(sp); log(LOG_INFO, "%s: %s: ignoring RXJ (%s) for code ?, " "danger will robinson\n", @@ -1633,6 +1761,7 @@ sppp_cp_input(const struct cp *cp, struc sppp_state_name(sp->state[cp->protoidx])); ++ifp->if_ierrors; } + sppp_lock_exit(sp); break; case PROTO_REJ: { @@ -1653,6 +1782,8 @@ sppp_cp_input(const struct cp *cp, struc if (upper == NULL) catastrophic++; + sppp_lock_enter(sp); + if (debug) log(LOG_INFO, "%s: %s: RXJ%c (%s) for proto 0x%x (%s/%s)\n", @@ -1668,6 +1799,7 @@ sppp_cp_input(const struct cp *cp, struc if (upper && !catastrophic) { if (sp->state[upper->protoidx] == STATE_REQ_SENT) { upper->Close(sp); + sppp_lock_exit(sp); break; } } @@ -1692,6 +1824,7 @@ sppp_cp_input(const struct cp *cp, struc sppp_state_name(sp->state[cp->protoidx])); ++ifp->if_ierrors; } + sppp_lock_exit(sp); break; } case DISC_REQ: @@ -1702,11 +1835,13 @@ sppp_cp_input(const struct cp *cp, struc case ECHO_REQ: if (cp->proto != PPP_LCP) goto illegal; + sppp_lock_enter(sp); if (sp->state[cp->protoidx] != STATE_OPENED) { if (debug) addlog("%s: lcp echo req but lcp closed\n", ifp->if_xname); ++ifp->if_ierrors; + sppp_lock_exit(sp); break; } if (len < 8) { @@ -1714,6 +1849,7 @@ sppp_cp_input(const struct cp *cp, struc addlog("%s: invalid lcp echo request " "packet length: %d bytes\n", ifp->if_xname, len); + sppp_lock_exit(sp); break; } memcpy(&u32, h + 1, sizeof u32); @@ -1727,6 +1863,7 @@ sppp_cp_input(const struct cp *cp, struc /* XXX */ lcp.Down(sp); lcp.Up(sp); + sppp_lock_exit(sp); break; } u32 = htonl(sp->lcp.magic); @@ -1736,12 +1873,15 @@ sppp_cp_input(const struct cp *cp, struc ifp->if_xname); sppp_cp_send(sp, PPP_LCP, ECHO_REPLY, h->ident, len - 4, h + 1); + sppp_lock_exit(sp); break; case ECHO_REPLY: if (cp->proto != PPP_LCP) goto illegal; + sppp_lock_enter(sp); if (h->ident != sp->lcp.echoid) { ++ifp->if_ierrors; + sppp_lock_exit(sp); break; } if (len < 8) { @@ -1749,6 +1889,7 @@ sppp_cp_input(const struct cp *cp, struc addlog("%s: lcp invalid echo reply " "packet length: %d bytes\n", ifp->if_xname, len); + sppp_lock_exit(sp); break; } if (debug) @@ -1757,16 +1898,19 @@ sppp_cp_input(const struct cp *cp, struc memcpy(&u32, h + 1, sizeof u32); if (ntohl(u32) != sp->lcp.magic) sp->pp_alivecnt = 0; + sppp_lock_exit(sp); break; default: /* Unknown packet type -- send Code-Reject packet. */ illegal: + sppp_lock_enter(sp); if (debug) addlog("%s: %s send code-rej for 0x%x\n", ifp->if_xname, cp->name, h->type); sppp_cp_send(sp, cp->proto, CODE_REJ, ++sp->pp_seq[cp->protoidx], m->m_pkthdr.len, h); ++ifp->if_ierrors; + sppp_lock_exit(sp); } } @@ -1780,6 +1924,8 @@ sppp_up_event(const struct cp *cp, struc { STDDCL; + KASSERT(sppp_locked(sp)); + if (debug) log(LOG_DEBUG, "%s: %s up(%s)\n", ifp->if_xname, cp->name, @@ -1806,6 +1952,8 @@ sppp_down_event(const struct cp *cp, str { STDDCL; + KASSERT(sppp_locked(sp)); + if (debug) log(LOG_DEBUG, "%s: %s down(%s)\n", ifp->if_xname, cp->name, @@ -1842,6 +1990,8 @@ sppp_open_event(const struct cp *cp, str { STDDCL; + KASSERT(sppp_locked(sp)); + if (debug) log(LOG_DEBUG, "%s: %s open(%s)\n", ifp->if_xname, cp->name, @@ -1878,6 +2028,8 @@ sppp_close_event(const struct cp *cp, st { STDDCL; + KASSERT(sppp_locked(sp)); + if (debug) log(LOG_DEBUG, "%s: %s close(%s)\n", ifp->if_xname, cp->name, @@ -1918,7 +2070,10 @@ sppp_to_event(const struct cp *cp, struc STDDCL; int s; + KASSERT(sppp_locked(sp)); + s = splnet(); + if (debug) log(LOG_DEBUG, "%s: %s TO(%s) rst_counter = %d\n", ifp->if_xname, cp->name, @@ -1979,6 +2134,8 @@ sppp_to_event(const struct cp *cp, struc void sppp_cp_change_state(const struct cp *cp, struct sppp *sp, int newstate) { + KASSERT(sppp_locked(sp)); + sp->state[cp->protoidx] = newstate; callout_stop(&sp->ch[cp->protoidx]); switch (newstate) { @@ -2009,6 +2166,7 @@ sppp_cp_change_state(const struct cp *cp static void sppp_lcp_init(struct sppp *sp) { + sppp_lock_enter(sp); sp->lcp.opts = (1 << LCP_OPT_MAGIC); sp->lcp.magic = 0; sp->state[IDX_LCP] = STATE_INITIAL; @@ -2029,6 +2187,7 @@ sppp_lcp_init(struct sppp *sp) sp->lcp.max_configure = 10; sp->lcp.max_failure = 10; callout_init(&sp->ch[IDX_LCP], 0); + sppp_lock_exit(sp); } static void @@ -2036,6 +2195,8 @@ sppp_lcp_up(struct sppp *sp) { STDDCL; + KASSERT(sppp_locked(sp)); + /* Initialize activity timestamp: opening a connection is an activity */ sp->pp_last_receive = sp->pp_last_activity = time_uptime; @@ -2070,6 +2231,8 @@ sppp_lcp_down(struct sppp *sp) { STDDCL; + KASSERT(sppp_locked(sp)); + sppp_down_event(&lcp, sp); /* @@ -2084,7 +2247,9 @@ sppp_lcp_down(struct sppp *sp) log(LOG_INFO, "%s: Down event (carrier loss), taking interface down.\n", ifp->if_xname); + sppp_lock_exit(sp); if_down(ifp); + sppp_lock_enter(sp); } else { if (debug) log(LOG_DEBUG, @@ -2100,6 +2265,8 @@ sppp_lcp_down(struct sppp *sp) static void sppp_lcp_open(struct sppp *sp) { + KASSERT(sppp_locked(sp)); + if (sp->pp_if.if_mtu < PP_MTU) { sp->lcp.mru = sp->pp_if.if_mtu; sp->lcp.opts |= (1 << LCP_OPT_MRU); @@ -2121,13 +2288,19 @@ sppp_lcp_open(struct sppp *sp) static void sppp_lcp_close(struct sppp *sp) { + KASSERT(sppp_locked(sp)); sppp_close_event(&lcp, sp); } static void sppp_lcp_TO(void *cookie) { - sppp_to_event(&lcp, (struct sppp *)cookie); + struct sppp *sp = (struct sppp*)cookie; + KASSERT(!sppp_locked(sp)); + + sppp_lock_enter(sp); + sppp_to_event(&lcp, sp); + sppp_lock_exit(sp); } /* @@ -2145,6 +2318,8 @@ sppp_lcp_RCR(struct sppp *sp, struct lcp uint32_t nmagic; u_short authproto; + KASSERT(sppp_locked(sp)); + len -= 4; origlen = len; buf = r = malloc (blen = len, M_TEMP, M_NOWAIT); @@ -2269,7 +2444,9 @@ sppp_lcp_RCR(struct sppp *sp, struct lcp ifp->if_xname); sp->pp_loopcnt = 0; if (ifp->if_flags & IFF_UP) { + sppp_lock_exit(sp); if_down(ifp); + sppp_lock_enter(sp); IF_PURGE(&sp->pp_cpq); /* XXX ? */ lcp.Down(sp); @@ -2388,6 +2565,8 @@ sppp_lcp_RCN_rej(struct sppp *sp, struct STDDCL; u_char *buf, *p, l; + KASSERT(sppp_locked(sp)); + len -= 4; buf = malloc (len, M_TEMP, M_NOWAIT); if (!buf) @@ -2470,6 +2649,8 @@ sppp_lcp_RCN_nak(struct sppp *sp, struct u_char *buf, *p, l, blen; uint32_t magic; + KASSERT(sppp_locked(sp)); + len -= 4; buf = malloc (blen = len, M_TEMP, M_NOWAIT); if (!buf) @@ -2557,11 +2738,15 @@ sppp_lcp_tlu(struct sppp *sp) int i; uint32_t mask; + KASSERT(sppp_locked(sp)); + /* XXX ? */ if (! (ifp->if_flags & IFF_UP) && (ifp->if_flags & IFF_RUNNING)) { /* Coming out of loopback mode. */ + sppp_lock_exit(sp); if_up(ifp); + sppp_lock_enter(sp); } for (i = 0; i < IDX_COUNT; i++) @@ -2593,9 +2778,11 @@ sppp_lcp_tlu(struct sppp *sp) } /* Send Up events to all started protos. */ - for (i = 0, mask = 1; i < IDX_COUNT; i++, mask <<= 1) - if ((sp->lcp.protos & mask) && ((cps[i])->flags & CP_LCP) == 0) + for (i = 0, mask = 1; i < IDX_COUNT; i++, mask <<= 1) { + if ((sp->lcp.protos & mask) && ((cps[i])->flags & CP_LCP) == 0) { (cps[i])->Up(sp); + } + } /* notify low-level driver of state change */ if (sp->pp_chg) @@ -2612,6 +2799,8 @@ sppp_lcp_tld(struct sppp *sp) int i; uint32_t mask; + KASSERT(sppp_locked(sp)); + sppp_change_phase(sp, SPPP_PHASE_TERMINATE); /* @@ -2620,22 +2809,27 @@ sppp_lcp_tld(struct sppp *sp) * ``a flurry of terminate-request packets'', as the RFC * describes it. */ - for (i = 0, mask = 1; i < IDX_COUNT; i++, mask <<= 1) + for (i = 0, mask = 1; i < IDX_COUNT; i++, mask <<= 1) { if ((sp->lcp.protos & mask) && ((cps[i])->flags & CP_LCP) == 0) { (cps[i])->Down(sp); (cps[i])->Close(sp); } + } } static void sppp_lcp_tls(struct sppp *sp) { + KASSERT(sppp_locked(sp)); + if (sp->pp_max_auth_fail != 0 && sp->pp_auth_failures >= sp->pp_max_auth_fail) { - printf("%s: authentication failed %d times, not retrying again\n", + printf("%s: authentication failed %d times, not retrying again\n", sp->pp_if.if_xname, sp->pp_auth_failures); - if_down(&sp->pp_if); - return; + sppp_lock_exit(sp); + if_down(&sp->pp_if); + sppp_lock_enter(sp); + return; } sppp_change_phase(sp, SPPP_PHASE_ESTABLISH); @@ -2648,6 +2842,7 @@ sppp_lcp_tls(struct sppp *sp) static void sppp_lcp_tlf(struct sppp *sp) { + KASSERT(sppp_locked(sp)); sppp_change_phase(sp, SPPP_PHASE_DEAD); @@ -2663,6 +2858,8 @@ sppp_lcp_scr(struct sppp *sp) int i = 0; u_short authproto; + KASSERT(sppp_locked(sp)); + if (sp->lcp.opts & (1 << LCP_OPT_MAGIC)) { if (! sp->lcp.magic) sp->lcp.magic = cprng_fast32(); @@ -2716,6 +2913,7 @@ sppp_ncp_check(struct sppp *sp) static void sppp_lcp_check_and_close(struct sppp *sp) { + KASSERT(sppp_locked(sp)); if (sp->pp_phase < SPPP_PHASE_NETWORK) /* don't bother, we are already going down */ @@ -2723,7 +2921,6 @@ sppp_lcp_check_and_close(struct sppp *sp if (sppp_ncp_check(sp)) return; - lcp.Close(sp); } @@ -2741,6 +2938,7 @@ sppp_ipcp_init(struct sppp *sp) { int error; + sppp_lock_enter(sp); sp->ipcp.opts = 0; sp->ipcp.flags = 0; sp->state[IDX_IPCP] = STATE_INITIAL; @@ -2757,17 +2955,20 @@ sppp_ipcp_init(struct sppp *sp) sp->ipcp.update_addrs_q = pcq_create(IPCP_UPDATE_LIMIT, KM_SLEEP); sp->ipcp.update_addrs_enqueued = 0; + sppp_lock_exit(sp); } static void sppp_ipcp_up(struct sppp *sp) { + KASSERT(sppp_locked(sp)); sppp_up_event(&ipcp, sp); } static void sppp_ipcp_down(struct sppp *sp) { + KASSERT(sppp_locked(sp)); sppp_down_event(&ipcp, sp); } @@ -2777,6 +2978,8 @@ sppp_ipcp_open(struct sppp *sp) STDDCL; uint32_t myaddr, hisaddr; + KASSERT(sppp_locked(sp)); + sp->ipcp.flags &= ~(IPCP_HISADDR_SEEN|IPCP_MYADDR_SEEN|IPCP_MYADDR_DYN|IPCP_HISADDR_DYN); sp->ipcp.req_myaddr = 0; sp->ipcp.req_hisaddr = 0; @@ -2823,7 +3026,9 @@ static void sppp_ipcp_close(struct sppp *sp) { + KASSERT(sppp_locked(sp)); sppp_close_event(&ipcp, sp); + #ifdef INET if (sp->ipcp.flags & (IPCP_MYADDR_DYN|IPCP_HISADDR_DYN)) /* @@ -2836,7 +3041,13 @@ sppp_ipcp_close(struct sppp *sp) static void sppp_ipcp_TO(void *cookie) { + struct sppp *sp = cookie; + + KASSERT(!sppp_locked(sp)); + + sppp_lock_enter(sp); sppp_to_event(&ipcp, (struct sppp *)cookie); + sppp_lock_exit(sp); } /* @@ -2853,6 +3064,7 @@ sppp_ipcp_RCR(struct sppp *sp, struct lc int rlen, origlen, debug = ifp->if_flags & IFF_DEBUG; uint32_t hisaddr, desiredaddr; + KASSERT(sppp_locked(sp)); len -= 4; origlen = len; /* @@ -3047,6 +3259,8 @@ sppp_ipcp_RCN_rej(struct sppp *sp, struc struct ifnet *ifp = &sp->pp_if; int debug = ifp->if_flags & IFF_DEBUG; + KASSERT(sppp_locked(sp)); + len -= 4; buf = malloc (blen = len, M_TEMP, M_NOWAIT); if (!buf) @@ -3101,6 +3315,8 @@ sppp_ipcp_RCN_nak(struct sppp *sp, struc int debug = ifp->if_flags & IFF_DEBUG; uint32_t wantaddr; + KASSERT(sppp_locked(sp)); + len -= 4; if (debug) @@ -3177,6 +3393,7 @@ static void sppp_ipcp_tlu(struct sppp *sp) { #ifdef INET + KASSERT(sppp_locked(sp)); /* we are up. Set addresses and notify anyone interested */ sppp_set_ip_addrs(sp); #endif @@ -3190,6 +3407,7 @@ sppp_ipcp_tld(struct sppp *sp) static void sppp_ipcp_tls(struct sppp *sp) { + KASSERT(sppp_locked(sp)); /* indicate to LCP that it must stay alive */ sp->lcp.protos |= (1 << IDX_IPCP); } @@ -3197,6 +3415,7 @@ sppp_ipcp_tls(struct sppp *sp) static void sppp_ipcp_tlf(struct sppp *sp) { + KASSERT(sppp_locked(sp)); /* we no longer need LCP */ sp->lcp.protos &= ~(1 << IDX_IPCP); } @@ -3210,6 +3429,8 @@ sppp_ipcp_scr(struct sppp *sp) #endif int i = 0; + KASSERT(sppp_locked(sp)); + #ifdef notyet if (sp->ipcp.opts & (1 << IPCP_OPT_COMPRESSION)) { opt[i++] = IPCP_OPT_COMPRESSION; @@ -3270,6 +3491,7 @@ sppp_ipcp_scr(struct sppp *sp) static void sppp_ipv6cp_init(struct sppp *sp) { + sppp_lock_enter(sp); sp->ipv6cp.opts = 0; sp->ipv6cp.flags = 0; sp->state[IDX_IPV6CP] = STATE_INITIAL; @@ -3277,17 +3499,20 @@ sppp_ipv6cp_init(struct sppp *sp) sp->pp_seq[IDX_IPV6CP] = 0; sp->pp_rseq[IDX_IPV6CP] = 0; callout_init(&sp->ch[IDX_IPV6CP], 0); + sppp_lock_exit(sp); } static void sppp_ipv6cp_up(struct sppp *sp) { + KASSERT(sppp_locked(sp)); sppp_up_event(&ipv6cp, sp); } static void sppp_ipv6cp_down(struct sppp *sp) { + KASSERT(sppp_locked(sp)); sppp_down_event(&ipv6cp, sp); } @@ -3297,6 +3522,8 @@ sppp_ipv6cp_open(struct sppp *sp) STDDCL; struct in6_addr myaddr, hisaddr; + KASSERT(sppp_locked(sp)); + #ifdef IPV6CP_MYIFID_DYN sp->ipv6cp.flags &= ~(IPV6CP_MYIFID_SEEN|IPV6CP_MYIFID_DYN); #else @@ -3326,13 +3553,19 @@ sppp_ipv6cp_open(struct sppp *sp) static void sppp_ipv6cp_close(struct sppp *sp) { + KASSERT(sppp_locked(sp)); sppp_close_event(&ipv6cp, sp); } static void sppp_ipv6cp_TO(void *cookie) { + struct sppp *sp = cookie; + KASSERT(!sppp_locked(sp)); + + sppp_lock_enter(sp); sppp_to_event(&ipv6cp, (struct sppp *)cookie); + sppp_lock_exit(sp); } /* @@ -3352,6 +3585,8 @@ sppp_ipv6cp_RCR(struct sppp *sp, struct int type; int collision, nohisaddr; + KASSERT(sppp_locked(sp)); + len -= 4; origlen = len; /* @@ -3529,6 +3764,8 @@ sppp_ipv6cp_RCN_rej(struct sppp *sp, str struct ifnet *ifp = &sp->pp_if; int debug = ifp->if_flags & IFF_DEBUG; + KASSERT(sppp_locked(sp)); + len -= 4; buf = malloc (blen = len, M_TEMP, M_NOWAIT); if (!buf) @@ -3582,6 +3819,8 @@ sppp_ipv6cp_RCN_nak(struct sppp *sp, str int debug = ifp->if_flags & IFF_DEBUG; struct in6_addr suggestaddr; + KASSERT(sppp_locked(sp)); + len -= 4; buf = malloc (blen = len, M_TEMP, M_NOWAIT); if (!buf) @@ -3677,6 +3916,7 @@ drop: static void sppp_ipv6cp_tlu(struct sppp *sp) { + KASSERT(sppp_locked(sp)); /* we are up - notify isdn daemon */ if (sp->pp_con) sp->pp_con(sp); @@ -3690,6 +3930,7 @@ sppp_ipv6cp_tld(struct sppp *sp) static void sppp_ipv6cp_tls(struct sppp *sp) { + KASSERT(sppp_locked(sp)); /* indicate to LCP that it must stay alive */ sp->lcp.protos |= (1 << IDX_IPV6CP); } @@ -3697,6 +3938,7 @@ sppp_ipv6cp_tls(struct sppp *sp) static void sppp_ipv6cp_tlf(struct sppp *sp) { + KASSERT(sppp_locked(sp)); /* we no longer need LCP */ sp->lcp.protos &= ~(1 << IDX_IPV6CP); } @@ -3708,6 +3950,8 @@ sppp_ipv6cp_scr(struct sppp *sp) struct in6_addr ouraddr; int i = 0; + KASSERT(sppp_locked(sp)); + if (sp->ipv6cp.opts & (1 << IPV6CP_OPT_IFID)) { sppp_get_ip6_addrs(sp, &ouraddr, 0, 0); opt[i++] = IPV6CP_OPT_IFID; @@ -3904,6 +4148,8 @@ sppp_chap_input(struct sppp *sp, struct int value_len, name_len; MD5_CTX ctx; + KASSERT(!sppp_locked(sp)); + len = m->m_pkthdr.len; if (len < 4) { if (debug) @@ -3919,12 +4165,14 @@ sppp_chap_input(struct sppp *sp, struct switch (h->type) { /* challenge, failure and success are his authproto */ case CHAP_CHALLENGE: + sppp_lock_enter(sp); if (sp->myauth.secret == NULL || sp->myauth.name == NULL) { - /* can't do anything useful */ - sp->pp_auth_failures++; - printf("%s: chap input without my name and my secret being set\n", - ifp->if_xname); - break; + /* can't do anything useful */ + sp->pp_auth_failures++; + printf("%s: chap input without my name and my secret being set\n", + ifp->if_xname); + sppp_lock_exit(sp); + break; } value = 1 + (u_char *)(h + 1); value_len = value[-1]; @@ -3943,6 +4191,7 @@ sppp_chap_input(struct sppp *sp, struct len - 4); addlog(">\n"); } + sppp_lock_exit(sp); break; } @@ -3972,9 +4221,11 @@ sppp_chap_input(struct sppp *sp, struct sp->myauth.name_len, sp->myauth.name, 0); + sppp_lock_exit(sp); break; case CHAP_SUCCESS: + sppp_lock_enter(sp); if (debug) { log(LOG_DEBUG, "%s: chap success", ifp->if_xname); @@ -3996,14 +4247,17 @@ sppp_chap_input(struct sppp *sp, struct * to network phase. */ splx(x); + sppp_lock_exit(sp); break; } splx(x); sppp_phase_network(sp); + sppp_lock_exit(sp); break; case CHAP_FAILURE: x = splnet(); + sppp_lock_enter(sp); sp->pp_auth_failures++; splx(x); if (debug) { @@ -4017,15 +4271,18 @@ sppp_chap_input(struct sppp *sp, struct } else log(LOG_INFO, "%s: chap failure\n", ifp->if_xname); + sppp_lock_exit(sp); /* await LCP shutdown by authenticator */ break; /* response is my authproto */ case CHAP_RESPONSE: + sppp_lock_enter(sp); if (sp->hisauth.secret == NULL) { - /* can't do anything useful */ - printf("%s: chap input without his secret being set\n", - ifp->if_xname); + /* can't do anything useful */ + printf("%s: chap input without his secret being set\n", + ifp->if_xname); + sppp_lock_exit(sp); break; } value = 1 + (u_char *)(h + 1); @@ -4045,6 +4302,7 @@ sppp_chap_input(struct sppp *sp, struct len - 4); addlog(">\n"); } + sppp_lock_exit(sp); break; } if (h->ident != sp->confid[IDX_CHAP]) { @@ -4054,6 +4312,7 @@ sppp_chap_input(struct sppp *sp, struct "(got %d, expected %d)\n", ifp->if_xname, h->ident, sp->confid[IDX_CHAP]); + sppp_lock_exit(sp); break; } if (sp->hisauth.name != NULL && @@ -4102,6 +4361,7 @@ sppp_chap_input(struct sppp *sp, struct if (value_len != sizeof digest || memcmp(digest, value, value_len) != 0) { chap_failure: + KASSERT(sppp_locked(sp)); /* action scn, tld */ x = splnet(); sp->pp_auth_failures++; @@ -4110,6 +4370,7 @@ chap_failure: sizeof(FAILMSG) - 1, (const u_char *)FAILMSG, 0); chap.tld(sp); + sppp_lock_exit(sp); break; } sp->pp_auth_failures = 0; @@ -4123,10 +4384,12 @@ chap_failure: sppp_cp_change_state(&chap, sp, STATE_OPENED); chap.tlu(sp); } + sppp_lock_exit(sp); break; default: /* Unknown CHAP packet type -- ignore. */ + sppp_lock_enter(sp); if (debug) { log(LOG_DEBUG, "%s: chap unknown input(%s) " "<0x%x id=0x%xh len=%d", @@ -4137,25 +4400,49 @@ chap_failure: sppp_print_bytes((u_char *)(h + 1), len - 4); addlog(">\n"); } + sppp_lock_exit(sp); break; } } +void +sppp_lock_enter(struct sppp *sp) +{ + if (sp->pp_lock) + mutex_enter(sp->pp_lock); +} + +void +sppp_lock_exit(struct sppp *sp) +{ + if (sp->pp_lock) + mutex_exit(sp->pp_lock); +} + +int +sppp_locked(struct sppp *sp) +{ + return (!(sp->pp_lock) || mutex_owned(sp->pp_lock)); +} + static void sppp_chap_init(struct sppp *sp) { + sppp_lock_enter(sp); /* Chap doesn't have STATE_INITIAL at all. */ sp->state[IDX_CHAP] = STATE_CLOSED; sp->fail_counter[IDX_CHAP] = 0; sp->pp_seq[IDX_CHAP] = 0; sp->pp_rseq[IDX_CHAP] = 0; callout_init(&sp->ch[IDX_CHAP], 0); + sppp_lock_exit(sp); } static void sppp_chap_open(struct sppp *sp) { + KASSERT(sppp_locked(sp)); if (sp->hisauth.proto == PPP_CHAP && (sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) != 0) { /* we are authenticator for CHAP, start it */ @@ -4169,6 +4456,8 @@ sppp_chap_open(struct sppp *sp) static void sppp_chap_close(struct sppp *sp) { + KASSERT(sppp_locked(sp)); + if (sp->state[IDX_CHAP] != STATE_CLOSED) sppp_cp_change_state(&chap, sp, STATE_CLOSED); } @@ -4180,7 +4469,11 @@ sppp_chap_TO(void *cookie) STDDCL; int s; + KASSERT(!sppp_locked(sp)); + s = splnet(); + sppp_lock_enter(sp); + if (debug) log(LOG_DEBUG, "%s: chap TO(%s) rst_counter = %d\n", ifp->if_xname, @@ -4209,6 +4502,7 @@ sppp_chap_TO(void *cookie) break; } + sppp_lock_exit(sp); splx(s); } @@ -4218,6 +4512,7 @@ sppp_chap_tlu(struct sppp *sp) STDDCL; int i, x; + KASSERT(sppp_locked(sp)); i = 0; sp->rst_counter[IDX_CHAP] = sp->lcp.max_configure; @@ -4277,6 +4572,8 @@ sppp_chap_tld(struct sppp *sp) { STDDCL; + KASSERT(sppp_locked(sp)); + if (debug) log(LOG_DEBUG, "%s: chap tld\n", ifp->if_xname); callout_stop(&sp->ch[IDX_CHAP]); @@ -4291,6 +4588,8 @@ sppp_chap_scr(struct sppp *sp) uint32_t *ch; u_char clen = 4 * sizeof(uint32_t); + KASSERT(sppp_locked(sp)); + if (sp->hisauth.name == NULL) { /* can't do anything useful */ printf("%s: chap starting without his name being set\n", @@ -4336,6 +4635,8 @@ sppp_pap_input(struct sppp *sp, struct m char *name, *secret; int name_len, secret_len; + KASSERT(!sppp_locked(sp)); + /* * Malicious input might leave this uninitialized, so * init to an impossible value. @@ -4356,11 +4657,14 @@ sppp_pap_input(struct sppp *sp, struct m switch (h->type) { /* PAP request is my authproto */ case PAP_REQ: + sppp_lock_enter(sp); if (sp->hisauth.name == NULL || sp->hisauth.secret == NULL) { - /* can't do anything useful */ - printf("%s: pap request without his name and his secret being set\n", - ifp->if_xname); - break; + /* can't do anything useful */ + printf("%s: " + "pap request without his name and his secret being set\n", + ifp->if_xname); + sppp_lock_exit(sp); + break; } name = 1 + (u_char *)(h + 1); name_len = name[-1]; @@ -4378,6 +4682,7 @@ sppp_pap_input(struct sppp *sp, struct m len - 4); addlog(">\n"); } + sppp_lock_exit(sp); break; } if (debug) { @@ -4404,6 +4709,7 @@ sppp_pap_input(struct sppp *sp, struct m sizeof(FAILMSG) - 1, (const u_char *)FAILMSG, 0); pap.tld(sp); + sppp_lock_exit(sp); break; } /* action sca, perhaps tlu */ @@ -4419,10 +4725,12 @@ sppp_pap_input(struct sppp *sp, struct m sppp_cp_change_state(&pap, sp, STATE_OPENED); pap.tlu(sp); } + sppp_lock_exit(sp); break; /* ack and nak are his authproto */ case PAP_ACK: + sppp_lock_enter(sp); callout_stop(&sp->pap_my_to_ch); if (debug) { log(LOG_DEBUG, "%s: pap success", @@ -4447,13 +4755,16 @@ sppp_pap_input(struct sppp *sp, struct m * to network phase. */ splx(x); + sppp_lock_exit(sp); break; } splx(x); sppp_phase_network(sp); + sppp_lock_exit(sp); break; case PAP_NAK: + sppp_lock_enter(sp); callout_stop(&sp->pap_my_to_ch); sp->pp_auth_failures++; if (debug) { @@ -4470,10 +4781,12 @@ sppp_pap_input(struct sppp *sp, struct m log(LOG_INFO, "%s: pap failure\n", ifp->if_xname); /* await LCP shutdown by authenticator */ + sppp_lock_exit(sp); break; default: /* Unknown PAP packet type -- ignore. */ + sppp_lock_enter(sp); if (debug) { log(LOG_DEBUG, "%s: pap corrupted input " "<0x%x id=0x%x len=%d", @@ -4483,6 +4796,7 @@ sppp_pap_input(struct sppp *sp, struct m sppp_print_bytes((u_char *)(h + 1), len - 4); addlog(">\n"); } + sppp_lock_exit(sp); break; } @@ -4492,17 +4806,21 @@ static void sppp_pap_init(struct sppp *sp) { /* PAP doesn't have STATE_INITIAL at all. */ + sppp_lock_enter(sp); sp->state[IDX_PAP] = STATE_CLOSED; sp->fail_counter[IDX_PAP] = 0; sp->pp_seq[IDX_PAP] = 0; sp->pp_rseq[IDX_PAP] = 0; callout_init(&sp->ch[IDX_PAP], 0); callout_init(&sp->pap_my_to_ch, 0); + sppp_lock_exit(sp); } static void sppp_pap_open(struct sppp *sp) { + KASSERT(sppp_locked(sp)); + if (sp->hisauth.proto == PPP_PAP && (sp->lcp.opts & (1 << LCP_OPT_AUTH_PROTO)) != 0) { /* we are authenticator for PAP, start our timer */ @@ -4520,6 +4838,7 @@ sppp_pap_open(struct sppp *sp) static void sppp_pap_close(struct sppp *sp) { + KASSERT(sppp_locked(sp)); if (sp->state[IDX_PAP] != STATE_CLOSED) sppp_cp_change_state(&pap, sp, STATE_CLOSED); } @@ -4535,7 +4854,10 @@ sppp_pap_TO(void *cookie) STDDCL; int s; + KASSERT(!sppp_locked(sp)); + s = splnet(); + sppp_lock_enter(sp); if (debug) log(LOG_DEBUG, "%s: pap TO(%s) rst_counter = %d\n", ifp->if_xname, @@ -4559,6 +4881,7 @@ sppp_pap_TO(void *cookie) break; } + sppp_lock_exit(sp); splx(s); } @@ -4573,11 +4896,15 @@ sppp_pap_my_TO(void *cookie) struct sppp *sp = (struct sppp *)cookie; STDDCL; + KASSERT(!sppp_locked(sp)); + + sppp_lock_enter(sp); if (debug) log(LOG_DEBUG, "%s: pap peer TO\n", ifp->if_xname); pap.scr(sp); + sppp_lock_exit(sp); } static void @@ -4586,6 +4913,7 @@ sppp_pap_tlu(struct sppp *sp) STDDCL; int x; + KASSERT(sppp_locked(sp)); sp->rst_counter[IDX_PAP] = sp->lcp.max_configure; if (debug) @@ -4615,6 +4943,8 @@ sppp_pap_tld(struct sppp *sp) { STDDCL; + KASSERT(sppp_locked(sp)); + if (debug) log(LOG_DEBUG, "%s: pap tld\n", ifp->if_xname); callout_stop(&sp->ch[IDX_PAP]); @@ -4629,6 +4959,8 @@ sppp_pap_scr(struct sppp *sp) { u_char idlen, pwdlen; + KASSERT(sppp_locked(sp)); + if (sp->myauth.secret == NULL || sp->myauth.name == NULL) { /* can't do anything useful */ printf("%s: pap starting without my name and secret being set\n", @@ -4678,6 +5010,8 @@ sppp_auth_send(const struct cp *cp, stru const char *msg; va_list ap; + KASSERT(sppp_locked(sp)); + MGETHDR(m, M_DONTWAIT, MT_DATA); if (! m) return; @@ -4739,8 +5073,11 @@ sppp_auth_send(const struct cp *cp, stru return; } else IF_ENQUEUE(&sp->pp_cpq, m); - if (! (ifp->if_flags & IFF_OACTIVE)) + if (! (ifp->if_flags & IFF_OACTIVE)) { + sppp_lock_exit(sp); if_start_lock(ifp); + sppp_lock_enter(sp); + } ifp->if_obytes += m->m_pkthdr.len + 3; } @@ -4754,10 +5091,15 @@ sppp_keepalive(void *dummy) int s; time_t now; + SPPPQ_LOCK(); + s = splnet(); now = time_uptime; for (sp=spppq; sp; sp=sp->pp_next) { - struct ifnet *ifp = &sp->pp_if; + struct ifnet *ifp = NULL; + + sppp_lock_enter(sp); + ifp = &sp->pp_if; /* check idle timeout */ if ((sp->pp_idle_timeout != 0) && (ifp->if_flags & IFF_RUNNING) @@ -4769,29 +5111,38 @@ sppp_keepalive(void *dummy) sp->pp_if.if_xname, (unsigned long)(now-sp->pp_last_activity)); lcp.Close(sp); + sppp_lock_exit(sp); continue; } } /* Keepalive mode disabled or channel down? */ if (! (sp->pp_flags & PP_KEEPALIVE) || - ! (ifp->if_flags & IFF_RUNNING)) + ! (ifp->if_flags & IFF_RUNNING)) { + sppp_lock_exit(sp); continue; + } + /* No keepalive in PPP mode if LCP not opened yet. */ if (! (sp->pp_flags & PP_CISCO) && - sp->pp_phase < SPPP_PHASE_AUTHENTICATE) + sp->pp_phase < SPPP_PHASE_AUTHENTICATE) { + sppp_lock_exit(sp); continue; + } /* No echo reply, but maybe user data passed through? */ if ((now - sp->pp_last_receive) < sp->pp_max_noreceive) { sp->pp_alivecnt = 0; + sppp_lock_exit(sp); continue; } if (sp->pp_alivecnt >= sp->pp_maxalive) { /* No keepalive packets got. Stop the interface. */ + sppp_lock_exit(sp); if_down (ifp); + sppp_lock_enter(sp); IF_PURGE(&sp->pp_cpq); if (! (sp->pp_flags & PP_CISCO)) { printf("%s: LCP keepalive timed out, going to restart the connection\n", @@ -4808,6 +5159,8 @@ sppp_keepalive(void *dummy) * will summon the magic needed to reestablish it. */ if (sp->pp_tlf) sp->pp_tlf(sp); + + sppp_lock_exit(sp); continue; } } @@ -4822,9 +5175,13 @@ sppp_keepalive(void *dummy) sppp_cp_send(sp, PPP_LCP, ECHO_REQ, sp->lcp.echoid, 4, &nmagic); } + + sppp_lock_exit(sp); } splx(s); callout_reset(&keepalive_ch, hz * LCP_KEEPALIVE_INTERVAL, sppp_keepalive, NULL); + + SPPPQ_UNLOCK(); } #ifdef INET @@ -5210,6 +5567,8 @@ sppp_params(struct sppp *sp, u_long cmd, int error; size_t len; + sppp_lock_enter(sp); + cfg->myauthflags = sp->myauth.flags; cfg->hisauthflags = sp->hisauth.flags; strlcpy(cfg->ifname, sp->pp_if.if_xname, sizeof(cfg->ifname)); @@ -5227,10 +5586,15 @@ sppp_params(struct sppp *sp, u_long cmd, cfg->myname_length = 0; } else { len = sp->myauth.name_len + 1; + + sppp_lock_exit(sp); + if (cfg->myname_length < len) return (ENAMETOOLONG); error = copyout(sp->myauth.name, cfg->myname, len); if (error) return error; + + sppp_lock_enter(sp); } } if (cfg->hisname_length == 0) { @@ -5241,12 +5605,16 @@ sppp_params(struct sppp *sp, u_long cmd, cfg->hisname_length = 0; } else { len = sp->hisauth.name_len + 1; + + sppp_lock_exit(sp); if (cfg->hisname_length < len) return (ENAMETOOLONG); error = copyout(sp->hisauth.name, cfg->hisname, len); if (error) return error; + sppp_lock_enter(sp); } } + sppp_lock_exit(sp); } break; case SPPPSETAUTHCFG: @@ -5254,6 +5622,8 @@ sppp_params(struct sppp *sp, u_long cmd, struct spppauthcfg *cfg = (struct spppauthcfg *)data; int error; + sppp_lock_enter(sp); + if (sp->myauth.name) { free(sp->myauth.name, M_DEVBUF); sp->myauth.name = NULL; @@ -5272,56 +5642,72 @@ sppp_params(struct sppp *sp, u_long cmd, } if (cfg->hisname != NULL && cfg->hisname_length > 0) { - if (cfg->hisname_length >= MCLBYTES) - return (ENAMETOOLONG); - sp->hisauth.name = malloc(cfg->hisname_length, M_DEVBUF, M_WAITOK); - error = copyin(cfg->hisname, sp->hisauth.name, cfg->hisname_length); - if (error) { - free(sp->hisauth.name, M_DEVBUF); - sp->hisauth.name = NULL; - return error; - } - sp->hisauth.name_len = cfg->hisname_length - 1; - sp->hisauth.name[sp->hisauth.name_len] = 0; + if (cfg->hisname_length >= MCLBYTES) { + sppp_lock_exit(sp); + return (ENAMETOOLONG); + } + sp->hisauth.name = malloc(cfg->hisname_length, M_DEVBUF, M_WAITOK); + error = copyin(cfg->hisname, sp->hisauth.name, cfg->hisname_length); + if (error) { + free(sp->hisauth.name, M_DEVBUF); + sp->hisauth.name = NULL; + sppp_lock_exit(sp); + return error; + } + sp->hisauth.name_len = cfg->hisname_length - 1; + sp->hisauth.name[sp->hisauth.name_len] = 0; } if (cfg->hissecret != NULL && cfg->hissecret_length > 0) { - if (cfg->hissecret_length >= MCLBYTES) - return (ENAMETOOLONG); - sp->hisauth.secret = malloc(cfg->hissecret_length, M_DEVBUF, M_WAITOK); - error = copyin(cfg->hissecret, sp->hisauth.secret, cfg->hissecret_length); - if (error) { - free(sp->hisauth.secret, M_DEVBUF); - sp->hisauth.secret = NULL; - return error; - } - sp->hisauth.secret_len = cfg->hissecret_length - 1; - sp->hisauth.secret[sp->hisauth.secret_len] = 0; + if (cfg->hissecret_length >= MCLBYTES) { + sppp_lock_exit(sp); + return (ENAMETOOLONG); + } + sp->hisauth.secret = malloc(cfg->hissecret_length, + M_DEVBUF, M_WAITOK); + error = copyin(cfg->hissecret, sp->hisauth.secret, + cfg->hissecret_length); + if (error) { + free(sp->hisauth.secret, M_DEVBUF); + sp->hisauth.secret = NULL; + sppp_lock_exit(sp); + return error; + } + sp->hisauth.secret_len = cfg->hissecret_length - 1; + sp->hisauth.secret[sp->hisauth.secret_len] = 0; } if (cfg->myname != NULL && cfg->myname_length > 0) { - if (cfg->myname_length >= MCLBYTES) - return (ENAMETOOLONG); - sp->myauth.name = malloc(cfg->myname_length, M_DEVBUF, M_WAITOK); - error = copyin(cfg->myname, sp->myauth.name, cfg->myname_length); - if (error) { - free(sp->myauth.name, M_DEVBUF); - sp->myauth.name = NULL; - return error; - } - sp->myauth.name_len = cfg->myname_length - 1; - sp->myauth.name[sp->myauth.name_len] = 0; + if (cfg->myname_length >= MCLBYTES) { + sppp_lock_exit(sp); + return (ENAMETOOLONG); + } + sp->myauth.name = malloc(cfg->myname_length, M_DEVBUF, M_WAITOK); + error = copyin(cfg->myname, sp->myauth.name, cfg->myname_length); + if (error) { + free(sp->myauth.name, M_DEVBUF); + sp->myauth.name = NULL; + sppp_lock_exit(sp); + return error; + } + sp->myauth.name_len = cfg->myname_length - 1; + sp->myauth.name[sp->myauth.name_len] = 0; } if (cfg->mysecret != NULL && cfg->mysecret_length > 0) { - if (cfg->mysecret_length >= MCLBYTES) - return (ENAMETOOLONG); - sp->myauth.secret = malloc(cfg->mysecret_length, M_DEVBUF, M_WAITOK); - error = copyin(cfg->mysecret, sp->myauth.secret, cfg->mysecret_length); - if (error) { - free(sp->myauth.secret, M_DEVBUF); - sp->myauth.secret = NULL; - return error; - } - sp->myauth.secret_len = cfg->mysecret_length - 1; - sp->myauth.secret[sp->myauth.secret_len] = 0; + if (cfg->mysecret_length >= MCLBYTES) { + sppp_lock_exit(sp); + return (ENAMETOOLONG); + } + sp->myauth.secret = malloc(cfg->mysecret_length, + M_DEVBUF, M_WAITOK); + error = copyin(cfg->mysecret, sp->myauth.secret, + cfg->mysecret_length); + if (error) { + free(sp->myauth.secret, M_DEVBUF); + sp->myauth.secret = NULL; + sppp_lock_exit(sp); + return error; + } + sp->myauth.secret_len = cfg->mysecret_length - 1; + sp->myauth.secret[sp->myauth.secret_len] = 0; } sp->myauth.flags = cfg->myauthflags; if (cfg->myauth) @@ -5334,120 +5720,156 @@ sppp_params(struct sppp *sp, u_long cmd, sp->lcp.opts |= (1 << LCP_OPT_AUTH_PROTO); else sp->lcp.opts &= ~(1 << LCP_OPT_AUTH_PROTO); + + sppp_lock_exit(sp); } break; case SPPPGETLCPCFG: { - struct sppplcpcfg *lcpp = (struct sppplcpcfg *)data; - lcpp->lcp_timeout = sp->lcp.timeout; + struct sppplcpcfg *lcpp = (struct sppplcpcfg *)data; + sppp_lock_enter(sp); + lcpp->lcp_timeout = sp->lcp.timeout; + sppp_lock_exit(sp); } break; case SPPPSETLCPCFG: { - struct sppplcpcfg *lcpp = (struct sppplcpcfg *)data; - sp->lcp.timeout = lcpp->lcp_timeout; + struct sppplcpcfg *lcpp = (struct sppplcpcfg *)data; + sppp_lock_enter(sp); + sp->lcp.timeout = lcpp->lcp_timeout; + sppp_lock_exit(sp); } break; case SPPPGETSTATUS: { struct spppstatus *status = (struct spppstatus *)data; + sppp_lock_enter(sp); status->phase = sp->pp_phase; + sppp_lock_exit(sp); } break; case SPPPGETSTATUSNCP: { struct spppstatusncp *status = (struct spppstatusncp *)data; + sppp_lock_enter(sp); status->phase = sp->pp_phase; + sppp_lock_exit(sp); status->ncpup = sppp_ncp_check(sp); } break; case SPPPGETIDLETO: { struct spppidletimeout *to = (struct spppidletimeout *)data; + sppp_lock_enter(sp); to->idle_seconds = sp->pp_idle_timeout; + sppp_lock_exit(sp); } break; case SPPPSETIDLETO: { struct spppidletimeout *to = (struct spppidletimeout *)data; - sp->pp_idle_timeout = to->idle_seconds; + sppp_lock_enter(sp); + sp->pp_idle_timeout = to->idle_seconds; + sppp_lock_exit(sp); } break; case SPPPSETAUTHFAILURE: { struct spppauthfailuresettings *afsettings = (struct spppauthfailuresettings *)data; - sp->pp_max_auth_fail = afsettings->max_failures; - sp->pp_auth_failures = 0; + sppp_lock_enter(sp); + sp->pp_max_auth_fail = afsettings->max_failures; + sp->pp_auth_failures = 0; + sppp_lock_exit(sp); } break; case SPPPGETAUTHFAILURES: { struct spppauthfailurestats *stats = (struct spppauthfailurestats *)data; - stats->auth_failures = sp->pp_auth_failures; - stats->max_failures = sp->pp_max_auth_fail; + sppp_lock_enter(sp); + stats->auth_failures = sp->pp_auth_failures; + stats->max_failures = sp->pp_max_auth_fail; + sppp_lock_exit(sp); } break; case SPPPSETDNSOPTS: { struct spppdnssettings *req = (struct spppdnssettings *)data; + sppp_lock_enter(sp); sp->query_dns = req->query_dns & 3; + sppp_lock_exit(sp); } break; case SPPPGETDNSOPTS: { struct spppdnssettings *req = (struct spppdnssettings *)data; + sppp_lock_enter(sp); req->query_dns = sp->query_dns; + sppp_lock_exit(sp); } break; case SPPPGETDNSADDRS: { - struct spppdnsaddrs *addrs = (struct spppdnsaddrs *)data; - memcpy(&addrs->dns, &sp->dns_addrs, sizeof addrs->dns); + struct spppdnsaddrs *addrs = (struct spppdnsaddrs *)data; + sppp_lock_enter(sp); + memcpy(&addrs->dns, &sp->dns_addrs, sizeof addrs->dns); + sppp_lock_exit(sp); } break; case SPPPGETKEEPALIVE: { struct spppkeepalivesettings *settings = (struct spppkeepalivesettings*)data; + sppp_lock_enter(sp); settings->maxalive = sp->pp_maxalive; settings->max_noreceive = sp->pp_max_noreceive; + sppp_lock_exit(sp); } break; case SPPPSETKEEPALIVE: { struct spppkeepalivesettings *settings = (struct spppkeepalivesettings*)data; + sppp_lock_enter(sp); sp->pp_maxalive = settings->maxalive; sp->pp_max_noreceive = settings->max_noreceive; + sppp_lock_exit(sp); } break; #if defined(COMPAT_50) || defined(MODULAR) case __SPPPGETIDLETO50: { struct spppidletimeout50 *to = (struct spppidletimeout50 *)data; + sppp_lock_enter(sp); to->idle_seconds = (uint32_t)sp->pp_idle_timeout; + sppp_lock_exit(sp); } break; case __SPPPSETIDLETO50: { - struct spppidletimeout50 *to = (struct spppidletimeout50 *)data; - sp->pp_idle_timeout = (time_t)to->idle_seconds; + struct spppidletimeout50 *to = (struct spppidletimeout50 *)data; + sppp_lock_enter(sp); + sp->pp_idle_timeout = (time_t)to->idle_seconds; + sppp_lock_exit(sp); } break; case __SPPPGETKEEPALIVE50: { struct spppkeepalivesettings50 *settings = (struct spppkeepalivesettings50*)data; + sppp_lock_enter(sp); settings->maxalive = sp->pp_maxalive; settings->max_noreceive = (uint32_t)sp->pp_max_noreceive; + sppp_lock_exit(sp); } break; case __SPPPSETKEEPALIVE50: { struct spppkeepalivesettings50 *settings = (struct spppkeepalivesettings50*)data; + sppp_lock_enter(sp); sp->pp_maxalive = settings->maxalive; sp->pp_max_noreceive = (time_t)settings->max_noreceive; + sppp_lock_exit(sp); } break; #endif /* COMPAT_50 || MODULAR */ @@ -5464,6 +5886,8 @@ sppp_phase_network(struct sppp *sp) int i; uint32_t mask; + KASSERT(sppp_locked(sp)); + sppp_change_phase(sp, SPPP_PHASE_NETWORK); /* Notify NCPs now. */ @@ -5473,8 +5897,9 @@ sppp_phase_network(struct sppp *sp) /* Send Up events to all NCPs. */ for (i = 0, mask = 1; i < IDX_COUNT; i++, mask <<= 1) - if ((sp->lcp.protos & mask) && ((cps[i])->flags & CP_NCP)) + if ((sp->lcp.protos & mask) && ((cps[i])->flags & CP_NCP)) { (cps[i])->Up(sp); + } /* if no NCP is starting, all this was in vain, close down */ sppp_lcp_check_and_close(sp); Index: src/sys/net/if_spppvar.h diff -u src/sys/net/if_spppvar.h:1.19 src/sys/net/if_spppvar.h:1.20 --- src/sys/net/if_spppvar.h:1.19 Thu Dec 1 02:30:54 2016 +++ src/sys/net/if_spppvar.h Tue Dec 13 00:35:11 2016 @@ -1,4 +1,4 @@ -/* $NetBSD: if_spppvar.h,v 1.19 2016/12/01 02:30:54 knakahara Exp $ */ +/* $NetBSD: if_spppvar.h,v 1.20 2016/12/13 00:35:11 knakahara Exp $ */ #ifndef _NET_IF_SPPPVAR_H_ #define _NET_IF_SPPPVAR_H_ @@ -111,6 +111,7 @@ struct sppp { int pp_auth_failures; /* authorization failures */ int pp_max_auth_fail; /* max. allowed authorization failures */ int pp_phase; /* phase we're currently in */ + kmutex_t *pp_lock; /* lock for sppp structure */ int query_dns; /* 1 if we want to know the dns addresses */ uint32_t dns_addrs[2]; int state[IDX_COUNT]; /* state machine */ @@ -180,5 +181,8 @@ int sppp_ioctl(struct ifnet *, u_long, v struct mbuf *sppp_dequeue (struct ifnet *); int sppp_isempty (struct ifnet *); void sppp_flush (struct ifnet *); +void sppp_lock_enter(struct sppp *); +void sppp_lock_exit(struct sppp *); +int sppp_locked(struct sppp *); #endif #endif /* !_NET_IF_SPPPVAR_H_ */