diff --git a/doc/tex/asterisk.tex b/doc/tex/asterisk.tex
index 3900f4cb6b215b5d113d1378f1905207361414ae..935d0506a603778bf3b7d8e622a97264c8b6c411 100644
--- a/doc/tex/asterisk.tex
+++ b/doc/tex/asterisk.tex
@@ -144,6 +144,9 @@ reference purposes.
 \chapter{Calendaring}
   \input{calendaring.tex}
 
+\chapter{Security Framework}
+  \input{security-events.tex}
+
 \chapter{Development}
   \section{Backtrace}
   \input{backtrace.tex}
diff --git a/doc/tex/security-events.tex b/doc/tex/security-events.tex
new file mode 100644
index 0000000000000000000000000000000000000000..d46b1505d73ffb5c200054e287d6dec016a3a93a
--- /dev/null
+++ b/doc/tex/security-events.tex
@@ -0,0 +1,250 @@
+\section{Introduction}
+
+   Attacks on Voice over IP networks are becoming increasingly more common.  It
+has become clear that we must do something within Asterisk to help mitigate
+these attacks.
+
+   Through a number of discussions with groups of developers in the Asterisk
+community, the general consensus is that the best thing that we can do within
+Asterisk is to build a framework which recognizes and reports events that could
+potentially have security implications.  Each channel driver has a different
+concept of what is an "event", and then each administrator has different
+thresholds of what is a "bad" event and what is a restorative event.  The
+process of acting upon this information is left to an external program to
+correlate and then take action - block traffic, modify dialing rules, etc.  It
+was decided that embedding actions inside of Asterisk was inappropriate, as the
+complexity of construction of such rule sets is difficult and there was no
+agreement on where rules should be enabled or how they should be processed.  The
+addition of a major section of code to handle rule expiration and severity
+interpretation was significant.  As a final determining factor, there are
+external programs and services which already parse log files and act in concert
+with packet filters or external devices to protect or alter network security
+models for IP connected hosts.
+
+\section{Framework Overview}
+
+    This section discusses the architecture of the Asterisk modifications being
+proposed.
+
+    There are two main components that we propose for the initial
+implementation of the security framework:
+
+\begin{itemize}
+    \item Security Event Generation
+    \item Security Event Logger
+\end{itemize}
+
+\subsection{Security Event Generation}
+
+    The ast\_event API is used for the generation of security events.  That
+way, the events are in an easily interpretable format within Asterisk to
+make it easy to write modules that do things with them.  There are also some
+helper data structures and functions to aid Asterisk modules in reporting these
+security events with the proper contents.
+
+    The next section of this document contains the current list of security events
+being proposed.  Each security event type has some required pieces of
+information and some other optional pieces of information.
+
+    Subscribing to security events from within Asterisk can be done by
+subscribing to events of type AST\_EVENT\_SECURITY.  These events have an
+information element, AST\_EVENT\_IE\_SECURITY\_EVENT, which identifies the security
+event sub-type (from the list described in the next section).  The result of the
+information elements in the events contain the required and optional meta data
+associated with the event sub-type.
+
+\subsection{Security Event Logger}
+
+    In addition to the infrastructure for generating the events, one module that
+is a consumer of these events has been implemented.
+
+    Asterisk trunk was recently updated to include support for dynamic logger
+levels.  This module takes advantage of this functionality to create a
+custom "security" logger level.  Then, when this module is in use, logger.conf
+can be configured to put security events into a file:
+
+\begin{verbatim}
+    security_log => security
+\end{verbatim}
+
+    The content of this file is a well defined and easily interpretable
+format for external scripts to read and act upon.  The definition for the format
+of the log file is described later in this chapter.
+
+\section{Events to Log}
+
+\begin{verbatim}
+(-) required
+(+) optional
+
+Invalid Account ID
+  (-) Local address family/IP address/port/transport
+  (-) Remote address family/IP address/port/transport
+  (-) Service (SIP, AMI, IAX2, ...)
+  (-) System Name
+  (+) Module
+  (+) Account ID (username, etc)
+  (+) Session ID (CallID, etc)
+  (+) Session timestamp (required if Session ID present)
+  (-) Event timestamp (sub-second precision)
+
+Failed ACL match
+  -> everything from invalid account ID
+  (+) Name of ACL (when we have named ACLs)
+
+Invalid Challenge/Response
+  -> everything from invalid account ID
+  (-) Challenge
+  (-) Response
+  (-) Expected Response
+
+Invalid Password
+  -> everything from invalid account ID
+
+Successful Authentication
+  -> informational event
+  -> everything from invalid account ID
+
+Invalid formatting of Request
+  -> everything from invalid account ID
+  -> account ID optional
+  (-) Request Type
+  (+) Request parameters
+
+Session Limit Reached (such as a call limit)
+  -> everything from invalid account ID
+
+Memory Limit Reached
+  -> everything from invalid account ID
+
+Maximum Load Average Reached
+  -> everything from invalid account ID
+
+Request Not Allowed
+  -> everything from invalid account ID
+  (-) Request Type
+  (+) Request parameters
+
+Request Not Supported
+  -> everything from invalid account ID
+  (-) Request Type
+
+Authentication Method Not Allowed
+  -> everything from invalid account ID
+  (-) Authentication Method attempted
+
+In dialog message from unexpected host
+  -> everything from invalid account ID
+  (-) expected host
+\end{verbatim}
+
+\section{Security Log File Format}
+
+    The beginning of each line in the log file is the same as it is for other
+logger levels within Asterisk.
+
+\begin{verbatim}
+    [Feb 11 07:57:03] SECURITY[23736] res_security_log.c: <...>
+\end{verbatim}
+
+    The part of the log entry identified by <...> is where the security event
+content resides.  The security event content is a comma separated list
+of key value pairs.  The key is the information element type, and the value is a
+quoted string that contains the associated meta data for that information
+element.  Any embedded quotes within the content are escaped with a
+backslash.
+
+\begin{verbatim}
+    INFORMATION_ELEMENT_1="IE1 content",INFORMATION_ELEMENT_2="IE2 content"
+\end{verbatim}
+
+The following table includes potential information elements and what the
+associated content looks like:
+
+\begin{verbatim}
+IE: SecurityEvent
+Content: This is the security event sub-type.
+Values: FailedACL, InvalidAccountID, SessionLimit, MemoryLimit, LoadAverageLimit,
+        RequestNotSupported, RequestNotAllowed, AuthMethodNotAllowed,
+        ReqBadFormat, UnexpectedAddress, ChallengeResponseFailed,
+        InvalidPassword
+
+IE: EventVersion
+Content: This is a numeric value that indicates when updates are made to the
+         content of the event.
+Values: Monotonically increasing integer, starting at 1
+
+IE: Service
+Content: This is the Asterisk service that generated the event.
+Values: TEST, SIP, AMI
+
+IE: Module
+Content: This is the Asterisk module that generated the event.
+Values: chan_sip
+
+IE: AccountID
+Content: This is a string used to identify the account associated with the
+         event.  In most cases, this would be a username.
+
+IE: SessionID
+Content: This is a string used to identify the session associated with the
+         event.  The format of the session identifier is specific to the
+         service.  In the case of SIP, this would be the Call-ID.
+
+IE: SessionTV
+Content: The time that the session associated with the SessionID started.
+Values: <seconds>-<microseconds> since epoch
+
+IE: ACLName
+Content: This is a string that identifies which named ACL is associated with
+         this event.
+
+IE: LocalAddress
+Content: This is the local address that was contacted for the related event.
+Values: <Address Family>/<Transport>/<Address>/<Port>
+Examples:
+     -> IPV4/UDP/192.168.1.1/5060
+     -> IPV4/TCP/192.168.1.1/5038
+
+IE: RemoteAddress
+Content: This is the remote address associated with the event.
+Examples:
+     -> IPV4/UDP/192.168.1.2/5060
+     -> IPV4/TCP/192.168.1.2/5038
+
+IE: ExpectedAddress
+Content: This is the address that was expected to be the remote address.
+Examples:
+     -> IPV4/UDP/192.168.1.2/5060
+     -> IPV4/TCP/192.168.1.2/5038
+
+IE: EventTV
+Content: This is the timestamp of when the event occurred.
+Values: <seconds>-<microseconds> since epoch
+
+IE: RequestType
+Content: This is a service specific string that represents the invalid request
+
+IE: RequestParams
+Content: This is a service specific string that represents relevant parameters
+         given with a request that was considered invalid.
+
+IE: AuthMethod
+Content: This is a service specific string that represents an authentication
+         method that was used or requested.
+
+IE: Challenge
+Content: This is a service specific string that represents the challenge
+         provided to a user attempting challenge/response authentication.
+
+IE: Response
+Content: This is a service specific string that represents the response
+         received from a user attempting challenge/response authentication.
+
+IE: ExpectedResponse
+Content: This is a service specific string that represents the response
+         that was expected to be received from a user attempting
+         challenge/response authentication.
+
+\end{verbatim}
+
diff --git a/include/asterisk/event_defs.h b/include/asterisk/event_defs.h
index 99edb6f5599bdfd754fa1380a2753b355dc93655..3779dac73cbe1e05b79ce3a6c4777da90a650b91 100644
--- a/include/asterisk/event_defs.h
+++ b/include/asterisk/event_defs.h
@@ -49,58 +49,60 @@ enum ast_event_type {
 	 *  directly, in general.  Use AST_EVENT_DEVICE_STATE instead. */
 	AST_EVENT_DEVICE_STATE_CHANGE = 0x06,
 	/*! Channel Event Logging events */
-	AST_EVENT_CEL = 0x07,
+	AST_EVENT_CEL                 = 0x07,
+	/*! A report of a security related event (see security_events.h) */
+	AST_EVENT_SECURITY            = 0x08,
 	/*! Number of event types.  This should be the last event type + 1 */
-	AST_EVENT_TOTAL        = 0x08,
+	AST_EVENT_TOTAL               = 0x09,
 };
 
 /*! \brief Event Information Element types */
 enum ast_event_ie_type {
 	/*! Used to terminate the arguments to event functions */
-	AST_EVENT_IE_END       = -1,
+	AST_EVENT_IE_END                 = -1,
 
 	/*! 
 	 * \brief Number of new messages
 	 * Used by: AST_EVENT_MWI 
 	 * Payload type: UINT
 	 */
-	AST_EVENT_IE_NEWMSGS   = 0x01,
+	AST_EVENT_IE_NEWMSGS             = 0x0001,
 	/*! 
 	 * \brief Number of
 	 * Used by: AST_EVENT_MWI 
 	 * Payload type: UINT
 	 */
-	AST_EVENT_IE_OLDMSGS   = 0x02,
+	AST_EVENT_IE_OLDMSGS             = 0x0002,
 	/*! 
 	 * \brief Mailbox name \verbatim (mailbox[@context]) \endverbatim
 	 * Used by: AST_EVENT_MWI 
 	 * Payload type: STR
 	 */
-	AST_EVENT_IE_MAILBOX   = 0x03,
+	AST_EVENT_IE_MAILBOX             = 0x0003,
 	/*! 
 	 * \brief Unique ID
 	 * Used by: AST_EVENT_SUB, AST_EVENT_UNSUB
 	 * Payload type: UINT
 	 */
-	AST_EVENT_IE_UNIQUEID  = 0x04,
+	AST_EVENT_IE_UNIQUEID            = 0x0004,
 	/*! 
 	 * \brief Event type 
 	 * Used by: AST_EVENT_SUB, AST_EVENT_UNSUB
 	 * Payload type: UINT
 	 */
-	AST_EVENT_IE_EVENTTYPE = 0x05,
+	AST_EVENT_IE_EVENTTYPE           = 0x0005,
 	/*!
 	 * \brief Hint that someone cares that an IE exists
 	 * Used by: AST_EVENT_SUB
 	 * Payload type: UINT (ast_event_ie_type)
 	 */
-	AST_EVENT_IE_EXISTS    = 0x6,
+	AST_EVENT_IE_EXISTS              = 0x0006,
 	/*!
 	 * \brief Device Name
 	 * Used by AST_EVENT_DEVICE_STATE_CHANGE
 	 * Payload type: STR
 	 */
-	AST_EVENT_IE_DEVICE    = 0x07,
+	AST_EVENT_IE_DEVICE              = 0x0007,
 	/*!
 	 * \brief Generic State IE
 	 * Used by AST_EVENT_DEVICE_STATE_CHANGE
@@ -108,162 +110,181 @@ enum ast_event_ie_type {
 	 * The actual state values depend on the event which
 	 * this IE is a part of.
 	 */
-	 AST_EVENT_IE_STATE    = 0x08,
+	 AST_EVENT_IE_STATE              = 0x0008,
 	 /*!
 	  * \brief Context IE
 	  * Used by AST_EVENT_MWI
 	  * Payload type: str
 	  */
-	 AST_EVENT_IE_CONTEXT  = 0x09,
+	 AST_EVENT_IE_CONTEXT            = 0x0009,
 	/*! 
 	 * \brief Channel Event Type
 	 * Used by: AST_EVENT_CEL
 	 * Payload type: UINT
 	 */
-	AST_EVENT_IE_CEL_EVENT_TYPE = 0x0a,
+	AST_EVENT_IE_CEL_EVENT_TYPE      = 0x000a,
 	/*! 
 	 * \brief Channel Event Time (seconds)
 	 * Used by: AST_EVENT_CEL
 	 * Payload type: UINT
 	 */
-	AST_EVENT_IE_CEL_EVENT_TIME = 0x0b,
+	AST_EVENT_IE_CEL_EVENT_TIME      = 0x000b,
 	/*! 
 	 * \brief Channel Event Time (micro-seconds)
 	 * Used by: AST_EVENT_CEL
 	 * Payload type: UINT
 	 */
-	AST_EVENT_IE_CEL_EVENT_TIME_USEC = 0x0c,
+	AST_EVENT_IE_CEL_EVENT_TIME_USEC = 0x000c,
 	/*! 
 	 * \brief Channel Event User Event Name
 	 * Used by: AST_EVENT_CEL
 	 * Payload type: STR
 	 */
-	AST_EVENT_IE_CEL_USEREVENT_NAME = 0x0d,
+	AST_EVENT_IE_CEL_USEREVENT_NAME  = 0x000d,
 	/*! 
 	 * \brief Channel Event CID name
 	 * Used by: AST_EVENT_CEL
 	 * Payload type: STR
 	 */
-	AST_EVENT_IE_CEL_CIDNAME = 0x0e,
+	AST_EVENT_IE_CEL_CIDNAME         = 0x000e,
 	/*! 
 	 * \brief Channel Event CID num
 	 * Used by: AST_EVENT_CEL
 	 * Payload type: STR
 	 */
-	AST_EVENT_IE_CEL_CIDNUM = 0x0f,
+	AST_EVENT_IE_CEL_CIDNUM          = 0x000f,
 	/*! 
 	 * \brief Channel Event extension name
 	 * Used by: AST_EVENT_CEL
 	 * Payload type: STR
 	 */
-	AST_EVENT_IE_CEL_EXTEN = 0x10,
+	AST_EVENT_IE_CEL_EXTEN           = 0x0010,
 	/*! 
 	 * \brief Channel Event context name
 	 * Used by: AST_EVENT_CEL
 	 * Payload type: STR
 	 */
-	AST_EVENT_IE_CEL_CONTEXT = 0x11,
+	AST_EVENT_IE_CEL_CONTEXT         = 0x0011,
 	/*! 
 	 * \brief Channel Event channel name
 	 * Used by: AST_EVENT_CEL
 	 * Payload type: STR
 	 */
-	AST_EVENT_IE_CEL_CHANNAME = 0x12,
+	AST_EVENT_IE_CEL_CHANNAME        = 0x0012,
 	/*! 
 	 * \brief Channel Event app name
 	 * Used by: AST_EVENT_CEL
 	 * Payload type: STR
 	 */
-	AST_EVENT_IE_CEL_APPNAME = 0x13,
+	AST_EVENT_IE_CEL_APPNAME         = 0x0013,
 	/*! 
 	 * \brief Channel Event app args/data
 	 * Used by: AST_EVENT_CEL
 	 * Payload type: STR
 	 */
-	AST_EVENT_IE_CEL_APPDATA = 0x14,
+	AST_EVENT_IE_CEL_APPDATA         = 0x0014,
 	/*! 
 	 * \brief Channel Event AMA flags
 	 * Used by: AST_EVENT_CEL
 	 * Payload type: UINT
 	 */
-	AST_EVENT_IE_CEL_AMAFLAGS = 0x15,
+	AST_EVENT_IE_CEL_AMAFLAGS        = 0x0015,
 	/*! 
 	 * \brief Channel Event AccountCode
 	 * Used by: AST_EVENT_CEL
 	 * Payload type: STR
 	 */
-	AST_EVENT_IE_CEL_ACCTCODE = 0x16,
+	AST_EVENT_IE_CEL_ACCTCODE        = 0x0016,
 	/*! 
 	 * \brief Channel Event UniqueID
 	 * Used by: AST_EVENT_CEL
 	 * Payload type: STR
 	 */
-	AST_EVENT_IE_CEL_UNIQUEID = 0x17,
+	AST_EVENT_IE_CEL_UNIQUEID        = 0x0017,
 	/*! 
 	 * \brief Channel Event Userfield
 	 * Used by: AST_EVENT_CEL
 	 * Payload type: STR
 	 */
-	AST_EVENT_IE_CEL_USERFIELD = 0x18,
+	AST_EVENT_IE_CEL_USERFIELD       = 0x0018,
 	/*! 
 	 * \brief Channel Event CID ANI field
 	 * Used by: AST_EVENT_CEL
 	 * Payload type: STR
 	 */
-	AST_EVENT_IE_CEL_CIDANI = 0x19,
+	AST_EVENT_IE_CEL_CIDANI          = 0x0019,
 	/*! 
 	 * \brief Channel Event CID RDNIS field
 	 * Used by: AST_EVENT_CEL
 	 * Payload type: STR
 	 */
-	AST_EVENT_IE_CEL_CIDRDNIS = 0x1a,
+	AST_EVENT_IE_CEL_CIDRDNIS        = 0x001a,
 	/*! 
 	 * \brief Channel Event CID dnid
 	 * Used by: AST_EVENT_CEL
 	 * Payload type: STR
 	 */
-	AST_EVENT_IE_CEL_CIDDNID = 0x1b,
+	AST_EVENT_IE_CEL_CIDDNID         = 0x001b,
 	/*! 
 	 * \brief Channel Event Peer -- for Things involving multiple channels, like BRIDGE
 	 * Used by: AST_EVENT_CEL
 	 * Payload type: STR
 	 */
-	AST_EVENT_IE_CEL_PEER = 0x1c,
+	AST_EVENT_IE_CEL_PEER            = 0x001c,
 	/*! 
 	 * \brief Channel Event LinkedID
 	 * Used by: AST_EVENT_CEL
 	 * Payload type: STR
 	 */
-	AST_EVENT_IE_CEL_LINKEDID = 0x1d,
+	AST_EVENT_IE_CEL_LINKEDID        = 0x001d,
 	/*! 
 	 * \brief Channel Event peeraccount
 	 * Used by: AST_EVENT_CEL
 	 * Payload type: STR
 	 */
-	AST_EVENT_IE_CEL_PEERACCT = 0x1e,
+	AST_EVENT_IE_CEL_PEERACCT        = 0x001e,
 	/*! 
 	 * \brief Channel Event extra data
 	 * Used by: AST_EVENT_CEL
 	 * Payload type: STR
 	 */
-	AST_EVENT_IE_CEL_EXTRA = 0x1f,
+	AST_EVENT_IE_CEL_EXTRA           = 0x001f,
 	/*!
 	 * \brief Description
 	 * Used by: AST_EVENT_SUB, AST_EVENT_UNSUB
 	 * Payload type: STR
 	 */
-	AST_EVENT_IE_DESCRIPTION = 0x20,
+	AST_EVENT_IE_DESCRIPTION         = 0x0020,
 	/*!
 	 * \brief Entity ID
 	 * Used by All events
 	 * Payload type: RAW
 	 * This IE indicates which server the event originated from
 	 */
-	AST_EVENT_IE_EID      = 0x21,
+	AST_EVENT_IE_EID                 = 0x0021,
+	AST_EVENT_IE_SECURITY_EVENT      = 0x0022,
+	AST_EVENT_IE_EVENT_VERSION       = 0x0023,
+	AST_EVENT_IE_SERVICE             = 0x0024,
+	AST_EVENT_IE_MODULE              = 0x0025,
+	AST_EVENT_IE_ACCOUNT_ID          = 0x0026,
+	AST_EVENT_IE_SESSION_ID          = 0x0027,
+	AST_EVENT_IE_SESSION_TV          = 0x0028,
+	AST_EVENT_IE_ACL_NAME            = 0x0029,
+	AST_EVENT_IE_LOCAL_ADDR          = 0x002a,
+	AST_EVENT_IE_REMOTE_ADDR         = 0x002b,
+	AST_EVENT_IE_EVENT_TV            = 0x002c,
+	AST_EVENT_IE_REQUEST_TYPE        = 0x002d,
+	AST_EVENT_IE_REQUEST_PARAMS      = 0x002e,
+	AST_EVENT_IE_AUTH_METHOD         = 0x002f,
+	AST_EVENT_IE_SEVERITY            = 0x0030,
+	AST_EVENT_IE_EXPECTED_ADDR       = 0x0031,
+	AST_EVENT_IE_CHALLENGE           = 0x0032,
+	AST_EVENT_IE_RESPONSE            = 0x0033,
+	AST_EVENT_IE_EXPECTED_RESPONSE   = 0x0034,
+	/*! \brief Must be the last IE value +1 */
+	AST_EVENT_IE_TOTAL               = 0x0035,
 };
 
