/*
* Copyright (c) 2001-2002 Packet Design, LLC.
* All rights reserved.
*
* Subject to the following obligations and disclaimer of warranty,
* use and redistribution of this software, in source or object code
* forms, with or without modifications are expressly permitted by
* Packet Design; provided, however, that:
*
* (i) Any and all reproductions of the source or object code
* must include the copyright notice above and the following
* disclaimer of warranties; and
* (ii) No rights are granted, in any manner or form, to use
* Packet Design trademarks, including the mark "PACKET DESIGN"
* on advertising, endorsements, or otherwise except as such
* appears in the above copyright notice or in the software.
*
* THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND
* TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO
* REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING
* THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
* OR NON-INFRINGEMENT. PACKET DESIGN DOES NOT WARRANT, GUARANTEE,
* OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS
* OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY,
* RELIABILITY OR OTHERWISE. IN NO EVENT SHALL PACKET DESIGN BE
* LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE
* OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL
* DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF
* USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
* THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*
* Author: Archie Cobbs <archie@freebsd.org>
*/
#include "ppp/ppp_defs.h"
#include "ppp/ppp_log.h"
#include "ppp/ppp_node.h"
#include "ppp/ppp_fsm_option.h"
#include "ppp/ppp_auth.h"
#include "ppp/ppp_lcp.h"
#include "ppp/ppp_link.h"
#include "ppp/ppp_bundle.h"
#include "ppp/ppp_manager.h"
#include "ppp/ppp_engine.h"
#include "ppp/ppp_pptp_server.h"
#include "ppp/ppp_l2tp_server.h"
#define ENGINE_MTYPE "ppp_engine"
#define ENGINE_HASH_LOAD 200
struct ppp_engine {
struct ppp_manager *manager; /* ppp manager object */
struct pevent_ctx *ev_ctx; /* event context */
pthread_mutex_t mutex; /* mutex for context */
struct ppp_log *log; /* log */
struct ghash *bundles; /* bundles, each >= 1 link */
struct ghash *links; /* "floating" unbundled links */
void *pptp_server; /* pptp server */
void *l2tp_server; /* l2tp server */
};
/* Macro for logging */
#define LOG(sev, fmt, args...) PPP_LOG(engine->log, sev, fmt , ## args)
/*
* Create a new PPP engine.
*
* The "log" is destroyed when the engine is destroyed.
*/
struct ppp_engine *
ppp_engine_create(struct ppp_manager *manager,
const pthread_attr_t *attr, struct ppp_log *log)
{
struct ppp_engine *engine;
pthread_mutexattr_t mattr;
int got_mutex = 0;
int got_mattr = 0;
/* Create engine object */
if ((engine = MALLOC(ENGINE_MTYPE, sizeof(*engine))) == NULL)
return (NULL);
memset(engine, 0, sizeof(*engine));
engine->manager = manager;
engine->log = log;
/* Create hash tables */
if ((engine->links = ghash_create(engine, 0, ENGINE_HASH_LOAD,
ENGINE_MTYPE, NULL, NULL, NULL, NULL)) == NULL)
goto fail;
if ((engine->bundles = ghash_create(engine, 0, ENGINE_HASH_LOAD,
ENGINE_MTYPE, NULL, NULL, NULL, NULL)) == NULL)
goto fail;
/* Initialize mutex */
if ((errno = pthread_mutexattr_init(&mattr)) != 0)
goto fail;
got_mattr = 1;
if ((errno = pthread_mutexattr_settype(&mattr,
PTHREAD_MUTEX_RECURSIVE)) != 0)
goto fail;
if ((errno = pthread_mutex_init(&engine->mutex, &mattr)) != 0)
goto fail;
got_mutex = 1;
pthread_mutexattr_destroy(&mattr);
got_mattr = 0;
/* Create event context */
if ((engine->ev_ctx = pevent_ctx_create("ppp_engine", attr)) == NULL)
goto fail;
/* Done */
return (engine);
fail:
/* Clean up after failure */
ghash_destroy(&engine->bundles);
ghash_destroy(&engine->links);
if (got_mutex)
pthread_mutex_destroy(&engine->mutex);
if (got_mattr)
pthread_mutexattr_destroy(&mattr);
FREE(ENGINE_MTYPE, engine);
return (NULL);
}
/*
* Destroy a PPP engine.
*
* If "wait" is true, wait for all associated threads to exit.
*/
void
ppp_engine_destroy(struct ppp_engine **enginep, int wait)
{
struct ppp_engine *const engine = *enginep;
struct ppp_bundle **bundles;
struct ppp_link **links;
int num;
int i;
/* Sanity check */
if (engine == NULL)
return;
*enginep = NULL;
/* Stop PPTP and L2TP servers (if any) */
ppp_pptp_server_stop(engine);
ppp_l2tp_server_stop(engine);
/* Destroy floating links */
if ((num = ghash_dump(engine->links,
(void ***)&links, TYPED_MEM_TEMP)) == -1)
LOG(LOG_ERR, "%s: %m", "ghash_dump");
else {
for (i = 0; i < num; i++)
ppp_link_destroy(&links[i]);
FREE(TYPED_MEM_TEMP, links);
}
/* Destroy bundles */
if ((num = ghash_dump(engine->bundles,
(void ***)&bundles, TYPED_MEM_TEMP)) == -1)
LOG(LOG_ERR, "%s: %m", "ghash_dump");
else {
for (i = 0; i < num; i++)
ppp_bundle_destroy(&bundles[i]);
FREE(TYPED_MEM_TEMP, bundles);
}
/* Destroy event context; there should be no remaining events */
if ((num = pevent_ctx_count(engine->ev_ctx)) != 0) {
LOG(LOG_ERR, "%s: %d events remain at shutdown",
__FUNCTION__, num);
}
pevent_ctx_destroy(&engine->ev_ctx);
/* Free engine object */
ghash_destroy(&engine->links);
ghash_destroy(&engine->bundles);
ppp_log_close(&engine->log);
FREE(ENGINE_MTYPE, engine);
}
/*
* Get a list of all known bundles.
*/
int
ppp_engine_get_bundles(struct ppp_engine *engine,
struct ppp_bundle ***listp, const char *mtype)
{
return (ghash_dump(engine->bundles, (void ***)listp, mtype));
}
/*
* Add a newly created (and therefore "floating") link.
*/
int
ppp_engine_add_link(struct ppp_engine *engine, struct ppp_link *link)
{
return (ghash_put(engine->links, link));
}
/*
* Remove a "floating" link from the floating link hash table.
*/
void
ppp_engine_del_link(struct ppp_engine *engine, struct ppp_link *link)
{
ghash_remove(engine->links, link);
}
/*
* Add a bundle.
*/
int
ppp_engine_add_bundle(struct ppp_engine *engine, struct ppp_bundle *bundle)
{
return (ghash_put(engine->bundles, bundle));
}
/*
* Remove a bundle.
*/
void
ppp_engine_del_bundle(struct ppp_engine *engine, struct ppp_bundle *bundle)
{
ghash_remove(engine->bundles, bundle);
}
/*
* An "unbundled" link has reached the OPENED state and authenticated.
* Add it to an existing bundle or create a new bundle as appropriate.
*/
struct ppp_bundle *
ppp_engine_join(struct ppp_engine *engine, struct ppp_link *link,
struct ppp_node **nodep, u_int16_t *link_num)
{
struct ppp_bundle *bundle = NULL;
struct ppp_lcp_req lcp_req;
struct ghash_walk walk;
/* Sanity */
assert(*nodep != NULL);
/* If link does not have multilink, can't join any bundles */
ppp_link_get_lcp_req(link, &lcp_req);
if (!lcp_req.multilink[PPP_SELF] || !lcp_req.multilink[PPP_PEER])
goto no_join;
/* See if link matches any existing bundle */
ghash_walk_init(engine->bundles, &walk);
while ((bundle = ghash_walk_next(engine->bundles, &walk)) != NULL) {
const char *link_authname[2];
const char *bund_authname[2];
struct ppp_eid link_eid[2];
struct ppp_eid bund_eid[2];
int j;
/* If bundle does not have multilink, can't join it */
if (!ppp_bundle_get_multilink(bundle))
continue;
/* Get info about link and bundle */
for (j = 0; j < 2; j++) {
link_authname[j] = ppp_link_get_authname(link, j);
ppp_link_get_eid(link, j, &link_eid[j]);
bund_authname[j] = ppp_bundle_get_authname(bundle, j);
ppp_bundle_get_eid(bundle, j, &bund_eid[j]);
}
/* Compare them */
for (j = 0; j < 2; j++) {
if (strcmp(link_authname[j], bund_authname[j]) != 0)
break;
if (link_eid[j].class != bund_eid[j].class)
break;
if (link_eid[j].length != bund_eid[j].length)
break;
if (memcmp(link_eid[j].value,
bund_eid[j].value, link_eid[j].length) != 0)
break;
}
/* If equal, stop */
if (j == 2)
break;
}
no_join:
/* If no matching bundle found, create a new one */
if (bundle == NULL) {
/* Create new bundle */
if ((bundle = ppp_bundle_create(engine,
link, *nodep)) == NULL) {
PPP_LOG(ppp_link_get_log(link), LOG_ERR,
"failed to create new bundle: %m");
return (NULL);
}
/* The bundle steals the link's node */
*nodep = NULL;
*link_num = 0;
} else {
/* Join link into bundle */
if (ppp_bundle_join(bundle, link, *nodep, link_num) == -1) {
PPP_LOG(ppp_link_get_log(link), LOG_ERR,
"link failed to join bundle: %m");
return (NULL);
}
/* Destroy link's node: it's no longer needed */
ppp_node_destroy(nodep);
}
/* Link is no longer 'floating' as bundle now references it */
ppp_engine_del_link(engine, link);
/* Done */
return (bundle);
}
void
ppp_engine_set_pptp_server(struct ppp_engine *engine, void *s)
{
engine->pptp_server = s;
}
void *
ppp_engine_get_pptp_server(struct ppp_engine *engine)
{
return (engine->pptp_server);
}
void
ppp_engine_set_l2tp_server(struct ppp_engine *engine, void *s)
{
engine->l2tp_server = s;
}
void *
ppp_engine_get_l2tp_server(struct ppp_engine *engine)
{
return (engine->l2tp_server);
}
struct pevent_ctx *
ppp_engine_get_ev_ctx(struct ppp_engine *engine)
{
return (engine->ev_ctx);
}
pthread_mutex_t *
ppp_engine_get_mutex(struct ppp_engine *engine)
{
return (&engine->mutex);
}
struct ppp_log *
ppp_engine_get_log(struct ppp_engine *engine)
{
return (engine->log);
}
/********************************************************************
MANAGER CALL-THROUGH FUNCTIONS
********************************************************************/
/*
* Configure a bundle.
*/
void *
ppp_engine_bundle_config(struct ppp_engine *engine,
struct ppp_link *link, struct ppp_bundle_config *conf)
{
return (ppp_manager_bundle_config(engine->manager, link, conf));
}
/*
* Plumb 'top' side of netgraph node.
*/
void *
ppp_engine_bundle_plumb(struct ppp_engine *engine,
struct ppp_bundle *bundle, const char *path, const char *hook,
struct in_addr *ips, struct in_addr *dns, struct in_addr *nbns,
u_int mtu)
{
return (ppp_manager_bundle_plumb(engine->manager, bundle,
path, hook, ips, dns, nbns, mtu));
}
/*
* Disconnect 'top' side of netgraph node.
*/
void
ppp_engine_bundle_unplumb(struct ppp_engine *engine, void *arg,
struct ppp_bundle *bundle)
{
ppp_manager_bundle_unplumb(engine->manager, arg, bundle);
}
/*
* Release an IP address for a peer.
*/
void
ppp_engine_release_ip(struct ppp_engine *engine,
struct ppp_bundle *bundle, struct in_addr ip)
{
ppp_manager_release_ip(engine->manager, bundle, ip);
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>