-#define AST_EVENT_IE_MAX AST_EVENT_IE_EID
-
 /*!
  * \brief Payload types for event information elements
  */
diff --git a/include/asterisk/security_events.h b/include/asterisk/security_events.h
new file mode 100644
index 0000000000000000000000000000000000000000..c15d04f0e0e8a6f20b029a35b68ac6912419d5bd
--- /dev/null
+++ b/include/asterisk/security_events.h
@@ -0,0 +1,114 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2009, Digium, Inc.
+ *
+ * Russell Bryant <russell@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Security Event Reporting API
+ *
+ * \author Russell Bryant <russell@digium.com>
+ */
+
+#ifndef __AST_SECURITY_EVENTS_H__
+#define __AST_SECURITY_EVENTS_H__
+
+#include "asterisk/event.h"
+
+/* Data structure definitions */
+#include "asterisk/security_events_defs.h"
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+/*!
+ * \brief Report a security event
+ *
+ * \param[in] sec security event data.  Callers of this function should never
+ *            declare an instance of ast_security_event_common directly.  The
+ *            argument should be an instance of a specific security event
+ *            descriptor which has ast_security_event_common at the very
+ *            beginning.
+ *
+ * \retval 0 success
+ * \retval non-zero failure
+ */
+int ast_security_event_report(const struct ast_security_event_common *sec);
+
+struct ast_security_event_ie_type {
+	enum ast_event_ie_type ie_type;
+	/*! \brief For internal usage */
+	size_t offset;
+};
+
+/*!
+ * \brief Get the list of required IEs for a given security event sub-type
+ *
+ * \param[in] event_type security event sub-type
+ *
+ * \retval NULL invalid event_type
+ * \retval non-NULL An array terminated with the value AST_EVENT_IE_END
+ *
+ * \since 1.6.3
+ */
+const struct ast_security_event_ie_type *ast_security_event_get_required_ies(
+		const enum ast_security_event_type event_type);
+
+/*!
+ * \brief Get the list of optional IEs for a given security event sub-type
+ *
+ * \param[in] event_type security event sub-type
+ *
+ * \retval NULL invalid event_type
+ * \retval non-NULL An array terminated with the value AST_EVENT_IE_END
+ *
+ * \since 1.6.3
+ */
+const struct ast_security_event_ie_type *ast_security_event_get_optional_ies(
+		const enum ast_security_event_type event_type);
+
+/*!
+ * \brief Get the name of a security event sub-type
+ *
+ * \param[in] event_type security event sub-type
+ *
+ * \retval NULL if event_type is invalid
+ * \retval non-NULL the name of the security event type
+ *
+ * \since 1.6.3
+ */
+const char *ast_security_event_get_name(const enum ast_security_event_type event_type);
+
+/*!
+ * \brief Get the name of a security event severity
+ *
+ * \param[in] severity security event severity
+ *
+ * \retval NULL if severity is invalid
+ * \retval non-NULL the name of the security event severity
+ *
+ * \since 1.6.3
+ */
+const char *ast_security_event_severity_get_name(
+		const enum ast_security_event_severity severity);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* __AST_SECURITY_EVENTS_H__ */
diff --git a/include/asterisk/security_events_defs.h b/include/asterisk/security_events_defs.h
new file mode 100644
index 0000000000000000000000000000000000000000..e39cf312d79291cd98b22946b595af9d911d61c5
--- /dev/null
+++ b/include/asterisk/security_events_defs.h
@@ -0,0 +1,470 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2009, Digium, Inc.
+ *
+ * Russell Bryant <russell@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Security Event Reporting Data Structures
+ *
+ * \author Russell Bryant <russell@digium.com>
+ */
+
+#ifndef __AST_SECURITY_EVENTS_DEFS_H__
+#define __AST_SECURITY_EVENTS_DEFS_H__
+
+#include "asterisk/network.h"
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+/*!
+ * \brief Security event types
+ *
+ * AST_EVENT_SECURITY is the event type of an ast_event generated as a security
+ * event.  The event will have an information element of type
+ * AST_EVENT_IE_SECURITY_EVENT which identifies the security event sub-type.
+ * This enum defines the possible values for this sub-type.
+ */
+enum ast_security_event_type {
+	/*!
+	 * \brief Failed ACL
+	 *
+	 * This security event should be generated when an incoming request
+	 * was made, but was denied due to configured IP address access control
+	 * lists.
+	 */
+	AST_SECURITY_EVENT_FAILED_ACL,
+	/*!
+	 * \brief Invalid Account ID
+	 *
+	 * This event is used when an invalid account identifier is supplied
+	 * during authentication.  For example, if an invalid username is given,
+	 * this event should be used.
+	 */
+	AST_SECURITY_EVENT_INVAL_ACCT_ID,
+	/*!
+	 * \brief Session limit reached
+	 *
+	 * A request has been denied because a configured session limit has been
+	 * reached, such as a call limit.
+	 */
+	AST_SECURITY_EVENT_SESSION_LIMIT,
+	/*!
+	 * \brief Memory limit reached
+	 *
+	 * A request has been denied because a configured memory limit has been
+	 * reached.
+	 */
+	AST_SECURITY_EVENT_MEM_LIMIT,
+	/*!
+	 * \brief Load Average limit reached
+	 *
+	 * A request has been denied because a configured load average limit has been
+	 * reached.
+	 */
+	AST_SECURITY_EVENT_LOAD_AVG,
+	/*!
+	 * \brief A request was made that we understand, but do not support
+	 */
+	AST_SECURITY_EVENT_REQ_NO_SUPPORT,
+	/*!
+	 * \brief A request was made that is not allowed
+	 */
+	AST_SECURITY_EVENT_REQ_NOT_ALLOWED,
+	/*!
+	 * \brief The attempted authentication method is not allowed
+	 */
+	AST_SECURITY_EVENT_AUTH_METHOD_NOT_ALLOWED,
+	/*!
+	 * \brief Request received with bad formatting
+	 */
+	AST_SECURITY_EVENT_REQ_BAD_FORMAT,
+	/*!
+	 * \brief FYI FWIW, Successful authentication has occurred
+	 */
+	AST_SECURITY_EVENT_SUCCESSFUL_AUTH,
+	/*!
+	 * \brief An unexpected source address was seen for a session in progress
+	 */
+	AST_SECURITY_EVENT_UNEXPECTED_ADDR,
+	/*!
+	 * \brief An attempt at challenge/response authentication failed
+	 */
+	AST_SECURITY_EVENT_CHAL_RESP_FAILED,
+	/*!
+	 * \brief An attempt at basic password authentication failed
+	 */
+	AST_SECURITY_EVENT_INVAL_PASSWORD,
+	/* \brief This _must_ stay at the end. */
+	AST_SECURITY_EVENT_NUM_TYPES
+};
+
+/*!
+ * \brief the severity of a security event
+ *
+ * This is defined as a bit field to make it easy for consumers of the API to
+ * subscribe to any combination of the defined severity levels.
+ *
+ * XXX \todo Do we need any more levels here?
+ */
+enum ast_security_event_severity {
+	/*! \brief Informational event, not something that has gone wrong */
+	AST_SECURITY_EVENT_SEVERITY_INFO  = (1 << 0),
+	/*! \brief Something has gone wrong */
+	AST_SECURITY_EVENT_SEVERITY_ERROR = (1 << 1),
+};
+
+/*!
+ * \brief Transport types
+ */
+enum ast_security_event_transport_type {
+	AST_SECURITY_EVENT_TRANSPORT_UDP,
+	AST_SECURITY_EVENT_TRANSPORT_TCP,
+	AST_SECURITY_EVENT_TRANSPORT_TLS,
+};
+
+#define AST_SEC_EVT(e) ((struct ast_security_event_common *) e)
+
+struct ast_security_event_ipv4_addr {
+	const struct sockaddr_in *sin;
+	enum ast_security_event_transport_type transport;
+};
+
+/*!
+ * \brief Common structure elements
+ *
+ * This is the structure header for all event descriptor structures defined
+ * below.  The contents of this structure are very important and must not
+ * change.  Even though these structures are exposed via a public API, we have
+ * a version field that can be used to ensure ABI safety.  If the event
+ * descriptors need to be changed or updated in the future, we can safely do
+ * so and can detect ABI changes at runtime.
+ */
+struct ast_security_event_common {
+	/*! \brief The security event sub-type */
+	enum ast_security_event_type event_type;
+	/*! \brief security event version */
+	uint32_t version;
+	/*!
+	 * \brief Service that generated the event
+	 * \note Always required
+	 *
+	 * Examples: "SIP", "AMI"
+	 */
+	const char *service;
+	/*!
+	 * \brief Module, Normally the AST_MODULE define
+	 * \note Always optional
+	 */
+	const char *module;
+	/*!
+	 * \brief Account ID, specific to the service type
+	 * \note optional/required, depending on event type
+	 */
+	const char *account_id;
+	/*!
+	 * \brief Session ID, specific to the service type
+	 * \note Always required
+	 */
+	const char *session_id;
+	/*!
+	 * \brief Session timeval, when the session started
+	 * \note Always optional
+	 */
+	const struct timeval *session_tv;
+	/*!
+	 * \brief Local address the request came in on
+	 * \note Always required
+	 */
+	struct ast_security_event_ipv4_addr local_addr;
+	/*!
+	 * \brief Remote address the request came from
+	 * \note Always required
+	 */
+	struct ast_security_event_ipv4_addr remote_addr;
+};
+
+/*!
+ * \brief Checking against an IP access control list failed
+ */
+struct ast_security_event_failed_acl {
+	/*!
+	 * \brief Event descriptor version
+	 * \note This _must_ be changed if this event descriptor is changed.
+	 */
+	#define AST_SECURITY_EVENT_FAILED_ACL_VERSION 1
+	/*!
+	 * \brief Common security event descriptor elements
+	 * \note Account ID required
+	 */
+	struct ast_security_event_common common;
+	/*!
+	 * \brief ACL name, identifies which ACL was hit
+	 * \note optional
+	 */
+	const char *acl_name;
+};
+
+/*!
+ * \brief Invalid account ID specified (invalid username, for example)
+ */
+struct ast_security_event_inval_acct_id {
+	/*!
+	 * \brief Event descriptor version
+	 * \note This _must_ be changed if this event descriptor is changed.
+	 */
+	#define AST_SECURITY_EVENT_INVAL_ACCT_ID_VERSION 1
+	/*!
+	 * \brief Common security event descriptor elements
+	 * \note Account ID required
+	 */
+	struct ast_security_event_common common;
+};
+
+/*!
+ * \brief Request denied because of a session limit
+ */
+struct ast_security_event_session_limit {
+	/*!
+	 * \brief Event descriptor version
+	 * \note This _must_ be changed if this event descriptor is changed.
+	 */
+	#define AST_SECURITY_EVENT_SESSION_LIMIT_VERSION 1
+	/*!
+	 * \brief Common security event descriptor elements
+	 * \note Account ID required
+	 */
+	struct ast_security_event_common common;
+};
+
+/*!
+ * \brief Request denied because of a memory limit
+ */
+struct ast_security_event_mem_limit {
+	/*!
+	 * \brief Event descriptor version
+	 * \note This _must_ be changed if this event descriptor is changed.
+	 */
+	#define AST_SECURITY_EVENT_MEM_LIMIT_VERSION 1
+	/*!
+	 * \brief Common security event descriptor elements
+	 * \note Account ID required
+	 */
+	struct ast_security_event_common common;
+};
+
+/*!
+ * \brief Request denied because of a load average limit
+ */
+struct ast_security_event_load_avg {
+	/*!
+	 * \brief Event descriptor version
+	 * \note This _must_ be changed if this event descriptor is changed.
+	 */
+	#define AST_SECURITY_EVENT_LOAD_AVG_VERSION 1
+	/*!
+	 * \brief Common security event descriptor elements
+	 * \note Account ID required
+	 */
+	struct ast_security_event_common common;
+};
+
+/*!
+ * \brief Request denied because we don't support it
+ */
+struct ast_security_event_req_no_support {
+	/*!
+	 * \brief Event descriptor version
+	 * \note This _must_ be changed if this event descriptor is changed.
+	 */
+	#define AST_SECURITY_EVENT_REQ_NO_SUPPORT_VERSION 1
+	/*!
+	 * \brief Common security event descriptor elements
+	 * \note Account ID required
+	 */
+	struct ast_security_event_common common;
+	/*!
+	 * \brief Request type that was made
+	 * \note required
+	 */
+	const char *request_type;
+};
+
+/*!
+ * \brief Request denied because it's not allowed
+ */
+struct ast_security_event_req_not_allowed {
+	/*!
+	 * \brief Event descriptor version
+	 * \note This _must_ be changed if this event descriptor is changed.
+	 */
+	#define AST_SECURITY_EVENT_REQ_NOT_ALLOWED_VERSION 1
+	/*!
+	 * \brief Common security event descriptor elements
+	 * \note Account ID required
+	 */
+	struct ast_security_event_common common;
+	/*!
+	 * \brief Request type that was made
+	 * \note required
+	 */
+	const char *request_type;
+	/*!
+	 * \brief Request type that was made
+	 * \note optional
+	 */
+	const char *request_params;
+};
+
+/*!
+ * \brief Auth method used not allowed
+ */
+struct ast_security_event_auth_method_not_allowed {
+	/*!
+	 * \brief Event descriptor version
+	 * \note This _must_ be changed if this event descriptor is changed.
+	 */
+	#define AST_SECURITY_EVENT_AUTH_METHOD_NOT_ALLOWED_VERSION 1
+	/*!
+	 * \brief Common security event descriptor elements
+	 * \note Account ID required
+	 */
+	struct ast_security_event_common common;
+	/*!
+	 * \brief Auth method attempted
+	 * \note required
+	 */
+	const char *auth_method;
+};
+
+/*!
+ * \brief Invalid formatting of request
+ */
+struct ast_security_event_req_bad_format {
+	/*!
+	 * \brief Event descriptor version
+	 * \note This _must_ be changed if this event descriptor is changed.
+	 */
+	#define AST_SECURITY_EVENT_REQ_BAD_FORMAT_VERSION 1
+	/*!
+	 * \brief Common security event descriptor elements
+	 * \note Account ID optional
+	 */
+	struct ast_security_event_common common;
+	/*!
+	 * \brief Request type that was made
+	 * \note required
+	 */
+	const char *request_type;
+	/*!
+	 * \brief Request type that was made
+	 * \note optional
+	 */
+	const char *request_params;
+};
+
+/*!
+ * \brief Successful authentication
+ */
+struct ast_security_event_successful_auth {
+	/*!
+	 * \brief Event descriptor version
+	 * \note This _must_ be changed if this event descriptor is changed.
+	 */
+	#define AST_SECURITY_EVENT_SUCCESSFUL_AUTH_VERSION 1
+	/*!
+	 * \brief Common security event descriptor elements
+	 * \note Account ID required
+	 */
+	struct ast_security_event_common common;
+};
+
+/*!
+ * \brief Unexpected source address for a session in progress
+ */
+struct ast_security_event_unexpected_addr {
+	/*!
+	 * \brief Event descriptor version
+	 * \note This _must_ be changed if this event descriptor is changed.
+	 */
+	#define AST_SECURITY_EVENT_UNEXPECTED_ADDR_VERSION 1
+	/*!
+	 * \brief Common security event descriptor elements
+	 * \note Account ID required
+	 */
+	struct ast_security_event_common common;
+	/*!
+	 * \brief Expected remote address
+	 * \note required
+	 */
+	struct ast_security_event_ipv4_addr expected_addr;
+};
+
+/*!
+ * \brief An attempt at challenge/response auth failed
+ */
+struct ast_security_event_chal_resp_failed {
+	/*!
+	 * \brief Event descriptor version
+	 * \note This _must_ be changed if this event descriptor is changed.
+	 */
+	#define AST_SECURITY_EVENT_CHAL_RESP_FAILED_VERSION 1
+	/*!
+	 * \brief Common security event descriptor elements
+	 * \note Account ID required
+	 */
+	struct ast_security_event_common common;
+	/*!
+	 * \brief Challenge provided
+	 * \note required
+	 */
+	const char *challenge;
+	/*!
+	 * \brief Response received
+	 * \note required
+	 */
+	const char *response;
+	/*!
+	 * \brief Response expected to be received
+	 * \note required
+	 */
+	const char *expected_response;
+};
+
+/*!
+ * \brief An attempt at basic password auth failed
+ */
+struct ast_security_event_inval_password {
+	/*!
+	 * \brief Event descriptor version
+	 * \note This _must_ be changed if this event descriptor is changed.
+	 */
+	#define AST_SECURITY_EVENT_INVAL_PASSWORD_VERSION 1
+	/*!
+	 * \brief Common security event descriptor elements
+	 * \note Account ID required
+	 */
+	struct ast_security_event_common common;
+};
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* __AST_SECURITY_EVENTS_DEFS_H__ */
diff --git a/main/event.c b/main/event.c
index 6a70a674184e7d37f773ebd9c2e58ca48c8a5080..44c65714d3cc2c384a14effc4d7897bd3327a57b 100644
--- a/main/event.c
+++ b/main/event.c
@@ -183,63 +183,77 @@ static struct {
 };
 
 /*!
- * The index of each entry _must_ match the event type number!
+ * \brief Event Names
  */
-static struct event_name {
-	enum ast_event_type type;
-	const char *name;
-} event_names[] = {
-	{ 0, "" },
-	{ AST_EVENT_CUSTOM,              "Custom" },
-	{ AST_EVENT_MWI,                 "MWI" },
-	{ AST_EVENT_SUB,                 "Subscription" },
-	{ AST_EVENT_UNSUB,               "Unsubscription" },
-	{ AST_EVENT_DEVICE_STATE,        "DeviceState" },
-	{ AST_EVENT_DEVICE_STATE_CHANGE, "DeviceStateChange" },
-	{ AST_EVENT_CEL, "CEL" },
+static const char * const event_names[AST_EVENT_TOTAL] = {
+	[AST_EVENT_CUSTOM]              = "Custom",
+	[AST_EVENT_MWI]                 = "MWI",
+	[AST_EVENT_SUB]                 = "Subscription",
+	[AST_EVENT_UNSUB]               = "Unsubscription",
+	[AST_EVENT_DEVICE_STATE]        = "DeviceState",
+	[AST_EVENT_DEVICE_STATE_CHANGE] = "DeviceStateChange",
+	[AST_EVENT_CEL]                 = "CEL",
+	[AST_EVENT_SECURITY]            = "Security",
 };
 
 /*!
- * The index of each entry _must_ match the event ie number!
+ * \brief IE payload types and names
  */
-static struct ie_map {
-	enum ast_event_ie_type ie_type;
+static const struct ie_map {
 	enum ast_event_ie_pltype ie_pltype;
 	const char *name;
-} ie_maps[] = {
-	{ 0, 0, "" },
-	{ AST_EVENT_IE_NEWMSGS,              AST_EVENT_IE_PLTYPE_UINT, "NewMessages" },
-	{ AST_EVENT_IE_OLDMSGS,              AST_EVENT_IE_PLTYPE_UINT, "OldMessages" },
-	{ AST_EVENT_IE_MAILBOX,              AST_EVENT_IE_PLTYPE_STR,  "Mailbox" },
-	{ AST_EVENT_IE_UNIQUEID,             AST_EVENT_IE_PLTYPE_UINT, "UniqueID" },
-	{ AST_EVENT_IE_EVENTTYPE,            AST_EVENT_IE_PLTYPE_UINT, "EventType" },
-	{ AST_EVENT_IE_EXISTS,               AST_EVENT_IE_PLTYPE_UINT, "Exists" },
-	{ AST_EVENT_IE_DEVICE,               AST_EVENT_IE_PLTYPE_STR,  "Device" },
-	{ AST_EVENT_IE_STATE,                AST_EVENT_IE_PLTYPE_UINT, "State" },
-	{ AST_EVENT_IE_CONTEXT,              AST_EVENT_IE_PLTYPE_STR,  "Context" },
-	{ AST_EVENT_IE_EID,                  AST_EVENT_IE_PLTYPE_RAW,  "EntityID" },
-	{ AST_EVENT_IE_CEL_EVENT_TYPE,       AST_EVENT_IE_PLTYPE_UINT,  "CELEventType" },
-	{ AST_EVENT_IE_CEL_EVENT_TIME,       AST_EVENT_IE_PLTYPE_UINT,  "CELEventTime" },
-	{ AST_EVENT_IE_CEL_EVENT_TIME_USEC,  AST_EVENT_IE_PLTYPE_UINT,  "CELEventTimeUSec" },
-	{ AST_EVENT_IE_CEL_USEREVENT_NAME,   AST_EVENT_IE_PLTYPE_UINT,  "CELUserEventName" },
-	{ AST_EVENT_IE_CEL_CIDNAME,          AST_EVENT_IE_PLTYPE_STR,  "CELCIDName" },
-	{ AST_EVENT_IE_CEL_CIDNUM,           AST_EVENT_IE_PLTYPE_STR,  "CELCIDNum" },
-	{ AST_EVENT_IE_CEL_EXTEN,            AST_EVENT_IE_PLTYPE_STR,  "CELExten" },
-	{ AST_EVENT_IE_CEL_CONTEXT,          AST_EVENT_IE_PLTYPE_STR,  "CELContext" },
-	{ AST_EVENT_IE_CEL_CHANNAME,         AST_EVENT_IE_PLTYPE_STR,  "CELChanName" },
-	{ AST_EVENT_IE_CEL_APPNAME,          AST_EVENT_IE_PLTYPE_STR,  "CELAppName" },
-	{ AST_EVENT_IE_CEL_APPDATA,          AST_EVENT_IE_PLTYPE_STR,  "CELAppData" },
-	{ AST_EVENT_IE_CEL_AMAFLAGS,         AST_EVENT_IE_PLTYPE_STR,  "CELAMAFlags" },
-	{ AST_EVENT_IE_CEL_ACCTCODE,         AST_EVENT_IE_PLTYPE_UINT,  "CELAcctCode" },
-	{ AST_EVENT_IE_CEL_UNIQUEID,         AST_EVENT_IE_PLTYPE_STR,  "CELUniqueID" },
-	{ AST_EVENT_IE_CEL_USERFIELD,        AST_EVENT_IE_PLTYPE_STR,  "CELUserField" },
-	{ AST_EVENT_IE_CEL_CIDANI,           AST_EVENT_IE_PLTYPE_STR,  "CELCIDani" },
-	{ AST_EVENT_IE_CEL_CIDRDNIS,         AST_EVENT_IE_PLTYPE_STR,  "CELCIDrdnis" },
-	{ AST_EVENT_IE_CEL_CIDDNID,          AST_EVENT_IE_PLTYPE_STR,  "CELCIDdnid" },
-	{ AST_EVENT_IE_CEL_PEER,             AST_EVENT_IE_PLTYPE_STR,  "CELPeer" },
-	{ AST_EVENT_IE_CEL_LINKEDID,         AST_EVENT_IE_PLTYPE_STR,  "CELLinkedID" },
-	{ AST_EVENT_IE_CEL_PEERACCT,         AST_EVENT_IE_PLTYPE_STR,  "CELPeerAcct" },
-	{ AST_EVENT_IE_CEL_EXTRA,            AST_EVENT_IE_PLTYPE_STR,  "CELExtra" },
+} ie_maps[AST_EVENT_IE_TOTAL] = {
+	[AST_EVENT_IE_NEWMSGS]             = { AST_EVENT_IE_PLTYPE_UINT, "NewMessages" },
+	[AST_EVENT_IE_OLDMSGS]             = { AST_EVENT_IE_PLTYPE_UINT, "OldMessages" },
+	[AST_EVENT_IE_MAILBOX]             = { AST_EVENT_IE_PLTYPE_STR,  "Mailbox" },
+	[AST_EVENT_IE_UNIQUEID]            = { AST_EVENT_IE_PLTYPE_UINT, "UniqueID" },
+	[AST_EVENT_IE_EVENTTYPE]           = { AST_EVENT_IE_PLTYPE_UINT, "EventType" },
+	[AST_EVENT_IE_EXISTS]              = { AST_EVENT_IE_PLTYPE_UINT, "Exists" },
+	[AST_EVENT_IE_DEVICE]              = { AST_EVENT_IE_PLTYPE_STR,  "Device" },
+	[AST_EVENT_IE_STATE]               = { AST_EVENT_IE_PLTYPE_UINT, "State" },
+	[AST_EVENT_IE_CONTEXT]             = { AST_EVENT_IE_PLTYPE_STR,  "Context" },
+	[AST_EVENT_IE_EID]                 = { AST_EVENT_IE_PLTYPE_RAW,  "EntityID" },
+	[AST_EVENT_IE_CEL_EVENT_TYPE]      = { AST_EVENT_IE_PLTYPE_UINT, "CELEventType" },
+	[AST_EVENT_IE_CEL_EVENT_TIME]      = { AST_EVENT_IE_PLTYPE_UINT, "CELEventTime" },
+	[AST_EVENT_IE_CEL_EVENT_TIME_USEC] = { AST_EVENT_IE_PLTYPE_UINT, "CELEventTimeUSec" },
+	[AST_EVENT_IE_CEL_USEREVENT_NAME]  = { AST_EVENT_IE_PLTYPE_UINT, "CELUserEventName" },
+	[AST_EVENT_IE_CEL_CIDNAME]         = { AST_EVENT_IE_PLTYPE_STR,  "CELCIDName" },
+	[AST_EVENT_IE_CEL_CIDNUM]          = { AST_EVENT_IE_PLTYPE_STR,  "CELCIDNum" },
+	[AST_EVENT_IE_CEL_EXTEN]           = { AST_EVENT_IE_PLTYPE_STR,  "CELExten" },
+	[AST_EVENT_IE_CEL_CONTEXT]         = { AST_EVENT_IE_PLTYPE_STR,  "CELContext" },
+	[AST_EVENT_IE_CEL_CHANNAME]        = { AST_EVENT_IE_PLTYPE_STR,  "CELChanName" },
+	[AST_EVENT_IE_CEL_APPNAME]         = { AST_EVENT_IE_PLTYPE_STR,  "CELAppName" },
+	[AST_EVENT_IE_CEL_APPDATA]         = { AST_EVENT_IE_PLTYPE_STR,  "CELAppData" },
+	[AST_EVENT_IE_CEL_AMAFLAGS]        = { AST_EVENT_IE_PLTYPE_STR,  "CELAMAFlags" },
+	[AST_EVENT_IE_CEL_ACCTCODE]        = { AST_EVENT_IE_PLTYPE_UINT, "CELAcctCode" },
+	[AST_EVENT_IE_CEL_UNIQUEID]        = { AST_EVENT_IE_PLTYPE_STR,  "CELUniqueID" },
+	[AST_EVENT_IE_CEL_USERFIELD]       = { AST_EVENT_IE_PLTYPE_STR,  "CELUserField" },
+	[AST_EVENT_IE_CEL_CIDANI]          = { AST_EVENT_IE_PLTYPE_STR,  "CELCIDani" },
+	[AST_EVENT_IE_CEL_CIDRDNIS]        = { AST_EVENT_IE_PLTYPE_STR,  "CELCIDrdnis" },
+	[AST_EVENT_IE_CEL_CIDDNID]         = { AST_EVENT_IE_PLTYPE_STR,  "CELCIDdnid" },
+	[AST_EVENT_IE_CEL_PEER]            = { AST_EVENT_IE_PLTYPE_STR,  "CELPeer" },
+	[AST_EVENT_IE_CEL_LINKEDID]        = { AST_EVENT_IE_PLTYPE_STR,  "CELLinkedID" },
+	[AST_EVENT_IE_CEL_PEERACCT]        = { AST_EVENT_IE_PLTYPE_STR,  "CELPeerAcct" },
+	[AST_EVENT_IE_CEL_EXTRA]           = { AST_EVENT_IE_PLTYPE_STR,  "CELExtra" },
+	[AST_EVENT_IE_SECURITY_EVENT]      = { AST_EVENT_IE_PLTYPE_STR,  "SecurityEvent" },
+	[AST_EVENT_IE_EVENT_VERSION]       = { AST_EVENT_IE_PLTYPE_UINT, "EventVersion" },
+	[AST_EVENT_IE_SERVICE]             = { AST_EVENT_IE_PLTYPE_STR,  "Service" },
+	[AST_EVENT_IE_MODULE]              = { AST_EVENT_IE_PLTYPE_STR,  "Module" },
+	[AST_EVENT_IE_ACCOUNT_ID]          = { AST_EVENT_IE_PLTYPE_STR,  "AccountID" },
+	[AST_EVENT_IE_SESSION_ID]          = { AST_EVENT_IE_PLTYPE_STR,  "SessionID" },
+	[AST_EVENT_IE_SESSION_TV]          = { AST_EVENT_IE_PLTYPE_STR,  "SessionTV" },
+	[AST_EVENT_IE_ACL_NAME]            = { AST_EVENT_IE_PLTYPE_STR,  "ACLName" },
+	[AST_EVENT_IE_LOCAL_ADDR]          = { AST_EVENT_IE_PLTYPE_STR,  "LocalAddress" },
+	[AST_EVENT_IE_REMOTE_ADDR]         = { AST_EVENT_IE_PLTYPE_STR,  "RemoteAddress" },
+	[AST_EVENT_IE_EVENT_TV]            = { AST_EVENT_IE_PLTYPE_STR,  "EventTV" },
+	[AST_EVENT_IE_REQUEST_TYPE]        = { AST_EVENT_IE_PLTYPE_STR,  "RequestType" },
+	[AST_EVENT_IE_REQUEST_PARAMS]      = { AST_EVENT_IE_PLTYPE_STR,  "RequestParams" },
+	[AST_EVENT_IE_AUTH_METHOD]         = { AST_EVENT_IE_PLTYPE_STR,  "AuthMethod" },
+	[AST_EVENT_IE_SEVERITY]            = { AST_EVENT_IE_PLTYPE_STR,  "Severity" },
+	[AST_EVENT_IE_EXPECTED_ADDR]       = { AST_EVENT_IE_PLTYPE_STR,  "ExpectedAddress" },
+	[AST_EVENT_IE_CHALLENGE]           = { AST_EVENT_IE_PLTYPE_STR,  "Challenge" },
+	[AST_EVENT_IE_RESPONSE]            = { AST_EVENT_IE_PLTYPE_STR,  "Response" },
+	[AST_EVENT_IE_EXPECTED_RESPONSE]   = { AST_EVENT_IE_PLTYPE_STR,  "ExpectedResponse" },
 };
 
 const char *ast_event_get_type_name(const struct ast_event *event)
@@ -248,12 +262,12 @@ const char *ast_event_get_type_name(const struct ast_event *event)
 
 	type = ast_event_get_type(event);
 
-	if (type >= AST_EVENT_TOTAL || type < 0) {
+	if (type < 0 || type >= ARRAY_LEN(event_names)) {
 		ast_log(LOG_ERROR, "Invalid event type - '%d'\n", type);
 		return "";
 	}
 
-	return event_names[type].name;
+	return event_names[type];
 }
 
 int ast_event_str_to_event_type(const char *str, enum ast_event_type *event_type)
@@ -261,10 +275,11 @@ int ast_event_str_to_event_type(const char *str, enum ast_event_type *event_type
 	int i;
 
 	for (i = 0; i < ARRAY_LEN(event_names); i++) {
-		if (strcasecmp(event_names[i].name, str))
+		if (strcasecmp(event_names[i], str)) {
 			continue;
+		}
 
-		*event_type = event_names[i].type;
+		*event_type = i;
 		return 0;
 	}
 
@@ -273,31 +288,21 @@ int ast_event_str_to_event_type(const char *str, enum ast_event_type *event_type
 
 const char *ast_event_get_ie_type_name(enum ast_event_ie_type ie_type)
 {
-	if (ie_type <= 0 || ie_type > AST_EVENT_IE_MAX) {
+	if (ie_type <= 0 || ie_type >= ARRAY_LEN(ie_maps)) {
 		ast_log(LOG_ERROR, "Invalid IE type - '%d'\n", ie_type);
 		return "";
 	}
 
-	if (ie_maps[ie_type].ie_type != ie_type) {
-		ast_log(LOG_ERROR, "The ie type passed in does not match the ie type defined in the ie table.\n");
-		return "";
-	}
-
 	return ie_maps[ie_type].name;
 }
 
 enum ast_event_ie_pltype ast_event_get_ie_pltype(enum ast_event_ie_type ie_type)
 {
-	if (ie_type <= 0 || ie_type > AST_EVENT_IE_MAX) {
+	if (ie_type <= 0 || ie_type >= ARRAY_LEN(ie_maps)) {
 		ast_log(LOG_ERROR, "Invalid IE type - '%d'\n", ie_type);
 		return AST_EVENT_IE_PLTYPE_UNKNOWN;
 	}
 
-	if (ie_maps[ie_type].ie_type != ie_type) {
-		ast_log(LOG_ERROR, "The ie type passed in does not match the ie type defined in the ie table.\n");
-		return AST_EVENT_IE_PLTYPE_UNKNOWN;
-	}
-
 	return ie_maps[ie_type].ie_pltype;
 }
 
@@ -306,10 +311,11 @@ int ast_event_str_to_ie_type(const char *str, enum ast_event_ie_type *ie_type)
 	int i;
 
 	for (i = 0; i < ARRAY_LEN(ie_maps); i++) {
-		if (strcasecmp(ie_maps[i].name, str))
+		if (strcasecmp(ie_maps[i].name, str)) {
 			continue;
+		}
 
-		*ie_type = ie_maps[i].ie_type;
+		*ie_type = i;
 		return 0;
 	}
 
@@ -661,7 +667,7 @@ int ast_event_sub_append_ie_uint(struct ast_event_sub *sub,
 {
 	struct ast_event_ie_val *ie_val;
 
-	if (ie_type < 0 || ie_type > AST_EVENT_IE_MAX) {
+	if (ie_type <= 0 || ie_type >= AST_EVENT_IE_TOTAL) {
 		return -1;
 	}
 
@@ -683,11 +689,13 @@ int ast_event_sub_append_ie_bitflags(struct ast_event_sub *sub,
 {
 	struct ast_event_ie_val *ie_val;
 
-	if (ie_type < 0 || ie_type > AST_EVENT_IE_MAX)
+	if (ie_type <= 0 || ie_type >= AST_EVENT_IE_TOTAL) {
 		return -1;
+	}
 
-	if (!(ie_val = ast_calloc(1, sizeof(*ie_val))))
+	if (!(ie_val = ast_calloc(1, sizeof(*ie_val)))) {
 		return -1;
+	}
 
 	ie_val->ie_type = ie_type;
 	ie_val->payload.uint = flags;
@@ -703,7 +711,7 @@ int ast_event_sub_append_ie_exists(struct ast_event_sub *sub,
 {
 	struct ast_event_ie_val *ie_val;
 
-	if (ie_type < 0 || ie_type > AST_EVENT_IE_MAX) {
+	if (ie_type <= 0 || ie_type >= AST_EVENT_IE_TOTAL) {
 		return -1;
 	}
 
@@ -724,7 +732,7 @@ int ast_event_sub_append_ie_str(struct ast_event_sub *sub,
 {
 	struct ast_event_ie_val *ie_val;
 
-	if (ie_type < 0 || ie_type > AST_EVENT_IE_MAX) {
+	if (ie_type <= 0 || ie_type >= AST_EVENT_IE_TOTAL) {
 		return -1;
 	}
 
@@ -752,7 +760,7 @@ int ast_event_sub_append_ie_raw(struct ast_event_sub *sub,
 {
 	struct ast_event_ie_val *ie_val;
 
-	if (ie_type < 0 || ie_type > AST_EVENT_IE_MAX) {
+	if (ie_type <= 0 || ie_type >= AST_EVENT_IE_TOTAL) {
 		return -1;
 	}
 
diff --git a/main/manager.c b/main/manager.c
index 0670da36d8150e6ddd2d86cf259c76bdf366bb53..c7e4ce4c65e3fe90cff359fd750f70de54951ed5 100644
--- a/main/manager.c
+++ b/main/manager.c
@@ -74,6 +74,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/term.h"
 #include "asterisk/astobj2.h"
 #include "asterisk/features.h"
+#include "asterisk/security_events.h"
 
 /*** DOCUMENTATION
 	<manager name="Ping" language="en_US">
@@ -807,6 +808,7 @@ struct mansession_session {
 	pthread_t waiting_thread;	/*!< Sleeping thread using this descriptor */
 	uint32_t managerid;	/*!< Unique manager identifier, 0 for AMI sessions */
 	time_t sessionstart;    /*!< Session start time */
+	struct timeval sessionstart_tv; /*!< Session start time */
 	time_t sessiontimeout;	/*!< Session timeout if HTTP */
 	char username[80];	/*!< Logged in username */
 	char challenge[10];	/*!< Authentication challenge */
@@ -834,6 +836,7 @@ struct mansession_session {
  */
 struct mansession {
 	struct mansession_session *session;
+	struct ast_tcptls_session_instance *tcptls_session;
 	FILE *f;
 	int fd;
 	ast_mutex_t lock;
@@ -1735,6 +1738,241 @@ static int set_eventmask(struct mansession *s, const char *eventmask)
 	return maskint;
 }
 
+static enum ast_security_event_transport_type mansession_get_transport(const struct mansession *s)
+{
+	return s->tcptls_session->parent->tls_cfg ? AST_SECURITY_EVENT_TRANSPORT_TLS :
+			AST_SECURITY_EVENT_TRANSPORT_TCP;
+}
+
+static struct sockaddr_in *mansession_encode_sin_local(const struct mansession *s,
+		struct sockaddr_in *sin_local)
+{
+	*sin_local = s->tcptls_session->parent->local_address;
+
+	return sin_local;
+}
+
+static void report_invalid_user(const struct mansession *s, const char *username)
+{
+	struct sockaddr_in sin_local;
+	char session_id[32];
+	struct ast_security_event_inval_acct_id inval_acct_id = {
+		.common.event_type = AST_SECURITY_EVENT_INVAL_ACCT_ID,
+		.common.version    = AST_SECURITY_EVENT_INVAL_ACCT_ID_VERSION,
+		.common.service    = "AMI",
+		.common.account_id = username,
+		.common.session_tv = &s->session->sessionstart_tv,
+		.common.local_addr = {
+			.sin       = mansession_encode_sin_local(s, &sin_local),
+			.transport = mansession_get_transport(s),
+		},
+		.common.remote_addr = {
+			.sin       = &s->session->sin,
+			.transport = mansession_get_transport(s),
+		},
+		.common.session_id = session_id,
+	};
+
+	snprintf(session_id, sizeof(session_id), "%p", s);
+
+	ast_security_event_report(AST_SEC_EVT(&inval_acct_id));
+}
+
+static void report_failed_acl(const struct mansession *s, const char *username)
+{
+	struct sockaddr_in sin_local;
+	char session_id[32];
+	struct ast_security_event_failed_acl failed_acl_event = {
+		.common.event_type = AST_SECURITY_EVENT_FAILED_ACL,
+		.common.version    = AST_SECURITY_EVENT_FAILED_ACL_VERSION,
+		.common.service    = "AMI",
+		.common.account_id = username,
+		.common.session_tv = &s->session->sessionstart_tv,
+		.common.local_addr = {
+			.sin       = mansession_encode_sin_local(s, &sin_local),
+			.transport = mansession_get_transport(s),
+		},
+		.common.remote_addr = {
+			.sin       = &s->session->sin,
+			.transport = mansession_get_transport(s),
+		},
+		.common.session_id = session_id,
+	};
+
+	snprintf(session_id, sizeof(session_id), "%p", s->session);
+
+	ast_security_event_report(AST_SEC_EVT(&failed_acl_event));
+}
+
+static void report_inval_password(const struct mansession *s, const char *username)
+{
+	struct sockaddr_in sin_local;
+	char session_id[32];
+	struct ast_security_event_inval_password inval_password = {
+		.common.event_type = AST_SECURITY_EVENT_INVAL_PASSWORD,
+		.common.version    = AST_SECURITY_EVENT_INVAL_PASSWORD_VERSION,
+		.common.service    = "AMI",
+		.common.account_id = username,
+		.common.session_tv = &s->session->sessionstart_tv,
+		.common.local_addr = {
+			.sin       = mansession_encode_sin_local(s, &sin_local),
+			.transport = mansession_get_transport(s),
+		},
+		.common.remote_addr = {
+			.sin       = &s->session->sin,
+			.transport = mansession_get_transport(s),
+		},
+		.common.session_id = session_id,
+	};
+
+	snprintf(session_id, sizeof(session_id), "%p", s->session);
+
+	ast_security_event_report(AST_SEC_EVT(&inval_password));
+}
+
+static void report_auth_success(const struct mansession *s)
+{
+	struct sockaddr_in sin_local;
+	char session_id[32];
+	struct ast_security_event_successful_auth successful_auth = {
+		.common.event_type = AST_SECURITY_EVENT_SUCCESSFUL_AUTH,
+		.common.version    = AST_SECURITY_EVENT_SUCCESSFUL_AUTH_VERSION,
+		.common.service    = "AMI",
+		.common.account_id = s->session->username,
+		.common.session_tv = &s->session->sessionstart_tv,
+		.common.local_addr = {
+			.sin       = mansession_encode_sin_local(s, &sin_local),
+			.transport = mansession_get_transport(s),
+		},
+		.common.remote_addr = {
+			.sin       = &s->session->sin,
+			.transport = mansession_get_transport(s),
+		},
+		.common.session_id = session_id,
+	};
+
+	snprintf(session_id, sizeof(session_id), "%p", s->session);
+
+	ast_security_event_report(AST_SEC_EVT(&successful_auth));
+}
+
+static void report_req_not_allowed(const struct mansession *s, const char *action)
+{
+	struct sockaddr_in sin_local;
+	char session_id[32];
+	char request_type[64];
+	struct ast_security_event_req_not_allowed req_not_allowed = {
+		.common.event_type = AST_SECURITY_EVENT_REQ_NOT_ALLOWED,
+		.common.version    = AST_SECURITY_EVENT_REQ_NOT_ALLOWED_VERSION,
+		.common.service    = "AMI",
+		.common.account_id = s->session->username,
+		.common.session_tv = &s->session->sessionstart_tv,
+		.common.local_addr = {
+			.sin       = mansession_encode_sin_local(s, &sin_local),
+			.transport = mansession_get_transport(s),
+		},
+		.common.remote_addr = {
+			.sin       = &s->session->sin,
+			.transport = mansession_get_transport(s),
+		},
+		.common.session_id = session_id,
+
+		.request_type      = request_type,
+	};
+
+	snprintf(session_id, sizeof(session_id), "%p", s->session);
+	snprintf(request_type, sizeof(request_type), "Action: %s", action);
+
+	ast_security_event_report(AST_SEC_EVT(&req_not_allowed));
+}
+
+static void report_req_bad_format(const struct mansession *s, const char *action)
+{
+	struct sockaddr_in sin_local;
+	char session_id[32];
+	char request_type[64];
+	struct ast_security_event_req_bad_format req_bad_format = {
+		.common.event_type = AST_SECURITY_EVENT_REQ_BAD_FORMAT,
+		.common.version    = AST_SECURITY_EVENT_REQ_BAD_FORMAT_VERSION,
+		.common.service    = "AMI",
+		.common.account_id = s->session->username,
+		.common.session_tv = &s->session->sessionstart_tv,
+		.common.local_addr = {
+			.sin       = mansession_encode_sin_local(s, &sin_local),
+			.transport = mansession_get_transport(s),
+		},
+		.common.remote_addr = {
+			.sin       = &s->session->sin,
+			.transport = mansession_get_transport(s),
+		},
+		.common.session_id = session_id,
+
+		.request_type      = request_type,
+	};
+
+	snprintf(session_id, sizeof(session_id), "%p", s->session);
+	snprintf(request_type, sizeof(request_type), "Action: %s", action);
+
+	ast_security_event_report(AST_SEC_EVT(&req_bad_format));
+}
+
+static void report_failed_challenge_response(const struct mansession *s,
+		const char *response, const char *expected_response)
+{
+	struct sockaddr_in sin_local;
+	char session_id[32];
+	struct ast_security_event_chal_resp_failed chal_resp_failed = {
+		.common.event_type = AST_SECURITY_EVENT_CHAL_RESP_FAILED,
+		.common.version    = AST_SECURITY_EVENT_CHAL_RESP_FAILED_VERSION,
+		.common.service    = "AMI",
+		.common.account_id = s->session->username,
+		.common.session_tv = &s->session->sessionstart_tv,
+		.common.local_addr = {
+			.sin       = mansession_encode_sin_local(s, &sin_local),
+			.transport = mansession_get_transport(s),
+		},
+		.common.remote_addr = {
+			.sin       = &s->session->sin,
+			.transport = mansession_get_transport(s),
+		},
+		.common.session_id = session_id,
+
+		.challenge         = s->session->challenge,
+		.response          = response,
+		.expected_response = expected_response,
+	};
+
+	snprintf(session_id, sizeof(session_id), "%p", s->session);
+
+	ast_security_event_report(AST_SEC_EVT(&chal_resp_failed));
+}
+
+static void report_session_limit(const struct mansession *s)
+{
+	struct sockaddr_in sin_local;
+	char session_id[32];
+	struct ast_security_event_session_limit session_limit = {
+		.common.event_type = AST_SECURITY_EVENT_SESSION_LIMIT,
+		.common.version    = AST_SECURITY_EVENT_SESSION_LIMIT_VERSION,
+		.common.service    = "AMI",
+		.common.account_id = s->session->username,
+		.common.session_tv = &s->session->sessionstart_tv,
+		.common.local_addr = {
+			.sin       = mansession_encode_sin_local(s, &sin_local),
+			.transport = mansession_get_transport(s),
+		},
+		.common.remote_addr = {
+			.sin       = &s->session->sin,
+			.transport = mansession_get_transport(s),
+		},
+		.common.session_id = session_id,
+	};
+
+	snprintf(session_id, sizeof(session_id), "%p", s->session);
+
+	ast_security_event_report(AST_SEC_EVT(&session_limit));
+}
+
 /*
  * Here we start with action_ handlers for AMI actions,
  * and the internal functions used by them.
@@ -1757,8 +1995,10 @@ static int authenticate(struct mansession *s, const struct message *m)
 	AST_RWLIST_WRLOCK(&users);
 
 	if (!(user = get_manager_by_name_locked(username))) {
+		report_invalid_user(s, username);
 		ast_log(LOG_NOTICE, "%s tried to authenticate with nonexistent user '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), username);
 	} else if (user->ha && !ast_apply_ha(user->ha, &(s->session->sin))) {
+		report_failed_acl(s, username);
 		ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_inet_ntoa(s->session->sin.sin_addr), username);
 	} else if (!strcasecmp(astman_get_header(m, "AuthType"), "MD5")) {
 		const char *key = astman_get_header(m, "Key");
@@ -1777,13 +2017,19 @@ static int authenticate(struct mansession *s, const struct message *m)
 				len += sprintf(md5key + len, "%2.2x", digest[x]);
 			if (!strcmp(md5key, key)) {
 				error = 0;
+			} else {
+				report_failed_challenge_response(s, key, md5key);
 			}
 		} else {
 			ast_debug(1, "MD5 authentication is not possible.  challenge: '%s'\n",
 				S_OR(s->session->challenge, ""));
 		}
-	} else if (password && user->secret && !strcmp(password, user->secret)) {
-		error = 0;
+	} else if (user->secret) {
+		if (!strcmp(password, user->secret)) {
+			error = 0;
+		} else {
+			report_inval_password(s, username);
+		}
 	}
 
 	if (error) {
@@ -1799,8 +2045,11 @@ static int authenticate(struct mansession *s, const struct message *m)
 	s->session->writeperm = user->writeperm;
 	s->session->writetimeout = user->writetimeout;
 	s->session->sessionstart = time(NULL);
+	s->session->sessionstart_tv = ast_tvnow();
 	set_eventmask(s, astman_get_header(m, "Events"));
 
+	report_auth_success(s);
+
 	AST_RWLIST_UNLOCK(&users);
 	return 0;
 }
@@ -3550,6 +3799,7 @@ static int process_message(struct mansession *s, const struct message *m)
 	ast_copy_string(action, __astman_get_header(m, "Action", GET_HEADER_SKIP_EMPTY), sizeof(action));
 
 	if (ast_strlen_zero(action)) {
+		report_req_bad_format(s, "NONE");
 		mansession_lock(s);
 		astman_send_error(s, m, "Missing action in request");
 		mansession_unlock(s);
@@ -3557,6 +3807,9 @@ static int process_message(struct mansession *s, const struct message *m)
 	}
 
 	if (!s->session->authenticated && strcasecmp(action, "Login") && strcasecmp(action, "Logoff") && strcasecmp(action, "Challenge")) {
+		if (!s->session->authenticated) {
+			report_req_not_allowed(s, action);
+		}
 		mansession_lock(s);
 		astman_send_error(s, m, "Permission denied");
 		mansession_unlock(s);
@@ -3566,6 +3819,7 @@ static int process_message(struct mansession *s, const struct message *m)
 	if (!allowmultiplelogin && !s->session->authenticated && user &&
 		(!strcasecmp(action, "Login") || !strcasecmp(action, "Challenge"))) {
 		if (check_manager_session_inuse(user)) {
+			report_session_limit(s);
 			sleep(1);
 			mansession_lock(s);
 			astman_send_error(s, m, "Login Already In Use");
@@ -3583,7 +3837,7 @@ static int process_message(struct mansession *s, const struct message *m)
 			call_func = tmp->func;
 		} else {
 			astman_send_error(s, m, "Permission denied");
-			tmp = NULL;
+			report_req_not_allowed(s, action);
 		}
 		break;
 	}
@@ -3595,6 +3849,9 @@ static int process_message(struct mansession *s, const struct message *m)
 		ret = call_func(s, m);
 	} else {
 		char buf[512];
+		if (!tmp) {
+			report_req_bad_format(s, action);
+		}
 		snprintf(buf, sizeof(buf), "Invalid/unknown command: %s. Use Action: ListCommands to show available commands.", action);
 		mansession_lock(s);
 		astman_send_error(s, m, buf);
@@ -3733,7 +3990,9 @@ static void *session_do(void *data)
 {
 	struct ast_tcptls_session_instance *ser = data;
 	struct mansession_session *session = build_mansession(ser->remote_address);
-	struct mansession s = { NULL, };
+	struct mansession s = {
+		.tcptls_session = data,
+	};
 	int flags;
 	int res;
 
diff --git a/main/security_events.c b/main/security_events.c
new file mode 100644
index 0000000000000000000000000000000000000000..d1e2ac0ccd3e96a33e667ffe4d13264187f3c38d
--- /dev/null
+++ b/main/security_events.c
@@ -0,0 +1,647 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2009, Digium, Inc.
+ *
+ * Russell Bryant <russell@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ *
+ * \brief Security Event Reporting Helpers
+ *
+ * \author Russell Bryant <russell@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/utils.h"
+#include "asterisk/strings.h"
+#include "asterisk/network.h"
+#include "asterisk/security_events.h"
+
+static const size_t TIMESTAMP_STR_LEN = 32;
+
+static const struct {
+	const char *name;
+	uint32_t version;
+	enum ast_security_event_severity severity;
+#define MAX_SECURITY_IES 12
+	struct ast_security_event_ie_type required_ies[MAX_SECURITY_IES];
+	struct ast_security_event_ie_type optional_ies[MAX_SECURITY_IES];
+#undef MAX_SECURITY_IES
+} sec_events[AST_SECURITY_EVENT_NUM_TYPES] = {
+
+#define SEC_EVT_FIELD(e, field) (offsetof(struct ast_security_event_##e, field))
+
+[AST_SECURITY_EVENT_FAILED_ACL] = {
+	.name     = "FailedACL",
+	.version  = AST_SECURITY_EVENT_FAILED_ACL_VERSION,
+	.severity = AST_SECURITY_EVENT_SEVERITY_ERROR,
+	.required_ies = {
+		{ AST_EVENT_IE_EVENT_TV, 0 },
+		{ AST_EVENT_IE_SEVERITY, 0 },
+		{ AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) },
+		{ AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) },
+		{ AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) },
+		{ AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) },
+		{ AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) },
+		{ AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) },
+		{ AST_EVENT_IE_END, 0 }
+	},
+	.optional_ies = {
+		{ AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) },
+		{ AST_EVENT_IE_ACL_NAME, SEC_EVT_FIELD(failed_acl, acl_name) },
+		{ AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) },
+		{ AST_EVENT_IE_END, 0 }
+	},
+},
+
+[AST_SECURITY_EVENT_INVAL_ACCT_ID] = {
+	.name     = "InvalidAccountID",
+	.version  = AST_SECURITY_EVENT_INVAL_ACCT_ID_VERSION,
+	.severity = AST_SECURITY_EVENT_SEVERITY_ERROR,
+	.required_ies = {
+		{ AST_EVENT_IE_EVENT_TV, 0 },
+		{ AST_EVENT_IE_SEVERITY, 0 },
+		{ AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) },
+		{ AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) },
+		{ AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) },
+		{ AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) },
+		{ AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) },
+		{ AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) },
+		{ AST_EVENT_IE_END, 0 }
+	},
+	.optional_ies = {
+		{ AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) },
+		{ AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) },
+		{ AST_EVENT_IE_END, 0 }
+	},
+},
+
+[AST_SECURITY_EVENT_SESSION_LIMIT] = {
+	.name     = "SessionLimit",
+	.version  = AST_SECURITY_EVENT_SESSION_LIMIT_VERSION,
+	.severity = AST_SECURITY_EVENT_SEVERITY_ERROR,
+	.required_ies = {
+		{ AST_EVENT_IE_EVENT_TV, 0 },
+		{ AST_EVENT_IE_SEVERITY, 0 },
+		{ AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) },
+		{ AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) },
+		{ AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) },
+		{ AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) },
+		{ AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) },
+		{ AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) },
+		{ AST_EVENT_IE_END, 0 }
+	},
+	.optional_ies = {
+		{ AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) },
+		{ AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) },
+		{ AST_EVENT_IE_END, 0 }
+	},
+},
+
+[AST_SECURITY_EVENT_MEM_LIMIT] = {
+	.name     = "MemoryLimit",
+	.version  = AST_SECURITY_EVENT_MEM_LIMIT_VERSION,
+	.severity = AST_SECURITY_EVENT_SEVERITY_ERROR,
+	.required_ies = {
+		{ AST_EVENT_IE_EVENT_TV, 0 },
+		{ AST_EVENT_IE_SEVERITY, 0 },
+		{ AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) },
+		{ AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) },
+		{ AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) },
+		{ AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) },
+		{ AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) },
+		{ AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) },
+		{ AST_EVENT_IE_END, 0 }
+	},
+	.optional_ies = {
+		{ AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) },
+		{ AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) },
+		{ AST_EVENT_IE_END, 0 }
+	},
+},
+
+[AST_SECURITY_EVENT_LOAD_AVG] = {
+	.name     = "LoadAverageLimit",
+	.version  = AST_SECURITY_EVENT_LOAD_AVG_VERSION,
+	.severity = AST_SECURITY_EVENT_SEVERITY_ERROR,
+	.required_ies = {
+		{ AST_EVENT_IE_EVENT_TV, 0 },
+		{ AST_EVENT_IE_SEVERITY, 0 },
+		{ AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) },
+		{ AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) },
+		{ AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) },
+		{ AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) },
+		{ AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) },
+		{ AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) },
+		{ AST_EVENT_IE_END, 0 }
+	},
+	.optional_ies = {
+		{ AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) },
+		{ AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) },
+		{ AST_EVENT_IE_END, 0 }
+	},
+},
+
+[AST_SECURITY_EVENT_REQ_NO_SUPPORT] = {
+	.name     = "RequestNotSupported",
+	.version  = AST_SECURITY_EVENT_REQ_NO_SUPPORT_VERSION,
+	.severity = AST_SECURITY_EVENT_SEVERITY_ERROR,
+	.required_ies = {
+		{ AST_EVENT_IE_EVENT_TV, 0 },
+		{ AST_EVENT_IE_SEVERITY, 0 },
+		{ AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) },
+		{ AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) },
+		{ AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) },
+		{ AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) },
+		{ AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) },
+		{ AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) },
+		{ AST_EVENT_IE_REQUEST_TYPE, SEC_EVT_FIELD(req_no_support, request_type) },
+		{ AST_EVENT_IE_END, 0 }
+	},
+	.optional_ies = {
+		{ AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) },
+		{ AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) },
+		{ AST_EVENT_IE_END, 0 }
+	},
+},
+
+[AST_SECURITY_EVENT_REQ_NOT_ALLOWED] = {
+	.name     = "RequestNotAllowed",
+	.version  = AST_SECURITY_EVENT_REQ_NOT_ALLOWED_VERSION,
+	.severity = AST_SECURITY_EVENT_SEVERITY_ERROR,
+	.required_ies = {
+		{ AST_EVENT_IE_EVENT_TV, 0 },
+		{ AST_EVENT_IE_SEVERITY, 0 },
+		{ AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) },
+		{ AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) },
+		{ AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) },
+		{ AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) },
+		{ AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) },
+		{ AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) },
+		{ AST_EVENT_IE_REQUEST_TYPE, SEC_EVT_FIELD(req_not_allowed, request_type) },
+		{ AST_EVENT_IE_END, 0 }
+	},
+	.optional_ies = {
+		{ AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) },
+		{ AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) },
+		{ AST_EVENT_IE_REQUEST_PARAMS, SEC_EVT_FIELD(req_not_allowed, request_params) },
+		{ AST_EVENT_IE_END, 0 }
+	},
+},
+
+[AST_SECURITY_EVENT_AUTH_METHOD_NOT_ALLOWED] = {
+	.name     = "AuthMethodNotAllowed",
+	.version  = AST_SECURITY_EVENT_AUTH_METHOD_NOT_ALLOWED_VERSION,
+	.severity = AST_SECURITY_EVENT_SEVERITY_ERROR,
+	.required_ies = {
+		{ AST_EVENT_IE_EVENT_TV, 0 },
+		{ AST_EVENT_IE_SEVERITY, 0 },
+		{ AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) },
+		{ AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) },
+		{ AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) },
+		{ AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) },
+		{ AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) },
+		{ AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) },
+		{ AST_EVENT_IE_AUTH_METHOD, SEC_EVT_FIELD(auth_method_not_allowed, auth_method) },
+		{ AST_EVENT_IE_END, 0 }
+	},
+	.optional_ies = {
+		{ AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) },
+		{ AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) },
+		{ AST_EVENT_IE_END, 0 }
+	},
+},
+
+[AST_SECURITY_EVENT_REQ_BAD_FORMAT] = {
+	.name     = "RequestBadFormat",
+	.version  = AST_SECURITY_EVENT_REQ_BAD_FORMAT_VERSION,
+	.severity = AST_SECURITY_EVENT_SEVERITY_ERROR,
+	.required_ies = {
+		{ AST_EVENT_IE_EVENT_TV, 0 },
+		{ AST_EVENT_IE_SEVERITY, 0 },
+		{ AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) },
+		{ AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) },
+		{ AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) },
+		{ AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) },
+		{ AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) },
+		{ AST_EVENT_IE_REQUEST_TYPE, SEC_EVT_FIELD(req_bad_format, request_type) },
+		{ AST_EVENT_IE_END, 0 }
+	},
+	.optional_ies = {
+		{ AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) },
+		{ AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) },
+		{ AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) },
+		{ AST_EVENT_IE_REQUEST_PARAMS, SEC_EVT_FIELD(req_bad_format, request_params) },
+		{ AST_EVENT_IE_END, 0 }
+	},
+},
+
+[AST_SECURITY_EVENT_SUCCESSFUL_AUTH] = {
+	.name     = "SuccessfulAuth",
+	.version  = AST_SECURITY_EVENT_SUCCESSFUL_AUTH_VERSION,
+	.severity = AST_SECURITY_EVENT_SEVERITY_INFO,
+	.required_ies = {
+		{ AST_EVENT_IE_EVENT_TV, 0 },
+		{ AST_EVENT_IE_SEVERITY, 0 },
+		{ AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) },
+		{ AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) },
+		{ AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) },
+		{ AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) },
+		{ AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) },
+		{ AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) },
+		{ AST_EVENT_IE_END, 0 }
+	},
+	.optional_ies = {
+		{ AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) },
+		{ AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) },
+		{ AST_EVENT_IE_END, 0 }
+	},
+},
+
+[AST_SECURITY_EVENT_UNEXPECTED_ADDR] = {
+	.name     = "UnexpectedAddress",
+	.version  = AST_SECURITY_EVENT_UNEXPECTED_ADDR_VERSION,
+	.severity = AST_SECURITY_EVENT_SEVERITY_ERROR,
+	.required_ies = {
+		{ AST_EVENT_IE_EVENT_TV, 0 },
+		{ AST_EVENT_IE_SEVERITY, 0 },
+		{ AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) },
+		{ AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) },
+		{ AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) },
+		{ AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) },
+		{ AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) },
+		{ AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) },
+		{ AST_EVENT_IE_EXPECTED_ADDR, SEC_EVT_FIELD(unexpected_addr, expected_addr) },
+		{ AST_EVENT_IE_END, 0 }
+	},
+	.optional_ies = {
+		{ AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) },
+		{ AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) },
+		{ AST_EVENT_IE_END, 0 }
+	},
+},
+
+[AST_SECURITY_EVENT_CHAL_RESP_FAILED] = {
+	.name     = "ChallengeResponseFailed",
+	.version  = AST_SECURITY_EVENT_CHAL_RESP_FAILED_VERSION,
+	.severity = AST_SECURITY_EVENT_SEVERITY_ERROR,
+	.required_ies = {
+		{ AST_EVENT_IE_EVENT_TV, 0 },
+		{ AST_EVENT_IE_SEVERITY, 0 },
+		{ AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) },
+		{ AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) },
+		{ AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) },
+		{ AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) },
+		{ AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) },
+		{ AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) },
+		{ AST_EVENT_IE_CHALLENGE, SEC_EVT_FIELD(chal_resp_failed, challenge) },
+		{ AST_EVENT_IE_RESPONSE, SEC_EVT_FIELD(chal_resp_failed, response) },
+		{ AST_EVENT_IE_EXPECTED_RESPONSE, SEC_EVT_FIELD(chal_resp_failed, expected_response) },
+		{ AST_EVENT_IE_END, 0 }
+	},
+	.optional_ies = {
+		{ AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) },
+		{ AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) },
+		{ AST_EVENT_IE_END, 0 }
+	},
+},
+
+[AST_SECURITY_EVENT_INVAL_PASSWORD] = {
+	.name     = "InvalidPassword",
+	.version  = AST_SECURITY_EVENT_INVAL_PASSWORD_VERSION,
+	.severity = AST_SECURITY_EVENT_SEVERITY_ERROR,
+	.required_ies = {
+		{ AST_EVENT_IE_EVENT_TV, 0 },
+		{ AST_EVENT_IE_SEVERITY, 0 },
+		{ AST_EVENT_IE_SERVICE, SEC_EVT_FIELD(common, service) },
+		{ AST_EVENT_IE_EVENT_VERSION, SEC_EVT_FIELD(common, version) },
+		{ AST_EVENT_IE_ACCOUNT_ID, SEC_EVT_FIELD(common, account_id) },
+		{ AST_EVENT_IE_SESSION_ID, SEC_EVT_FIELD(common, session_id) },
+		{ AST_EVENT_IE_LOCAL_ADDR, SEC_EVT_FIELD(common, local_addr) },
+		{ AST_EVENT_IE_REMOTE_ADDR, SEC_EVT_FIELD(common, remote_addr) },
+		{ AST_EVENT_IE_END, 0 }
+	},
+	.optional_ies = {
+		{ AST_EVENT_IE_MODULE, SEC_EVT_FIELD(common, module) },
+		{ AST_EVENT_IE_SESSION_TV, SEC_EVT_FIELD(common, session_tv) },
+		{ AST_EVENT_IE_END, 0 }
+	},
+},
+
+#undef SEC_EVT_FIELD
+
+};
+
+static const struct {
+	enum ast_security_event_severity severity;
+	const char *str;
+} severities[] = {
+	{ AST_SECURITY_EVENT_SEVERITY_INFO,  "Informational" },
+	{ AST_SECURITY_EVENT_SEVERITY_ERROR, "Error" },
+};
+
+const char *ast_security_event_severity_get_name(
+		const enum ast_security_event_severity severity)
+{
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_LEN(severities); i++) {
+		if (severities[i].severity == severity) {
+			return severities[i].str;
+		}
+	}
+
+	return NULL;
+}
+
+static int check_event_type(const enum ast_security_event_type event_type)
+{
+	if (event_type < 0 || event_type >= AST_SECURITY_EVENT_NUM_TYPES) {
+		ast_log(LOG_ERROR, "Invalid security event type %u\n", event_type);
+		return -1;
+	}
+
+	return 0;
+}
+
+const char *ast_security_event_get_name(const enum ast_security_event_type event_type)
+{
+	if (check_event_type(event_type)) {
+		return NULL;
+	}
+
+	return sec_events[event_type].name;
+}
+
+const struct ast_security_event_ie_type *ast_security_event_get_required_ies(
+		const enum ast_security_event_type event_type)
+{
+	if (check_event_type(event_type)) {
+		return NULL;
+	}
+
+	return sec_events[event_type].required_ies;
+}
+
+const struct ast_security_event_ie_type *ast_security_event_get_optional_ies(
+		const enum ast_security_event_type event_type)
+{
+	if (check_event_type(event_type)) {
+		return NULL;
+	}
+
+	return sec_events[event_type].optional_ies;
+}
+
+static void encode_timestamp(struct ast_str **str, const struct timeval *tv)
+{
+	ast_str_set(str, 0, "%u-%u",
+			(unsigned int) tv->tv_sec,
+			(unsigned int) tv->tv_usec);
+}
+
+static struct ast_event *alloc_event(const struct ast_security_event_common *sec)
+{
+	struct ast_str *str = ast_str_alloca(TIMESTAMP_STR_LEN);
+	struct timeval tv = ast_tvnow();
+	const char *severity_str;
+
+	if (check_event_type(sec->event_type)) {
+		return NULL;
+	}
+
+	encode_timestamp(&str, &tv);
+
+	severity_str = S_OR(
+		ast_security_event_severity_get_name(sec_events[sec->event_type].severity),
+		"Unknown"
+	);
+
+	return ast_event_new(AST_EVENT_SECURITY,
+		AST_EVENT_IE_SECURITY_EVENT, AST_EVENT_IE_PLTYPE_UINT, sec->event_type,
+		AST_EVENT_IE_EVENT_VERSION, AST_EVENT_IE_PLTYPE_UINT, sec->version,
+		AST_EVENT_IE_EVENT_TV, AST_EVENT_IE_PLTYPE_STR, str->str,
+		AST_EVENT_IE_SERVICE, AST_EVENT_IE_PLTYPE_STR, sec->service,
+		AST_EVENT_IE_SEVERITY, AST_EVENT_IE_PLTYPE_STR, severity_str,
+		AST_EVENT_IE_END);
+}
+
+static int add_timeval_ie(struct ast_event **event, enum ast_event_ie_type ie_type,
+		const struct timeval *tv)
+{
+	struct ast_str *str = ast_str_alloca(TIMESTAMP_STR_LEN);
+
+	encode_timestamp(&str, tv);
+
+	return ast_event_append_ie_str(event, ie_type, ast_str_buffer(str));
+}
+
+static int add_ipv4_ie(struct ast_event **event, enum ast_event_ie_type ie_type,
+		const struct ast_security_event_ipv4_addr *addr)
+{
+	struct ast_str *str = ast_str_alloca(64);
+
+	ast_str_set(&str, 0, "IPV4/");
+
+	switch (addr->transport) {
+	case AST_SECURITY_EVENT_TRANSPORT_UDP:
+		ast_str_append(&str, 0, "UDP/");
+		break;
+	case AST_SECURITY_EVENT_TRANSPORT_TCP:
+		ast_str_append(&str, 0, "TCP/");
+		break;
+	case AST_SECURITY_EVENT_TRANSPORT_TLS:
+		ast_str_append(&str, 0, "TLS/");
+		break;
+	}
+
+	ast_str_append(&str, 0, "%s/%hu",
+			ast_inet_ntoa(addr->sin->sin_addr),
+			ntohs(addr->sin->sin_port));
+
+	return ast_event_append_ie_str(event, ie_type, ast_str_buffer(str));
+}
+
+enum ie_required {
+	NOT_REQUIRED,
+	REQUIRED
+};
+
+static int add_ie(struct ast_event **event, const struct ast_security_event_common *sec,
+		const struct ast_security_event_ie_type *ie_type, enum ie_required req)
+{
+	int res = 0;
+
+	switch (ie_type->ie_type) {
+	case AST_EVENT_IE_SERVICE:
+	case AST_EVENT_IE_ACCOUNT_ID:
+	case AST_EVENT_IE_SESSION_ID:
+	case AST_EVENT_IE_MODULE:
+	case AST_EVENT_IE_ACL_NAME:
+	case AST_EVENT_IE_REQUEST_TYPE:
+	case AST_EVENT_IE_REQUEST_PARAMS:
+	case AST_EVENT_IE_AUTH_METHOD:
+	case AST_EVENT_IE_CHALLENGE:
+	case AST_EVENT_IE_RESPONSE:
+	case AST_EVENT_IE_EXPECTED_RESPONSE:
+	{
+		const char *str;
+
+		str = *((const char **)(((const char *) sec) + ie_type->offset));
+
+		if (req && !str) {
+			ast_log(LOG_WARNING, "Required IE '%d' for security event "
+					"type '%d' not present\n", ie_type->ie_type,
+					sec->event_type);
+			res = -1;
+		}
+
+		if (str) {
+			res = ast_event_append_ie_str(event, ie_type->ie_type, str);
+		}
+
+		break;
+	}
+	case AST_EVENT_IE_EVENT_VERSION:
+	{
+		uint32_t val;
+		val = *((const uint32_t *)(((const char *) sec) + ie_type->offset));
+		res = ast_event_append_ie_uint(event, ie_type->ie_type, val);
+		break;
+	}
+	case AST_EVENT_IE_LOCAL_ADDR:
+	case AST_EVENT_IE_REMOTE_ADDR:
+	case AST_EVENT_IE_EXPECTED_ADDR:
+	{
+		const struct ast_security_event_ipv4_addr *addr;
+
+		addr = (const struct ast_security_event_ipv4_addr *)(((const char *) sec) + ie_type->offset);
+
+		if (req && !addr->sin) {
+			ast_log(LOG_WARNING, "Required IE '%d' for security event "
+					"type '%d' not present\n", ie_type->ie_type,
+					sec->event_type);
+			res = -1;
+		}
+
+		if (addr->sin) {
+			res = add_ipv4_ie(event, ie_type->ie_type, addr);
+		}
+		break;
+	}
+	case AST_EVENT_IE_SESSION_TV:
+	{
+		const struct timeval *tval;
+
+		tval = *((const struct timeval **)(((const char *) sec) + ie_type->offset));
+
+		if (req && !tval) {
+			ast_log(LOG_WARNING, "Required IE '%d' for security event "
+					"type '%d' not present\n", ie_type->ie_type,
+					sec->event_type);
+			res = -1;
+		}
+
+		if (tval) {
+			add_timeval_ie(event, ie_type->ie_type, tval);
+		}
+
+		break;
+	}
+	case AST_EVENT_IE_EVENT_TV:
+	case AST_EVENT_IE_SEVERITY:
+		/* Added automatically, nothing to do here. */
+		break;
+	default:
+		ast_log(LOG_WARNING, "Unhandled IE type '%d', this security event "
+				"will be missing data.\n", ie_type->ie_type);
+		break;
+	}
+
+	return res;
+}
+
+static int handle_security_event(const struct ast_security_event_common *sec)
+{
+	struct ast_event *event;
+	const struct ast_security_event_ie_type *ies;
+	unsigned int i;
+
+	if (!(event = alloc_event(sec))) {
+		return -1;
+	}
+
+	for (ies = ast_security_event_get_required_ies(sec->event_type), i = 0;
+			ies[i].ie_type != AST_EVENT_IE_END;
+			i++) {
+		if (add_ie(&event, sec, ies + i, REQUIRED)) {
+			goto return_error;
+		}
+	}
+
+	for (ies = ast_security_event_get_optional_ies(sec->event_type), i = 0;
+			ies[i].ie_type != AST_EVENT_IE_END;
+			i++) {
+		if (add_ie(&event, sec, ies + i, NOT_REQUIRED)) {
+			goto return_error;
+		}
+	}
+
+
+	if (ast_event_queue(event)) {
+		goto return_error;
+	}
+
+	return 0;
+
+return_error:
+	if (event) {
+		ast_event_destroy(event);
+	}
+
+	return -1;
+}
+
+int ast_security_event_report(const struct ast_security_event_common *sec)
+{
+	int res;
+
+	if (sec->event_type < 0 || sec->event_type >= AST_SECURITY_EVENT_NUM_TYPES) {
+		ast_log(LOG_ERROR, "Invalid security event type\n");
+		return -1;
+	}
+
+	if (!sec_events[sec->event_type].name) {
+		ast_log(LOG_WARNING, "Security event type %u not handled\n",
+				sec->event_type);
+		return -1;
+	}
+
+	if (sec->version != sec_events[sec->event_type].version) {
+		ast_log(LOG_WARNING, "Security event %u version mismatch\n",
+				sec->event_type);
+		return -1;
+	}
+
+	res = handle_security_event(sec);
+
+	return res;
+}
+
+
diff --git a/res/res_security_log.c b/res/res_security_log.c
new file mode 100644
index 0000000000000000000000000000000000000000..2e3d4af52ac98ee74fd418c7f07bf334ac9a7004
--- /dev/null
+++ b/res/res_security_log.c
@@ -0,0 +1,163 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2009, Digium, Inc.
+ *
+ * Russell Bryant <russell@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ *
+ * \author Russell Bryant <russell@digium.com>
+ *
+ * \brief Security Event Logging
+ *
+ * \todo Make informational security events optional
+ * \todo Escape quotes in string payload IE contents
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
+
+#include "asterisk/module.h"
+#include "asterisk/logger.h"
+#include "asterisk/event.h"
+#include "asterisk/threadstorage.h"
+#include "asterisk/strings.h"
+#include "asterisk/security_events.h"
+
+static const char LOG_SECURITY_NAME[] = "SECURITY";
+
+static int LOG_SECURITY;
+
+static struct ast_event_sub *security_event_sub;
+
+AST_THREADSTORAGE(security_event_buf);
+static const size_t SECURITY_EVENT_BUF_INIT_LEN = 256;
+
+enum ie_required {
+	NOT_REQUIRED,
+	REQUIRED
+};
+
+static int ie_is_present(const struct ast_event *event,
+		const enum ast_event_ie_type ie_type)
+{
+	return (ast_event_get_ie_raw(event, ie_type) != NULL);
+}
+
+static void append_ie(struct ast_str **str, const struct ast_event *event,
+		const enum ast_event_ie_type ie_type, enum ie_required required)
+{
+	if (!required && !ie_is_present(event, ie_type)) {
+		/* Optional IE isn't present.  Ignore. */
+		return;
+	}
+
+	/* At this point, it _better_ be there! */
+	ast_assert(ie_is_present(event, ie_type));
+
+	switch (ast_event_get_ie_pltype(ie_type)) {
+	case AST_EVENT_IE_PLTYPE_UINT:
+		ast_str_append(str, 0, ",%s=\"%u\"",
+				ast_event_get_ie_type_name(ie_type),
+				ast_event_get_ie_uint(event, ie_type));
+		break;
+	case AST_EVENT_IE_PLTYPE_STR:
+		ast_str_append(str, 0, ",%s=\"%s\"",
+				ast_event_get_ie_type_name(ie_type),
+				ast_event_get_ie_str(event, ie_type));
+		break;
+	case AST_EVENT_IE_PLTYPE_BITFLAGS:
+		ast_str_append(str, 0, ",%s=\"%u\"",
+				ast_event_get_ie_type_name(ie_type),
+				ast_event_get_ie_bitflags(event, ie_type));
+		break;
+	case AST_EVENT_IE_PLTYPE_UNKNOWN:
+	case AST_EVENT_IE_PLTYPE_EXISTS:
+	case AST_EVENT_IE_PLTYPE_RAW:
+		ast_log(LOG_WARNING, "Unexpected payload type for IE '%s'\n",
+				ast_event_get_ie_type_name(ie_type));
+		break;
+	}
+}
+
+static void append_ies(struct ast_str **str, const struct ast_event *event,
+		const struct ast_security_event_ie_type *ies, enum ie_required required)
+{
+	unsigned int i;
+
+	for (i = 0; ies[i].ie_type != AST_EVENT_IE_END; i++) {
+		append_ie(str, event, ies[i].ie_type, required);
+	}
+}
+
+static void security_event_cb(const struct ast_event *event, void *data)
+{
+	struct ast_str *str;
+	enum ast_security_event_type event_type;
+
+	if (!(str = ast_str_thread_get(&security_event_buf,
+			SECURITY_EVENT_BUF_INIT_LEN))) {
+		return;
+	}
+
+	/* Note that the event type is guaranteed to be valid here. */
+	event_type = ast_event_get_ie_uint(event, AST_EVENT_IE_SECURITY_EVENT);
+	ast_assert(event_type >= 0 && event_type < AST_SECURITY_EVENT_NUM_TYPES);
+
+	ast_str_set(&str, 0, "%s=\"%s\"",
+			ast_event_get_ie_type_name(AST_EVENT_IE_SECURITY_EVENT),
+			ast_security_event_get_name(event_type));
+
+	append_ies(&str, event,
+			ast_security_event_get_required_ies(event_type), REQUIRED);
+	append_ies(&str, event,
+			ast_security_event_get_optional_ies(event_type), NOT_REQUIRED);
+
+	ast_log_dynamic_level(LOG_SECURITY, "%s\n", ast_str_buffer(str));
+}
+
+static int load_module(void)
+{
+	if ((LOG_SECURITY = ast_logger_register_level(LOG_SECURITY_NAME)) == -1) {
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
+	if (!(security_event_sub = ast_event_subscribe(AST_EVENT_SECURITY,
+			security_event_cb, "Security Event Logger",
+			NULL, AST_EVENT_IE_END))) {
+		ast_logger_unregister_level(LOG_SECURITY_NAME);
+		LOG_SECURITY = -1;
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
+	ast_verb(3, "Security Logging Enabled\n");
+
+	return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+	if (security_event_sub) {
+		security_event_sub = ast_event_unsubscribe(security_event_sub);
+	}
+
+	ast_verb(3, "Security Logging Disabled\n");
+
+	return 0;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Security Event Logging");
diff --git a/tests/test_ami_security_events.sh b/tests/test_ami_security_events.sh
new file mode 100755
index 0000000000000000000000000000000000000000..6f125dc5a45409e435fc22c5aafebde3b159bd2f
--- /dev/null
+++ b/tests/test_ami_security_events.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+
+# manager.conf:
+#
+# [general]
+# ...
+# allowmultipleconnects=no
+# ...
+#
+# [russell]
+# secret=blah123
+# read = system,call,log,verbose,command,agent,user,config
+# write = system,call,log,verbose,command,agent,user,config
+# deny=0.0.0.0/0.0.0.0
+# permit=127.0.0.1/255.255.255.255
+#
+# [russell2]
+# secret=blah123
+# read = system,call,log,verbose,command,agent,user,config
+# write = system,call,log,verbose,command,agent,user,config
+# deny=127.0.0.1/255.255.255.255
+
+# Invalid User
+printf "Action: Login\r\nUsername: foo\r\nSecret: moo\r\n\r\n" | nc localhost 5038
+
+# Invalid Secret
+printf "Action: Login\r\nUsername: russell\r\nSecret: moo\r\n\r\n" | nc localhost 5038
+
+# Auth Success
+printf "Action: Login\r\nUsername: russell\r\nSecret: blah123\r\n\r\n" | nc -w 1 localhost 5038
+
+# Failed ACL
+printf "Action: Login\r\nUsername: russell2\r\nSecret: blah123\r\n\r\n" | nc -w 1 localhost 5038
+
+# Request Not Allowed
+printf "Action: Login\r\nUsername: russell\r\nSecret: blah123\r\n\r\nAction: Originate\r\n\r\n" | nc -w 1 localhost 5038
+
+# Request Bad Format
+printf "Action: Login\r\nUsername: russell\r\nSecret: blah123\r\n\r\nAction: FakeActionBLAH\r\n\r\n" | nc -w 1 localhost 5038
+
+# Failed Challenge Response
+printf "Action: Challenge\r\nUsername: russell\r\nAuthType: MD5\r\n\r\nAction: Login\r\nUsername: russell\r\nAuthType: MD5\r\nKey: 00000000\r\n\r\n" | nc localhost 5038
+
+# Session Limit
+printf "Action: Login\r\nUsername: russell\r\nSecret: blah123\r\n\r\n" | nc -w 5 localhost 5038 &
+printf "Action: Login\r\nUsername: russell\r\nSecret: blah123\r\n\r\n" | nc -w 1 localhost 5038
+
diff --git a/tests/test_security_events.c b/tests/test_security_events.c
new file mode 100644
index 0000000000000000000000000000000000000000..33b40e87715631784708ddd681677cc30c1772af
--- /dev/null
+++ b/tests/test_security_events.c
@@ -0,0 +1,625 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2009, Digium, Inc.
+ *
+ * Russell Bryant <russell@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Test security event generation
+ *
+ * \author Russell Bryant <russell@digium.com>
+ */
+
+/*** MODULEINFO
+	<defaultenabled>no</defaultenabled>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/cli.h"
+#include "asterisk/utils.h"
+#include "asterisk/security_events.h"
+
+static void evt_gen_failed_acl(void);
+static void evt_gen_inval_acct_id(void);
+static void evt_gen_session_limit(void);
+static void evt_gen_mem_limit(void);
+static void evt_gen_load_avg(void);
+static void evt_gen_req_no_support(void);
+static void evt_gen_req_not_allowed(void);
+static void evt_gen_auth_method_not_allowed(void);
+static void evt_gen_req_bad_format(void);
+static void evt_gen_successful_auth(void);
+static void evt_gen_unexpected_addr(void);
+static void evt_gen_chal_resp_failed(void);
+static void evt_gen_inval_password(void);
+
+typedef void (*evt_generator)(void);
+static const evt_generator evt_generators[AST_SECURITY_EVENT_NUM_TYPES] = {
+	[AST_SECURITY_EVENT_FAILED_ACL]              = evt_gen_failed_acl,
+	[AST_SECURITY_EVENT_INVAL_ACCT_ID]           = evt_gen_inval_acct_id,
+	[AST_SECURITY_EVENT_SESSION_LIMIT]           = evt_gen_session_limit,
+	[AST_SECURITY_EVENT_MEM_LIMIT]               = evt_gen_mem_limit,
+	[AST_SECURITY_EVENT_LOAD_AVG]                = evt_gen_load_avg,
+	[AST_SECURITY_EVENT_REQ_NO_SUPPORT]          = evt_gen_req_no_support,
+	[AST_SECURITY_EVENT_REQ_NOT_ALLOWED]         = evt_gen_req_not_allowed,
+	[AST_SECURITY_EVENT_AUTH_METHOD_NOT_ALLOWED] = evt_gen_auth_method_not_allowed,
+	[AST_SECURITY_EVENT_REQ_BAD_FORMAT]          = evt_gen_req_bad_format,
+	[AST_SECURITY_EVENT_SUCCESSFUL_AUTH]         = evt_gen_successful_auth,
+	[AST_SECURITY_EVENT_UNEXPECTED_ADDR]         = evt_gen_unexpected_addr,
+	[AST_SECURITY_EVENT_CHAL_RESP_FAILED]        = evt_gen_chal_resp_failed,
+	[AST_SECURITY_EVENT_INVAL_PASSWORD]          = evt_gen_inval_password,
+};
+
+static void evt_gen_failed_acl(void)
+{
+	struct sockaddr_in sin_local = {
+		.sin_family = AF_INET
+	};
+	struct sockaddr_in sin_remote = {
+		.sin_family = AF_INET
+	};
+	struct timeval session_tv = ast_tvnow();
+	struct ast_security_event_failed_acl failed_acl_event = {
+		.common.event_type = AST_SECURITY_EVENT_FAILED_ACL,
+		.common.version    = AST_SECURITY_EVENT_FAILED_ACL_VERSION,
+		.common.service    = "TEST",
+		.common.module     = AST_MODULE,
+		.common.account_id = "Username",
+		.common.session_id = "Session123",
+		.common.session_tv = &session_tv,
+		.common.local_addr = {
+			.sin  = &sin_local,
+			.transport  = AST_SECURITY_EVENT_TRANSPORT_UDP,
+		},
+		.common.remote_addr = {
+			.sin = &sin_remote,
+			.transport  = AST_SECURITY_EVENT_TRANSPORT_UDP,
+		},
+
+		.acl_name   = "TEST_ACL",
+	};
+
+	inet_aton("192.168.1.1", &sin_local.sin_addr);
+	sin_local.sin_port = htons(12121);
+
+	inet_aton("192.168.1.2", &sin_remote.sin_addr);
+	sin_remote.sin_port = htons(12345);
+
+	ast_security_event_report(AST_SEC_EVT(&failed_acl_event));
+}
+
+static void evt_gen_inval_acct_id(void)
+{
+	struct sockaddr_in sin_local = {
+		.sin_family = AF_INET
+	};
+	struct sockaddr_in sin_remote = {
+		.sin_family = AF_INET
+	};
+	struct timeval session_tv = ast_tvnow();
+	struct ast_security_event_inval_acct_id inval_acct_id = {
+		.common.event_type = AST_SECURITY_EVENT_INVAL_ACCT_ID,
+		.common.version    = AST_SECURITY_EVENT_INVAL_ACCT_ID_VERSION,
+		.common.service    = "TEST",
+		.common.module     = AST_MODULE,
+		.common.account_id = "FakeUser",
+		.common.session_id = "Session456",
+		.common.session_tv = &session_tv,
+		.common.local_addr = {
+			.sin  = &sin_local,
+			.transport  = AST_SECURITY_EVENT_TRANSPORT_TCP,
+		},
+		.common.remote_addr = {
+			.sin = &sin_remote,
+			.transport  = AST_SECURITY_EVENT_TRANSPORT_TCP,
+		},
+	};
+
+	inet_aton("10.1.2.3", &sin_local.sin_addr);
+	sin_local.sin_port = htons(4321);
+
+	inet_aton("10.1.2.4", &sin_remote.sin_addr);
+	sin_remote.sin_port = htons(1234);
+
+	ast_security_event_report(AST_SEC_EVT(&inval_acct_id));
+}
+
+static void evt_gen_session_limit(void)
+{
+	struct sockaddr_in sin_local = {
+		.sin_family = AF_INET
+	};
+	struct sockaddr_in sin_remote = {
+		.sin_family = AF_INET
+	};
+	struct timeval session_tv = ast_tvnow();
+	struct ast_security_event_session_limit session_limit = {
+		.common.event_type = AST_SECURITY_EVENT_SESSION_LIMIT,
+		.common.version    = AST_SECURITY_EVENT_SESSION_LIMIT_VERSION,
+		.common.service    = "TEST",
+		.common.module     = AST_MODULE,
+		.common.account_id = "Jenny",
+		.common.session_id = "8675309",
+		.common.session_tv = &session_tv,
+		.common.local_addr = {
+			.sin  = &sin_local,
+			.transport  = AST_SECURITY_EVENT_TRANSPORT_TLS,
+		},
+		.common.remote_addr = {
+			.sin = &sin_remote,
+			.transport  = AST_SECURITY_EVENT_TRANSPORT_TLS,
+		},
+	};
+
+	inet_aton("10.5.4.3", &sin_local.sin_addr);
+	sin_local.sin_port = htons(4444);
+
+	inet_aton("10.5.4.2", &sin_remote.sin_addr);
+	sin_remote.sin_port = htons(3333);
+
+	ast_security_event_report(AST_SEC_EVT(&session_limit));
+}
+
+static void evt_gen_mem_limit(void)
+{
+	struct sockaddr_in sin_local = {
+		.sin_family = AF_INET
+	};
+	struct sockaddr_in sin_remote = {
+		.sin_family = AF_INET
+	};
+	struct timeval session_tv = ast_tvnow();
+	struct ast_security_event_mem_limit mem_limit = {
+		.common.event_type = AST_SECURITY_EVENT_MEM_LIMIT,
+		.common.version    = AST_SECURITY_EVENT_MEM_LIMIT_VERSION,
+		.common.service    = "TEST",
+		.common.module     = AST_MODULE,
+		.common.account_id = "Felix",
+		.common.session_id = "Session2604",
+		.common.session_tv = &session_tv,
+		.common.local_addr = {
+			.sin  = &sin_local,
+			.transport  = AST_SECURITY_EVENT_TRANSPORT_UDP,
+		},
+		.common.remote_addr = {
+			.sin = &sin_remote,
+			.transport  = AST_SECURITY_EVENT_TRANSPORT_UDP,
+		},
+	};
+
+	inet_aton("10.10.10.10", &sin_local.sin_addr);
+	sin_local.sin_port = htons(555);
+
+	inet_aton("10.10.10.12", &sin_remote.sin_addr);
+	sin_remote.sin_port = htons(5656);
+
+	ast_security_event_report(AST_SEC_EVT(&mem_limit));
+}
+
+static void evt_gen_load_avg(void)
+{
+	struct sockaddr_in sin_local = {
+		.sin_family = AF_INET
+	};
+	struct sockaddr_in sin_remote = {
+		.sin_family = AF_INET
+	};
+	struct timeval session_tv = ast_tvnow();
+	struct ast_security_event_load_avg load_avg = {
+		.common.event_type = AST_SECURITY_EVENT_LOAD_AVG,
+		.common.version    = AST_SECURITY_EVENT_LOAD_AVG_VERSION,
+		.common.service    = "TEST",
+		.common.module     = AST_MODULE,
+		.common.account_id = "GuestAccount",
+		.common.session_id = "XYZ123",
+		.common.session_tv = &session_tv,
+		.common.local_addr = {
+			.sin  = &sin_local,
+			.transport  = AST_SECURITY_EVENT_TRANSPORT_UDP,
+		},
+		.common.remote_addr = {
+			.sin = &sin_remote,
+			.transport  = AST_SECURITY_EVENT_TRANSPORT_UDP,
+		},
+	};
+
+	inet_aton("10.11.12.13", &sin_local.sin_addr);
+	sin_local.sin_port = htons(9876);
+
+	inet_aton("10.12.11.10", &sin_remote.sin_addr);
+	sin_remote.sin_port = htons(9825);
+
+	ast_security_event_report(AST_SEC_EVT(&load_avg));
+}
+
+static void evt_gen_req_no_support(void)
+{
+	struct sockaddr_in sin_local = {
+		.sin_family = AF_INET
+	};
+	struct sockaddr_in sin_remote = {
+		.sin_family = AF_INET
+	};
+	struct timeval session_tv = ast_tvnow();
+	struct ast_security_event_req_no_support req_no_support = {
+		.common.event_type = AST_SECURITY_EVENT_REQ_NO_SUPPORT,
+		.common.version    = AST_SECURITY_EVENT_REQ_NO_SUPPORT_VERSION,
+		.common.service    = "TEST",
+		.common.module     = AST_MODULE,
+		.common.account_id = "George",
+		.common.session_id = "asdkl23478289lasdkf",
+		.common.session_tv = &session_tv,
+		.common.local_addr = {
+			.sin  = &sin_local,
+			.transport  = AST_SECURITY_EVENT_TRANSPORT_UDP,
+		},
+		.common.remote_addr = {
+			.sin = &sin_remote,
+			.transport  = AST_SECURITY_EVENT_TRANSPORT_UDP,
+		},
+
+		.request_type = "MakeMeDinner",
+	};
+
+	inet_aton("10.110.120.130", &sin_local.sin_addr);
+	sin_local.sin_port = htons(9888);
+
+	inet_aton("10.120.110.100", &sin_remote.sin_addr);
+	sin_remote.sin_port = htons(9777);
+
+	ast_security_event_report(AST_SEC_EVT(&req_no_support));
+}
+
+static void evt_gen_req_not_allowed(void)
+{
+	struct sockaddr_in sin_local = {
+		.sin_family = AF_INET
+	};
+	struct sockaddr_in sin_remote = {
+		.sin_family = AF_INET
+	};
+	struct timeval session_tv = ast_tvnow();
+	struct ast_security_event_req_not_allowed req_not_allowed = {
+		.common.event_type = AST_SECURITY_EVENT_REQ_NOT_ALLOWED,
+		.common.version    = AST_SECURITY_EVENT_REQ_NOT_ALLOWED_VERSION,
+		.common.service    = "TEST",
+		.common.module     = AST_MODULE,
+		.common.account_id = "George",
+		.common.session_id = "alksdjf023423h4lka0df",
+		.common.session_tv = &session_tv,
+		.common.local_addr = {
+			.sin  = &sin_local,
+			.transport  = AST_SECURITY_EVENT_TRANSPORT_UDP,
+		},
+		.common.remote_addr = {
+			.sin = &sin_remote,
+			.transport  = AST_SECURITY_EVENT_TRANSPORT_UDP,
+		},
+
+		.request_type = "MakeMeBreakfast",
+		.request_params = "BACONNNN!",
+	};
+
+	inet_aton("10.110.120.130", &sin_local.sin_addr);
+	sin_local.sin_port = htons(9888);
+
+	inet_aton("10.120.110.100", &sin_remote.sin_addr);
+	sin_remote.sin_port = htons(9777);
+
+	ast_security_event_report(AST_SEC_EVT(&req_not_allowed));
+}
+
+static void evt_gen_auth_method_not_allowed(void)
+{
+	struct sockaddr_in sin_local = {
+		.sin_family = AF_INET
+	};
+	struct sockaddr_in sin_remote = {
+		.sin_family = AF_INET
+	};
+	struct timeval session_tv = ast_tvnow();
+	struct ast_security_event_auth_method_not_allowed auth_method_not_allowed = {
+		.common.event_type = AST_SECURITY_EVENT_AUTH_METHOD_NOT_ALLOWED,
+		.common.version    = AST_SECURITY_EVENT_AUTH_METHOD_NOT_ALLOWED_VERSION,
+		.common.service    = "TEST",
+		.common.module     = AST_MODULE,
+		.common.account_id = "Bob",
+		.common.session_id = "010101010101",
+		.common.session_tv = &session_tv,
+		.common.local_addr = {
+			.sin  = &sin_local,
+			.transport  = AST_SECURITY_EVENT_TRANSPORT_TCP,
+		},
+		.common.remote_addr = {
+			.sin = &sin_remote,
+			.transport  = AST_SECURITY_EVENT_TRANSPORT_TCP,
+		},
+
+		.auth_method = "PlainText"
+	};
+
+	inet_aton("10.110.120.135", &sin_local.sin_addr);
+	sin_local.sin_port = htons(8754);
+
+	inet_aton("10.120.110.105", &sin_remote.sin_addr);
+	sin_remote.sin_port = htons(8745);
+
+	ast_security_event_report(AST_SEC_EVT(&auth_method_not_allowed));
+}
+
+static void evt_gen_req_bad_format(void)
+{
+	struct sockaddr_in sin_local = {
+		.sin_family = AF_INET
+	};
+	struct sockaddr_in sin_remote = {
+		.sin_family = AF_INET
+	};
+	struct timeval session_tv = ast_tvnow();
+	struct ast_security_event_req_bad_format req_bad_format = {
+		.common.event_type = AST_SECURITY_EVENT_REQ_BAD_FORMAT,
+		.common.version    = AST_SECURITY_EVENT_REQ_BAD_FORMAT_VERSION,
+		.common.service    = "TEST",
+		.common.module     = AST_MODULE,
+		.common.account_id = "Larry",
+		.common.session_id = "838383fhfhf83hf8h3f8h",
+		.common.session_tv = &session_tv,
+		.common.local_addr = {
+			.sin  = &sin_local,
+			.transport  = AST_SECURITY_EVENT_TRANSPORT_TCP,
+		},
+		.common.remote_addr = {
+			.sin = &sin_remote,
+			.transport  = AST_SECURITY_EVENT_TRANSPORT_TCP,
+		},
+
+		.request_type = "CheeseBurger",
+		.request_params = "Onions,Swiss,MotorOil",
+	};
+
+	inet_aton("10.110.220.230", &sin_local.sin_addr);
+	sin_local.sin_port = htons(1212);
+
+	inet_aton("10.120.210.200", &sin_remote.sin_addr);
+	sin_remote.sin_port = htons(2121);
+
+	ast_security_event_report(AST_SEC_EVT(&req_bad_format));
+}
+
+static void evt_gen_successful_auth(void)
+{
+	struct sockaddr_in sin_local = {
+		.sin_family = AF_INET
+	};
+	struct sockaddr_in sin_remote = {
+		.sin_family = AF_INET
+	};
+	struct timeval session_tv = ast_tvnow();
+	struct ast_security_event_successful_auth successful_auth = {
+		.common.event_type = AST_SECURITY_EVENT_SUCCESSFUL_AUTH,
+		.common.version    = AST_SECURITY_EVENT_SUCCESSFUL_AUTH_VERSION,
+		.common.service    = "TEST",
+		.common.module     = AST_MODULE,
+		.common.account_id = "ValidUser",
+		.common.session_id = "Session456",
+		.common.session_tv = &session_tv,
+		.common.local_addr = {
+			.sin  = &sin_local,
+			.transport  = AST_SECURITY_EVENT_TRANSPORT_TCP,
+		},
+		.common.remote_addr = {
+			.sin = &sin_remote,
+			.transport  = AST_SECURITY_EVENT_TRANSPORT_TCP,
+		},
+	};
+
+	inet_aton("10.1.2.3", &sin_local.sin_addr);
+	sin_local.sin_port = htons(4321);
+
+	inet_aton("10.1.2.4", &sin_remote.sin_addr);
+	sin_remote.sin_port = htons(1234);
+
+	ast_security_event_report(AST_SEC_EVT(&successful_auth));
+}
+
+static void evt_gen_unexpected_addr(void)
+{
+	struct sockaddr_in sin_local = {
+		.sin_family = AF_INET
+	};
+	struct sockaddr_in sin_remote = {
+		.sin_family = AF_INET
+	};
+	struct sockaddr_in sin_expected = {
+		.sin_family = AF_INET
+	};
+	struct timeval session_tv = ast_tvnow();
+	struct ast_security_event_unexpected_addr unexpected_addr = {
+		.common.event_type = AST_SECURITY_EVENT_UNEXPECTED_ADDR,
+		.common.version    = AST_SECURITY_EVENT_UNEXPECTED_ADDR_VERSION,
+		.common.service    = "TEST",
+		.common.module     = AST_MODULE,
+		.common.account_id = "CoolUser",
+		.common.session_id = "Session789",
+		.common.session_tv = &session_tv,
+		.common.local_addr = {
+			.sin  = &sin_local,
+			.transport  = AST_SECURITY_EVENT_TRANSPORT_UDP,
+		},
+		.common.remote_addr = {
+			.sin = &sin_remote,
+			.transport  = AST_SECURITY_EVENT_TRANSPORT_UDP,
+		},
+
+		.expected_addr = {
+			.sin = &sin_expected,
+			.transport  = AST_SECURITY_EVENT_TRANSPORT_UDP,
+		},
+	};
+
+	inet_aton("10.1.2.3", &sin_local.sin_addr);
+	sin_local.sin_port = htons(4321);
+
+	inet_aton("10.1.2.4", &sin_remote.sin_addr);
+	sin_remote.sin_port = htons(1234);
+
+	inet_aton("10.1.2.5", &sin_expected.sin_addr);
+	sin_expected.sin_port = htons(2343);
+
+	ast_security_event_report(AST_SEC_EVT(&unexpected_addr));
+}
+
+static void evt_gen_chal_resp_failed(void)
+{
+	struct sockaddr_in sin_local = {
+		.sin_family = AF_INET
+	};
+	struct sockaddr_in sin_remote = {
+		.sin_family = AF_INET
+	};
+	struct timeval session_tv = ast_tvnow();
+	struct ast_security_event_chal_resp_failed chal_resp_failed = {
+		.common.event_type = AST_SECURITY_EVENT_CHAL_RESP_FAILED,
+		.common.version    = AST_SECURITY_EVENT_CHAL_RESP_FAILED_VERSION,
+		.common.service    = "TEST",
+		.common.module     = AST_MODULE,
+		.common.account_id = "SuperDuperUser",
+		.common.session_id = "Session1231231231",
+		.common.session_tv = &session_tv,
+		.common.local_addr = {
+			.sin  = &sin_local,
+			.transport  = AST_SECURITY_EVENT_TRANSPORT_TCP,
+		},
+		.common.remote_addr = {
+			.sin = &sin_remote,
+			.transport  = AST_SECURITY_EVENT_TRANSPORT_TCP,
+		},
+
+		.challenge         = "8adf8a9sd8fas9df23ljk4",
+		.response          = "9u3jlaksdjflakjsdfoi23",
+		.expected_response = "oiafaljhadf9834luahk3k",
+	};
+
+	inet_aton("10.1.2.3", &sin_local.sin_addr);
+	sin_local.sin_port = htons(4321);
+
+	inet_aton("10.1.2.4", &sin_remote.sin_addr);
+	sin_remote.sin_port = htons(1234);
+
+	ast_security_event_report(AST_SEC_EVT(&chal_resp_failed));
+}
+
+static void evt_gen_inval_password(void)
+{
+	struct sockaddr_in sin_local = {
+		.sin_family = AF_INET
+	};
+	struct sockaddr_in sin_remote = {
+		.sin_family = AF_INET
+	};
+	struct timeval session_tv = ast_tvnow();
+	struct ast_security_event_inval_password inval_password = {
+		.common.event_type = AST_SECURITY_EVENT_INVAL_PASSWORD,
+		.common.version    = AST_SECURITY_EVENT_INVAL_PASSWORD_VERSION,
+		.common.service    = "TEST",
+		.common.module     = AST_MODULE,
+		.common.account_id = "AccountIDGoesHere",
+		.common.session_id = "SessionIDGoesHere",
+		.common.session_tv = &session_tv,
+		.common.local_addr = {
+			.sin  = &sin_local,
+			.transport  = AST_SECURITY_EVENT_TRANSPORT_TCP,
+		},
+		.common.remote_addr = {
+			.sin = &sin_remote,
+			.transport  = AST_SECURITY_EVENT_TRANSPORT_TCP,
+		},
+	};
+
+	inet_aton("10.200.100.30", &sin_local.sin_addr);
+	sin_local.sin_port = htons(4321);
+
+	inet_aton("10.200.100.40", &sin_remote.sin_addr);
+	sin_remote.sin_port = htons(1234);
+
+	ast_security_event_report(AST_SEC_EVT(&inval_password));
+}
+
+static void gen_events(struct ast_cli_args *a)
+{
+	unsigned int i;
+
+	ast_cli(a->fd, "Generating some security events ...\n");
+
+	for (i = 0; i < ARRAY_LEN(evt_generators); i++) {
+		const char *event_type = ast_security_event_get_name(i);
+
+		if (!evt_generators[i]) {
+			ast_cli(a->fd, "*** No event generator for event type '%s' ***\n",
+					event_type);
+			continue;
+		}
+
+		ast_cli(a->fd, "Generating a '%s' security event ...\n", event_type);
+
+		evt_generators[i]();
+	}
+
+	ast_cli(a->fd, "Security event generation complete.\n");
+}
+
+static char *handle_cli_sec_evt_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "securityevents test generation";
+		e->usage = ""
+			"Usage: securityevents test generation"
+			"";
+		return NULL;
+	case CLI_GENERATE:
+		return NULL;
+	case CLI_HANDLER:
+		gen_events(a);
+		return CLI_SUCCESS;
+	}
+
+	return CLI_FAILURE;
+}
+
+static struct ast_cli_entry cli_sec_evt[] = {
+	AST_CLI_DEFINE(handle_cli_sec_evt_test, "Test security event generation"),
+};
+
+static int unload_module(void)
+{
+	return ast_cli_unregister_multiple(cli_sec_evt, ARRAY_LEN(cli_sec_evt));
+}
+
+static int load_module(void)
+{
+	int res;
+
+	res = ast_cli_register_multiple(cli_sec_evt, ARRAY_LEN(cli_sec_evt));
+
+	return res ? AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Test Security Event Generation");