/* ****************************************************************************** * INTERPEAK SOURCE FILE * * Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipl2tp_avp.c,v $ $Revision: 1.14.12.2 $ * $Source: /home/interpeak/CVSRoot/ipl2tp/src/ipl2tp_avp.c,v $ * $Author: jren $ * $State: Exp $ $Locker: $ * * INTERPEAK_COPYRIGHT_STRING * Design and implementation by Joel Hortelius ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #include "ipl2tp_h.h" #include "ipl2tp_avp.h" #include "ipl2tp_attr.h" #include "ipl2tp_trpt.h" #include "ipl2tp_message.h" #include "ipl2tp_pseudowire.h" #ifdef IPMCRYPTO #include #include #else #include #include #endif /* **************************************************************************** * 4 DEFINES **************************************************************************** */ #define IPL2TP_AVP_PARSE_ERROUT(s,c,m) \ do { \ errout_status = (s); \ errout_code = (c); \ errout_message = (m); \ goto errout; \ } while (0) #define IPL2TP_AVP_PARSE_MALFORMED() \ IPL2TP_AVP_PARSE_ERROUT(IPL2TP_STATUS_MALFORMED, \ IPL2TP_ERR_WRONG_LENGTH, \ "malformed") #define IPL2TP_AVP_PARSE_CHECK_MALFORMED(x) \ do { if (x) IPL2TP_AVP_PARSE_MALFORMED(); } while (0) #define IPL2TP_AVP_PARSE_CHECK_NOMEM(x) \ do { \ if ((x) == IP_NULL) \ IPL2TP_AVP_PARSE_ERROUT(IPL2TP_STATUS_NOMEM, \ IPL2TP_ERR_VENDOR_ERROR_LAC, \ "out of memory in"); \ } while (0) #define IPL2TP_AVP_HANDSHAKE(hs,optional) \ (Ip_u16) ((optional) | ((hs)? IPL2TP_AVP_FLAG_NONE : IPL2TP_AVP_FLAG_MANDATORY)) #define HMAC_IPAD 0x36 #define HMAC_OPAD 0x5C /* **************************************************************************** * 5 TYPES **************************************************************************** */ /* **************************************************************************** * 6 EXTERN PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 7 LOCAL PROTOTYPES **************************************************************************** */ /* **************************************************************************** * 8 DATA **************************************************************************** */ static Ip_u8 ipl2tp_avp_hmac_opad[64]; /* 64 '0x5C' */ static Ip_u8 ipl2tp_avp_hmac_ipad[64]; /* 64 '0x36' */ static Ipcom_once_t ipl2tp_avp_hmac_once = IPCOM_ONCE_INIT; /* **************************************************************************** * 9 STATIC FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipl2tp_avp_hmac_md5_setup *=========================================================================== * Description: Initialize the MD5 HMAC I/O-pads. * Parameters: unused - Not used. * Returns: IPCOM_SUCCESS * */ IP_STATIC Ip_err ipl2tp_avp_hmac_setup(void *unused) { IPL2TP_UNUSED(unused); /* Initialize the MD5 O/I-PAD speedups */ ipcom_memset(ipl2tp_avp_hmac_opad, HMAC_OPAD, sizeof(ipl2tp_avp_hmac_opad)); ipcom_memset(ipl2tp_avp_hmac_ipad, HMAC_IPAD, sizeof(ipl2tp_avp_hmac_ipad)); return 0; } /* *=========================================================================== * ipl2tp_avp_hmac_msg_digest_shared_key *=========================================================================== * Description: Create the shared key for the message digest AVP * Parameters: tunnel - The tunnel * Returns: * */ IP_STATIC void ipl2tp_avp_hmac_msg_digest_shared_key(Ipl2tp_tunnel *tunnel) { Ip_u8 value = 2; Ip_u8 digest[MD5_DIGEST_LENGTH]; /* Copy the shared secret to the key */ ipl2tp_util_vector_dup(&tunnel->v.v3.msg_digest_shared_key, &tunnel->attr.secret); (void)ipl2tp_avp_hmac_md5_digest(tunnel, &value, 1, IP_NULL, IP_NULL, digest); /* Create */ ipl2tp_util_vector_create(&tunnel->v.v3.msg_digest_shared_key, digest, MD5_DIGEST_LENGTH); } /* *=========================================================================== * ipl2tp_avp_hmac_hide_avp_shared_key *=========================================================================== * Description: Create the shared key for the hiding and unhiding AVPs * Parameters: tunnel - The tunnel * Returns: * */ IP_STATIC void ipl2tp_avp_hmac_hide_avp_shared_key(Ipl2tp_tunnel *tunnel) { Ip_u8 value = 1; /* HMAC_MD5 value for hiding AVPs */ Ip_u8 digest[MD5_DIGEST_LENGTH]; MD5_CTX i_ctx; MD5_CTX o_ctx; Ipl2tp_vector secret; Ip_size_t i; if (tunnel->local.key.version != IPL2TP_VERSION_L2TPV3) return; if (tunnel->attr.secret.size == 0) return; /* Clear the digest */ (void)ipcom_memset(digest, 0, MD5_DIGEST_LENGTH); /* Duplicate the secret */ secret.array = IP_NULL; ipl2tp_util_vector_dup(&secret, &tunnel->attr.secret); /* Sanity */ if (secret.array == IP_NULL) return; /* Compute the I pad. */ for (i = 0; i < secret.size; i++) secret.array[i] ^= HMAC_IPAD; (void)MD5_Init(&i_ctx); (void)MD5_Update(&i_ctx, secret.array, secret.size); (void)MD5_Update(&i_ctx, ipl2tp_avp_hmac_ipad, 64 - secret.size); (void)MD5_Update(&i_ctx, &value, 1); (void)MD5_Final(digest, &i_ctx); /* Compute the O pad. */ for (i = 0; i < secret.size; i++) secret.array[i] ^= (HMAC_IPAD ^ HMAC_OPAD); (void)MD5_Init(&o_ctx); (void)MD5_Update(&o_ctx, secret.array, secret.size); (void)MD5_Update(&o_ctx, ipl2tp_avp_hmac_opad, 64 - secret.size); /* Outer HMAC */ (void)MD5_Update(&o_ctx, digest, MD5_DIGEST_LENGTH); (void)MD5_Final(digest, &o_ctx); IPL2TP_SAFE_FREE(secret.array); /* Create */ ipl2tp_util_vector_create(&tunnel->v.v3.hide_avp_shared_key, digest, MD5_DIGEST_LENGTH); return; } /* *=========================================================================== * ipl2tp_avp_hmac_md5_verify *=========================================================================== * Description: Verify MD5 hmac for tunnel. * Parameters: tunnel - Tunnel to create digest for * buffer - To calculate digest over. * size - Size of buffer * digest - Pointer to digest. * Returns: * */ IP_STATIC Ip_bool ipl2tp_avp_hmac_md5_verify(Ipl2tp_tunnel *tunnel, void *buffer, Ip_size_t size, void *digest) { char sdigest[MD5_DIGEST_LENGTH]; ipcom_memcpy(sdigest, digest, MD5_DIGEST_LENGTH); ipcom_memset(digest, 0, MD5_DIGEST_LENGTH); /* */ if (ipl2tp_avp_hmac_md5_digest(tunnel, buffer, size, &tunnel->remote.nonce, &tunnel->local.nonce, digest) == IP_FALSE) return IP_FALSE; /* */ return ipcom_memcmp(sdigest, digest, MD5_DIGEST_LENGTH) == 0; } /* *=========================================================================== * ipl2tp_avp_hmac_sha_verify *=========================================================================== * Description: Verify SHA digest for a given tunnel * Parameters: tunnel - Tunnel to create digest for * buffer - To calculate digest over. * size - Size of buffer * digest - Pointer to digest. * Returns: * */ IP_STATIC Ip_bool ipl2tp_avp_hmac_sha_verify(Ipl2tp_tunnel *tunnel, void *buffer, Ip_size_t size, void *digest) { char sdigest[SHA_DIGEST_LENGTH]; ipcom_memcpy(sdigest, digest, SHA_DIGEST_LENGTH); ipcom_memset(digest, 0, SHA_DIGEST_LENGTH); /* */ if (ipl2tp_avp_hmac_sha_digest(tunnel, buffer, size, &tunnel->remote.nonce, &tunnel->local.nonce, digest) == IP_FALSE) return IP_FALSE; /* */ return ipcom_memcmp(sdigest, digest, SHA_DIGEST_LENGTH) == 0; } /* *=========================================================================== * ipl2tp_avp_name *=========================================================================== * Description: Get name of attribute. * Parameters: attr_type - Attribute type * Returns: Human readable attribute name. * */ IP_STATIC CONST char * ipl2tp_avp_name(Ip_u16 attr_type) { static CONST char *ipl2tp_attr_string[] = { "MESSAGE_TYPE", /* 0 */ "RESULT_CODE", /* 1 */ "PROTOCOL_VERSION", /* 2 */ "FRAMING_CAPABILITIES", /* 3 */ "BEARER_CAPABILITIES", /* 4 */ "TIE_BREAKER", /* 5 */ "FIRMWARE_REVISION", /* 6 */ "HOST_NAME", /* 7 */ "VENDOR_NAME", /* 8 */ "ASSIGNED_TUNNEL_ID", /* 9 */ "RECEIVE_WINDOW_SIZE", /* 10 */ "CHALLENGE", /* 11 */ "Q931_CAUSE_CODE", /* 12 */ "CHALLENGE_RESPONSE", /* 13 */ "ASSIGNED_SESSION_ID", /* 14 */ "CALL_SERIAL_NUMBER", /* 15 */ "MINIMUM_BPS", /* 16 */ "MAXIMUM_BPS", /* 17 */ "BEARER_TYPE", /* 18 */ "FRAMING_TYPE", /* 19 */ "VOID", /* 20 */ "CALLED_NUMBER", /* 21 */ "CALLING_NUMBER", /* 22 */ "SUB_ADDRESS", /* 23 */ "TX_CONNECT_SPEED", /* 24 */ "PHYSICAL_CHANNEL_ID", /* 25 */ "INITIAL_RECEIVED_LCP_CONFREQ", /* 26 */ "LAST_SENT_LCP_CONFREQ", /* 27 */ "LAST_RECEIVED_LCP_CONFREQ", /* 28 */ "PROXY_AUTHEN_TYPE", /* 29 */ "PROXY_AUTHEN_NAME", /* 30 */ "PROXY_AUTHEN_CHALLENGE", /* 31 */ "PROXY_AUTHEN_ID", /* 32 */ "PROXY_AUTHEN_RESPONSE", /* 33 */ "CALL_ERRORS", /* 34 */ "ACCM", /* 35 */ "RANDOM_VECTOR", /* 36 */ "PRIVATE_GROUP_ID", /* 37 */ "RX_CONNECT_SPEED", /* 38 */ "SEQUENCING_REQUIRED", /* 39 */ "RX MINIMUM BPS", /* 40 */ "RX MAXIMUM BPS", /* 41 */ "SERVICE CATEGORY", /* 42 */ "SERVICE NAME", /* 43 */ "CALLING_SUB_ADDRESS", /* 44 */ "VPI_VCI_IDENTIFIER", /* 45 */ "PPP_DISCONNECT_CAUSE_CODE", /* 46 */ "CCDS", /* 47 */ "SDS", /* 48 */ "LCP_WANT_OPTIONS", /* 49 */ "LCP_ALLOW_OPTIONS", /* 50 */ "LNS_LAST_SENT_LCP_CONFREQ", /* 51 */ "LNS_LAST_RECEIVED_LCP_CONFREQ", /* 52 */ "MODEM_ON_HOLD_CAPABLE", /* 53 */ "MODEM_ON_HOLD_STATUS", /* 54 */ "PPPOE_RELAY", /* 55 */ "PPPOE_RELAY_RESPONSE_CAPABILITY", /* 56 */ "PPPOE_RELAY_FORWARD_CAPABILITY", /* 57 */ "EXTENDED_VENDOR_ID", /* 58 */ "MESSAGE_DIGEST", /* 59 */ "ROUTER_ID", /* 60 */ "ASSIGNED_CONTROL_CONNECTION_ID", /* 61 */ "PSEUDOWIRE_CAPABILITIES_LIST", /* 62 */ "LOCAL_SESSION_ID", /* 63 */ "REMOTE_SESSION_ID", /* 64 */ "ASSIGNED_COOKIE", /* 65 */ "REMOTE_END_ID", /* 66 */ "APPLICATION_CODE", /* 67 */ "PSEUDOWIRE_TYPE", /* 68 */ "L2_SPECIFIC_SUBLAYER", /* 69 */ "DATA_SEQUENCING", /* 70 */ "CIRCUIT_STATUS", /* 71 */ "PREFERRED_LANGUAGE", /* 72 */ "CONTROL_MESSAGE_AUTH_NONCE", /* 73 */ "TX_CONNECT_SPEED64", /* 74 */ "RX_CONNECT_SPEED64", /* 75 */ }; return (attr_type < (sizeof(ipl2tp_attr_string) / sizeof(ipl2tp_attr_string[0])) ? ipl2tp_attr_string[attr_type] : ""); } /* *=========================================================================== * ipl2tp_avp_mandatory *=========================================================================== * Description: Check if attribute is mandatory, and print error message. * Parameters: tunnel - The tunnel * avp - The AVP * flags - Flags from the L2TP header. * avp_type - Type of attribute. * msg - Error message to print. * Returns: IP_TRUE if mandatory. * */ IP_STATIC Ip_bool ipl2tp_avp_mandatory(Ipl2tp_tunnel *tunnel, Ipl2tp_avp *avp, Ip_u16 flags, Ip_u16 avp_type, Ipl2tp_status status, Ip_u16 err_code, CONST char *msg) { if ((flags & IPL2TP_AVP_FLAG_MANDATORY) != 0) { if (tunnel) { IPL2TP_TUNNEL_RESULT_SET(tunnel, status, IPL2TP_RES_StopCCN_GENERAL_ERROR, err_code, "Got an unknown mandatory attribute"); } IPCOM_LOG3(ERR, "%s: Got %s mandatory attribute %s", ipl2tp_versions[avp->version], msg, ipl2tp_avp_name(avp_type)); return IP_TRUE; } IPCOM_LOG3(WARNING, "%s: Got %s attribute %s", ipl2tp_versions[avp->version], msg, ipl2tp_avp_name(avp_type)); return IP_FALSE; } /* *=========================================================================== * ipl2tp_avp_validate *=========================================================================== * Description: Check if any compulsory AVPs are missing. * Parameters: version - Version * avp - AVP collection. * hidden - Check hidden AVP's * Returns: IPL2TP_STATUS_OK if OK. * */ IP_STATIC Ipl2tp_status ipl2tp_avp_validate(Ipl2tp_avp *avp, int hidden) { CONST Ip_u8 *vector; Ipl2tp_status status = IPL2TP_STATUS_OK; int i; /* Message type */ if (avp->message_type.head.ok == 0) { IPCOM_LOG2(WARNING, "%s: Missing %s attribute", ipl2tp_versions[avp->version], ipl2tp_avp_name(IPL2TP_AVP_MESSAGE_TYPE)); return IPL2TP_STATUS_TEARDOWN; } /* Get compulsory vector */ vector = avp->compulsory[avp->message_type.value]; /* Check that all compulsory AVPs are present */ for (i = 0; vector[i] != IPL2TP_AVP_END; i++) { /*lint -e504*/ if ((hidden != 0 || (vector[i] & IPL2TP_AVP_VISIBLE) != 0) && BITTST(&avp->map, (vector[i] & ~IPL2TP_AVP_VISIBLE)) == 0) { IPCOM_LOG4(WARNING, "%s: Missing %s attribute in %s (%d)", ipl2tp_versions[avp->version], ipl2tp_avp_name((Ip_u16)(vector[i] & ~IPL2TP_AVP_VISIBLE)), ipl2tp_message_name(avp->message_type.value), hidden); status = IPL2TP_STATUS_TEARDOWN; } } return status; } /* *=========================================================================== * ipl2tp_avp_hide *=========================================================================== * Description: hide avp * Parameters: * Returns: * */ IP_STATIC void ipl2tp_avp_hide(Ipl2tp_avp_message_header *hdr, void *buffer, Ip_size_t buffer_len, Ip_u8 *secret, Ip_size_t secret_len, Ip_u8 *random_vector, Ip_size_t random_vector_len) { Ip_u8 *data = buffer; MD5_CTX ctx; int buffer_pos = 0; int digest_pos = 0; Ip_u8 digest[MD5_DIGEST_LENGTH]; /* Compute initial digest */ (void)MD5_Init(&ctx); (void)MD5_Update(&ctx, (Ip_u8 *)&hdr->attribute_type, 2); (void)MD5_Update(&ctx, secret, secret_len); (void)MD5_Update(&ctx, random_vector, random_vector_len); (void)MD5_Final(digest, &ctx); /* Insert value */ for (; buffer_pos < buffer_len; buffer_pos++, digest_pos++) { if (digest_pos == MD5_DIGEST_LENGTH) { /* Compute new digest */ digest_pos = 0; (void)MD5_Init(&ctx); (void)MD5_Update(&ctx, secret, secret_len); (void)MD5_Update(&ctx, &data[buffer_pos] - MD5_DIGEST_LENGTH, MD5_DIGEST_LENGTH); (void)MD5_Final(digest, &ctx); } data[buffer_pos] ^= digest[digest_pos]; } return; } /* *=========================================================================== * ipl2tp_avp_unhide *=========================================================================== * Description: unhide avp * Parameters: * Returns: * */ IP_STATIC void ipl2tp_avp_unhide(Ipl2tp_avp_message_header *hdr, void *buffer, Ip_size_t buffer_len, Ip_u8 *secret, Ip_size_t secret_len, Ip_u8 *random_vector, Ip_size_t random_vector_len) { Ip_u8 *data = buffer; MD5_CTX ctx; int buffer_pos = 0; int digest_pos = 0; Ip_u8 digest[MD5_DIGEST_LENGTH]; for (buffer_pos = (buffer_len - 1) & ~0xF; buffer_pos > 0; buffer_pos++, digest_pos++) { if (digest_pos == 16 || buffer_pos == buffer_len) { digest_pos = 0; buffer_pos = ((buffer_pos -1) & ~0xF) - 16; if(buffer_pos == 0) break; } if (digest_pos == 0) { /* Compute new digest */ (void)MD5_Init(&ctx); (void)MD5_Update(&ctx, secret, secret_len); (void)MD5_Update(&ctx, &data[buffer_pos] - 16, 16); (void)MD5_Final(digest, &ctx); } data[buffer_pos] ^= digest[digest_pos]; } /* Compute initial digest */ (void)MD5_Init(&ctx); (void)MD5_Update(&ctx, (Ip_u8 *)&hdr->attribute_type, 2); (void)MD5_Update(&ctx, secret, secret_len); (void)MD5_Update(&ctx, random_vector, random_vector_len); (void)MD5_Final(digest, &ctx); for (buffer_pos = 0, digest_pos = 0; buffer_pos < buffer_len && digest_pos != 16; buffer_pos++, digest_pos++) { data[buffer_pos] ^= digest[digest_pos]; } return; } /* *=========================================================================== * ipl2tp_avp_md5 *=========================================================================== * Description: * Parameters: digest - MD5 digest * dXp - data pointer * dX1 - length of data. * Returns: * */ IP_STATIC void ipl2tp_avp_md5(Ip_u8 digest[MD5_DIGEST_LENGTH], Ipl2tp_tunnel *tunnel, void *tdata, Ip_size_t tsize, void *ddata, Ip_size_t dsize) { MD5_CTX ctx; MD5_Init(&ctx); /* Hash the AVP type if it's present */ if (tsize != 0) MD5_Update(&ctx, tdata, tsize); MD5_Update(&ctx, tunnel->attr.secret.array, tunnel->attr.secret.size); MD5_Update(&ctx, ddata, dsize); MD5_Final(digest, &ctx); } /* *=========================================================================== * ipl2tp_avp_update *=========================================================================== * Description: Update L2TP message with a new AVP. Uses data in * static variable ipl2tp_avp. * Parameters: container - Container holding the message * type - AVP type. * flength - Length of fixed part. * vptr - Pointer to variable part. * vlength - Length of variable part. * flags - AVP flags. * Returns: * */ IP_STATIC void * ipl2tp_avp_update(Ipl2tp_message *container, Ipl2tp_avp_message_header *hdr, struct Ip_iovec *iovec, Ip_u16 iovec_size) { Ipl2tp_tunnel *tunnel = container->admin.tunnel; Ip_u16 flags_length; Ip_u16 avp_flags; Ip_u16 avp_length; Ip_u16 avp_type; Ip_u16 vec_length = 0; Ip_u16 hidden_length = 0; Ip_u16 message_length; Ip_u8 *data; int i; /* Get current flags */ avp_flags = (Ip_u16)(IP_GET_NTOHS(&hdr->flags_length) & 0xFC00); /* Get the header length of this AVP */ avp_length = (Ip_u16)(IP_GET_NTOHS(&hdr->flags_length) & 0x3FF); /* Get the AVP type */ avp_type = IP_GET_NTOHS(&hdr->attribute_type); /* Calculate total size */ for (i = 0; i < iovec_size; i++) vec_length += iovec[i].iov_len; /* Use hidden AVP? */ if (tunnel->attr.hide_avp == IPL2TP_MIB_NO || tunnel->attr.secret.size == 0 || (avp_flags & IPL2TP_AVP_FLAG_HIDDEN) == 0) { /* No */ avp_flags &= ~IPL2TP_AVP_FLAG_HIDDEN; } else { /* Insert random vector if not previously done */ if ((container->admin.flags & IPL2TP_CONTAINER_RV) == 0) { Ipl2tp_avp_message_header rv_hdr; struct Ip_iovec rv_iovec; Ip_u8 rv_value[IPL2TP_MAX_CHALLENGE_LENGTH]; Ipl2tp_vector rv_vec; rv_vec.array = rv_value; ipl2tp_util_vector_random(&rv_vec, ipl2tp_avp_challenge_size()); /* Data */ rv_iovec.iov_len = rv_vec.size; rv_iovec.iov_base = rv_value; IPL2TP_AVP_INITIALIZE(&rv_hdr, IPL2TP_AVP_RANDOM_VECTOR, IPL2TP_AVP_FLAG_MANDATORY); /* Enter random vector AVP */ container->admin.rv_value = ipl2tp_avp_update(container, &rv_hdr, &rv_iovec, 1); container->admin.rv_length = (Ip_u16) rv_iovec.iov_len; container->admin.flags |= IPL2TP_CONTAINER_RV; } /* Reserve two bytes and pad to 16 bytes */ hidden_length = (Ip_u16) (2 + ((2 - (avp_length + vec_length)) & (MD5_DIGEST_LENGTH - 1))); } IPCOM_LOG5(DEBUG, "%s(%d,%s): Encoding %s AVP: %s", ipl2tp_versions[tunnel->local.key.version], (int)tunnel->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr), (avp_flags & IPL2TP_AVP_FLAG_HIDDEN ? "hidden" : "visible"), ipl2tp_avp_name(avp_type)); /* Get lengths */ flags_length = (Ip_u16)((avp_length + hidden_length + vec_length) | avp_flags); message_length = container->admin.size; /* Check if overflow */ ip_assert((message_length + hidden_length + avp_length + vec_length) <= IPL2TP_MAX_MESSAGE_SIZE); /* Update length field */ IP_SET_HTONS(&hdr->flags_length, flags_length); /* Update header part */ ipcom_memcpy(container->admin.start + message_length, hdr, avp_length); /* Increase the message size */ message_length += avp_length; /* Update original length */ if (avp_flags & IPL2TP_AVP_FLAG_HIDDEN) { IP_SET_HTONS(container->admin.start + message_length, vec_length); message_length += sizeof(Ip_u16); } /* Update variable part */ data = container->admin.start + message_length; for (i = 0; i < iovec_size; i++) { ipcom_memcpy(container->admin.start + message_length, iovec[i].iov_base, iovec[i].iov_len); message_length += iovec[i].iov_len; } /* Padding */ if (hidden_length > 2) { message_length += (hidden_length - 2); } /* Hide message? */ if (avp_flags & IPL2TP_AVP_FLAG_HIDDEN) { /* Current AVP past current AVP header size */ Ip_u8 *base = container->admin.start + container->admin.size + avp_length; if (tunnel->local.key.version == IPL2TP_VERSION_L2TPV3) { if (tunnel->v.v3.hide_avp_shared_key.size == 0) { /* Create the shared_key = HMAC_MD5(shared_key, 1) per RFC 3931*/ ipl2tp_avp_hmac_hide_avp_shared_key(tunnel); } /* V3 uses a shared_key */ ipl2tp_avp_hide(hdr, base, hidden_length + vec_length, tunnel->v.v3.hide_avp_shared_key.array, tunnel->v.v3.hide_avp_shared_key.size, container->admin.rv_value, container->admin.rv_length); } else { /* default V2 uses a shared_secret */ ipl2tp_avp_hide(hdr, base, hidden_length + vec_length, tunnel->attr.secret.array, tunnel->attr.secret.size, container->admin.rv_value, container->admin.rv_length); } } /* Update container size & length fields */ container->admin.size = message_length; IP_SET_HTONS(container->admin.length, message_length - container->admin.pre_header); return data; } /* *=========================================================================== * ipl2tp_avp_set *=========================================================================== * Description: Set a simple AVP * Parameters: cont - The container * type - The type of the AVP. * flags - The flags * data - Data * size - Size of data * Returns: Where the data was stored. * */ IP_STATIC Ip_u8 * ipl2tp_avp_set(Ipl2tp_message *cont, Ip_u16 type, Ip_u16 flags, void *data, Ip_size_t size) { Ipl2tp_avp_message_header avp_hdr; struct Ip_iovec avp_iovec; /* Initialize the AVP header */ IPL2TP_AVP_INITIALIZE(&avp_hdr, type, flags); /* Setup the io vector */ avp_iovec.iov_base = data; avp_iovec.iov_len = size; /* Update the AVP. */ return ipl2tp_avp_update(cont, &avp_hdr, &avp_iovec, 1); } /* **************************************************************************** * 10 GLOBAL FUNCTIONS **************************************************************************** */ /* *=========================================================================== * ipl2tp_avp_parse *=========================================================================== * Description: Parse the AVP's attached to a control channel message. * Parameters: avp - The AVP to store the parsed data in. * message - The pointer to the message. * mindex - The index where to start parse AVP * message_length - The total size of the message. * hidden - Hidden AVP's. * tunnel - The tunnel, if found. * addr - The address of the sender of the message. * Returns: IPL2TP_SUCCESS - Success. * */ IP_GLOBAL Ipl2tp_status ipl2tp_avp_parse(Ipl2tp_avp *avp, Ip_u8 *message_base, Ip_size_t mindex, Ip_size_t message_length, Ip_bool hidden, Ipl2tp_tunnel *tunnel, union Ip_sockaddr_union *addr) { Ip_u16 totlength = 1; Ip_u16 length; int avp_no = 0; IPL2TP_UNUSED(addr); /* ZLB messages gets special treatment */ if (message_length <= mindex) { /* Here we fake a ZLB message type */ avp->message_type.head.ok = 1; avp->message_type.head.mandatory = 0; avp->message_type.head.vendor_id = 0; avp->message_type.value = ip_htons(IPL2TP_MSG_ZLB); return IPL2TP_STATUS_OK; } /* Loop until end of message */ for (; totlength != 0 && mindex < message_length; mindex += totlength, avp_no++) { Ipl2tp_avp_message_header *unaligned = (Ipl2tp_avp_message_header *)&message_base[mindex]; Ipl2tp_avp_header *avph; Ip_u8 *data; Ip_u16 flags; Ip_u32 vendor_id; Ip_u16 avp_type; Ipl2tp_status errout_status; Ip_u16 errout_code; const char *errout_message; Ip_u32 hsize = 6; Ip_u32 val_size; /* Get header fields */ flags = (Ip_u16)(IP_GET_NTOHS(&unaligned->flags_length) & 0xFC00); vendor_id = IP_GET_NTOHS(&unaligned->vendor_id); avp_type = IP_GET_NTOHS(&unaligned->attribute_type); /* Get length of this AVP */ totlength = length = (Ip_u16)(IP_GET_NTOHS(&unaligned->flags_length) & 0x3FF); /* Verify if this is an 'extended' AVP */ if (vendor_id == 0 && avp_type == IPL2TP_AVP_EXTENDED_VENDOR_ID) { Ipl2tp_avp_ext_message_header *ext; ext = (Ipl2tp_avp_ext_message_header *) unaligned; /* Extract the real values */ vendor_id = IP_GET_NTOHL(&ext->vendor_id[0]); avp_type = IP_GET_NTOHS(&ext->attribute_type); /* We've got an extended header */ hsize = 12; } data = (Ip_u8 *) unaligned + hsize; /* Remember the presence of hidden AVPs */ avp->hidden = avp->hidden || avp_type == IPL2TP_AVP_MESSAGE_DIGEST || ((flags & IPL2TP_AVP_FLAG_HIDDEN) != 0); if (hidden) { /* MUST read the random vector */ if (avp_type != IPL2TP_AVP_RANDOM_VECTOR && avp_type != IPL2TP_AVP_MESSAGE_DIGEST) { /* Don't bother with visible attributes */ if (IP_BIT_ISFALSE(flags, IPL2TP_AVP_FLAG_HIDDEN)) continue; } } else { /* Don't parse hidden attributes */ if (IP_BIT_ISSET(flags, IPL2TP_AVP_FLAG_HIDDEN)) continue; /* Don't bother with the random vector */ if (avp_type == IPL2TP_AVP_RANDOM_VECTOR) continue; } /* Check reserved bits */ if ((flags & 0x3C00) != 0) { IPL2TP_AVP_PARSE_ERROUT(IPL2TP_STATUS_RESERVEDNZ, IPL2TP_ERR_OUT_OF_RANGE, "non-zero reserved bits in"); } /* Check that AVP length matches message length */ IPL2TP_AVP_PARSE_CHECK_MALFORMED((mindex + totlength) > message_length); /* Cannot handle vendor-specific stuff */ if (vendor_id != 0) { IPL2TP_AVP_PARSE_ERROUT(IPL2TP_STATUS_VENDOR, IPL2TP_ERR_UNKNOWN_M_AVP, "unknown vendor-specific"); } /* Handle hidden AVPs */ if ((flags & IPL2TP_AVP_FLAG_HIDDEN) == 0) { IPCOM_LOG2(DEBUG, "%s: Decoding visible AVP: %s", ipl2tp_versions[avp->version], ipl2tp_avp_name(avp_type)); } else { int vallength = totlength - hsize; Ip_u16 orilen; IPCOM_LOG2(DEBUG, "%s: Decoding hidden AVP: %s", ipl2tp_versions[avp->version], ipl2tp_avp_name(avp_type)); /* Check if random vector present */ if (avp->random_vector.vec.size == 0) { IPL2TP_AVP_PARSE_ERROUT(IPL2TP_STATUS_NORAND, IPL2TP_ERR_VENDOR_ERROR_LAC, "no random vector for"); } /* Check if secret present */ if (tunnel == IP_NULL || tunnel->attr.secret.size == 0) { IPL2TP_AVP_PARSE_ERROUT(IPL2TP_STATUS_NOSECRET, IPL2TP_ERR_VENDOR_ERROR_LAC, "no shared secret for"); } /* Length must always be at least 2 octets */ /* Check that AVP length matches message length */ IPL2TP_AVP_PARSE_CHECK_MALFORMED(vallength < 2); if (tunnel->local.key.version == IPL2TP_VERSION_L2TPV3) { if (tunnel->v.v3.hide_avp_shared_key.size == 0) { /* Create the shared_key = HMAC_MD5(shared_key, 1) per RFC 3931*/ ipl2tp_avp_hmac_hide_avp_shared_key(tunnel); } /* V3 uses a shared_key */ ipl2tp_avp_unhide(unaligned, data, vallength, tunnel->v.v3.hide_avp_shared_key.array, tunnel->v.v3.hide_avp_shared_key.size, avp->random_vector.vec.array, avp->random_vector.vec.size); } else { /* default V2 uses a shared_secret */ ipl2tp_avp_unhide(unaligned, data, vallength, tunnel->attr.secret.array, tunnel->attr.secret.size, avp->random_vector.vec.array, avp->random_vector.vec.size); } /* Get original length */ orilen = IP_GET_NTOHS(data); IPL2TP_AVP_PARSE_CHECK_MALFORMED(orilen > (vallength - 2)); /* Update values */ length = (Ip_u16) (orilen + hsize); data += 2; } /* Length must always be at least hsize octets */ IPL2TP_AVP_PARSE_CHECK_MALFORMED(length < hsize); /* Calculate the value size */ val_size = length - hsize; /* Which AVP? */ switch (avp_type) { case IPL2TP_AVP_MESSAGE_TYPE: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != 2); avph = &avp->message_type.head; avp->message_type.value = IP_GET_NTOHS(data); if (ipl2tp_message_recognized(avp->message_type.value) == 0) { if (ipl2tp_avp_mandatory(tunnel, avp, flags, avp_type, IPL2TP_STATUS_BADMTYPE, IPL2TP_ERR_VENDOR_ERROR_LAC, "invalid message type in")) { return IPL2TP_STATUS_TEARDOWN; } /* Non-mandatory AVP means ignore the whole message */ return IPL2TP_STATUS_IGNORE; } break; case IPL2TP_AVP_RANDOM_VECTOR: avph = &avp->random_vector.head; ipl2tp_util_vector_create(&avp->random_vector.vec, data, val_size); IPL2TP_AVP_PARSE_CHECK_NOMEM(avp->random_vector.vec.array); break; case IPL2TP_AVP_RESULT_CODE: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size < 2); avph = &avp->result_code.head; avp->result_code.result_code = IP_GET_NTOHS(data); data += sizeof(Ip_u16); avp->result_code.error_message = IP_NULL; if (val_size >= 4) { /* Error code present */ avp->result_code.error_code = IP_GET_NTOHS(data); data += sizeof(Ip_u16); if (val_size > 4) { char *cp = ipl2tp_util_strdup(data, val_size - 4); /* Error message present */ IPL2TP_AVP_PARSE_CHECK_NOMEM(cp); IPL2TP_SAFE_FREE(avp->result_code.error_message); avp->result_code.error_message = cp; } } break; case IPL2TP_AVP_PROTOCOL_VERSION: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != 2); avph = &avp->protocol_version.head; avp->protocol_version.ver = *data++; avp->protocol_version.rev = *data; break; case IPL2TP_AVP_FRAMING_CAPABILITIES: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != 4); avph = &avp->framing_capabilities.head; avp->framing_capabilities.value = ipl2tp_pw_bits2framing(IP_GET_NTOHL(data)); break; case IPL2TP_AVP_BEARER_CAPABILITIES: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != 4); avph = &avp->bearer_capabilities.head; avp->bearer_capabilities.value = ipl2tp_pw_bits2bearer(IP_GET_NTOHL(data)); break; case IPL2TP_AVP_TIE_BREAKER: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != 8); avph = &avp->tie_breaker.head; ipl2tp_util_vector_create(&avp->tie_breaker.vec, data, val_size); break; case IPL2TP_AVP_FIRMWARE_REVISION: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != 2); avph = &avp->firmware_revision.head; avp->firmware_revision.value = IP_GET_NTOHS(data); break; case IPL2TP_AVP_HOST_NAME: { char *cp = ipl2tp_util_strdup(data, val_size); avph = &avp->host_name.head; IPL2TP_AVP_PARSE_CHECK_NOMEM(cp); IPL2TP_SAFE_FREE(avp->host_name.value); avp->host_name.value = cp; break; } case IPL2TP_AVP_VENDOR_NAME: { char *cp = ipl2tp_util_strdup(data, val_size); avph = &avp->vendor_name.head; IPL2TP_AVP_PARSE_CHECK_NOMEM(cp); IPL2TP_SAFE_FREE(avp->vendor_name.value); avp->vendor_name.value = cp; break; } case IPL2TP_AVP_ASSIGNED_TUNNEL_ID: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != 2); avph = &avp->assigned_tunnel_id.head; avp->assigned_tunnel_id.value = IP_GET_NTOHS(data); break; case IPL2TP_AVP_RECEIVE_WINDOW_SIZE: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != 2); avph = &avp->receive_window_size.head; avp->receive_window_size.value = IP_GET_NTOHS(data); break; case IPL2TP_AVP_CHALLENGE: avph = &avp->challenge.head; ipl2tp_util_vector_create(&avp->challenge.vec, data, val_size); IPL2TP_AVP_PARSE_CHECK_NOMEM(avp->challenge.vec.array); break; case IPL2TP_AVP_CHALLENGE_RESPONSE: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != MD5_DIGEST_LENGTH); avph = &avp->challenge_response.head; ipl2tp_util_vector_create(&avp->challenge_response.vec, data, MD5_DIGEST_LENGTH); break; case IPL2TP_AVP_Q931_CAUSE_CODE: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size < 3); avph = &avp->q931_cause_code.head; avp->q931_cause_code.code = IP_GET_NTOHS(data); data += sizeof(Ip_u16); avp->q931_cause_code.msg = *data++; if (val_size > 3) { char *cp = ipl2tp_util_strdup(data, val_size - 3); IPL2TP_AVP_PARSE_CHECK_NOMEM(cp); IPL2TP_SAFE_FREE(avp->q931_cause_code.advisory_msg); avp->q931_cause_code.advisory_msg = cp; } break; case IPL2TP_AVP_ASSIGNED_SESSION_ID: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != 2); avph = &avp->assigned_session_id.head; avp->assigned_session_id.value = IP_GET_NTOHS(data); break; case IPL2TP_AVP_CALL_SERIAL_NUMBER: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != 4); avph = &avp->call_serial_number.head; avp->call_serial_number.value = IP_GET_NTOHL(data); break; case IPL2TP_AVP_MINIMUM_BPS: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != 4); avph = &avp->minimum_bps.head; avp->minimum_bps.value = IP_GET_NTOHL(data); break; case IPL2TP_AVP_MAXIMUM_BPS: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != 4); avph = &avp->maximum_bps.head; avp->maximum_bps.value = IP_GET_NTOHL(data); break; case IPL2TP_AVP_BEARER_TYPE: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != 4); avph = &avp->bearer_type.head; avp->bearer_type.value = ipl2tp_pw_bits2bearer(IP_GET_NTOHL(data)); break; case IPL2TP_AVP_FRAMING_TYPE: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != 4); avph = &avp->framing_type.head; avp->framing_type.value = ipl2tp_pw_bits2framing(IP_GET_NTOHL(data)); break; case IPL2TP_AVP_CALLED_NUMBER: { char *cp = ipl2tp_util_strdup(data, val_size); IPL2TP_AVP_PARSE_CHECK_NOMEM(cp); avph = &avp->called_number.head; IPL2TP_SAFE_FREE(avp->called_number.value); avp->called_number.value = cp; break; } case IPL2TP_AVP_CALLING_NUMBER: { char *cp = ipl2tp_util_strdup(data, val_size); IPL2TP_AVP_PARSE_CHECK_NOMEM(cp); avph = &avp->calling_number.head; IPL2TP_SAFE_FREE(avp->calling_number.value); avp->calling_number.value = cp; break; } case IPL2TP_AVP_SUB_ADDRESS: { char *cp = ipl2tp_util_strdup(data, val_size); IPL2TP_AVP_PARSE_CHECK_NOMEM(cp); avph = &avp->sub_address.head; IPL2TP_SAFE_FREE(avp->sub_address.value); avp->sub_address.value = cp; break; } case IPL2TP_AVP_TX_CONNECT_SPEED: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != 4); avph = &avp->tx_connect_speed.head; avp->tx_connect_speed.value = IP_GET_NTOHL(data); break; case IPL2TP_AVP_RX_CONNECT_SPEED: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != 4); avph = &avp->rx_connect_speed.head; avp->rx_connect_speed.value = IP_GET_NTOHL(data); break; case IPL2TP_AVP_PHYSICAL_CHANNEL_ID: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != 4); avph = &avp->physical_channel_id.head; avp->physical_channel_id.value = IP_GET_NTOHL(data); break; case IPL2TP_AVP_PRIVATE_GROUP_ID: avph = &avp->private_group_id.head; avp->private_group_id.value = ipl2tp_util_strdup(data, val_size); IPL2TP_AVP_PARSE_CHECK_NOMEM(avp->private_group_id.value); break; case IPL2TP_AVP_SEQUENCING_REQUIRED: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != 0); avph = &avp->sequencing_required.head; break; case IPL2TP_AVP_INITIAL_RECEIVED_LCP_CONFREQ: avph = &avp->initial_received_lcp_confreq.head; ipl2tp_util_vector_create(&avp->initial_received_lcp_confreq.vec, data, val_size); IPL2TP_AVP_PARSE_CHECK_NOMEM(avp->initial_received_lcp_confreq.vec.array); break; case IPL2TP_AVP_LAST_SENT_LCP_CONFREQ: avph = &avp->last_sent_lcp_confreq.head; ipl2tp_util_vector_create(&avp->last_sent_lcp_confreq.vec, data, val_size); IPL2TP_AVP_PARSE_CHECK_NOMEM(avp->last_sent_lcp_confreq.vec.array); break; case IPL2TP_AVP_LAST_RECEIVED_LCP_CONFREQ: avph = &avp->last_received_lcp_confreq.head; ipl2tp_util_vector_create(&avp->last_received_lcp_confreq.vec, data, val_size); IPL2TP_AVP_PARSE_CHECK_NOMEM(avp->last_received_lcp_confreq.vec.array); break; case IPL2TP_AVP_PROXY_AUTHEN_TYPE: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != 2); avph = &avp->proxy_authen_type.head; avp->proxy_authen_type.value = IP_GET_NTOHS(data); break; case IPL2TP_AVP_PROXY_AUTHEN_NAME: avph = &avp->proxy_authen_name.head; avp->proxy_authen_name.value = ipl2tp_util_strdup(data, val_size); IPL2TP_AVP_PARSE_CHECK_NOMEM(avp->proxy_authen_name.value); break; case IPL2TP_AVP_PROXY_AUTHEN_CHALLENGE: avph = &avp->proxy_authen_challenge.head; ipl2tp_util_vector_create(&avp->proxy_authen_challenge.vec, data, val_size); IPL2TP_AVP_PARSE_CHECK_NOMEM(avp->proxy_authen_challenge.vec.array); break; case IPL2TP_AVP_PROXY_AUTHEN_ID: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != 2); avph = &avp->proxy_authen_id.head; avp->proxy_authen_id.value = data[1]; break; case IPL2TP_AVP_PROXY_AUTHEN_RESPONSE: avph = &avp->proxy_authen_response.head; ipl2tp_util_vector_create(&avp->proxy_authen_response.vec, data, val_size); IPL2TP_AVP_PARSE_CHECK_NOMEM(avp->proxy_authen_response.vec.array); break; case IPL2TP_AVP_CALL_ERRORS: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != 26); avph = &avp->call_errors.head; data += sizeof(Ip_u16); avp->call_errors.crc_errors = IP_GET_NTOHL(data); data += sizeof(Ip_u32); avp->call_errors.framing_errors = IP_GET_NTOHL(data); data += sizeof(Ip_u32); avp->call_errors.hardware_overruns = IP_GET_NTOHL(data); data += sizeof(Ip_u32); avp->call_errors.buffer_overruns = IP_GET_NTOHL(data); data += sizeof(Ip_u32); avp->call_errors.timeout_errors = IP_GET_NTOHL(data); data += sizeof(Ip_u32); avp->call_errors.alignment_errors = IP_GET_NTOHL(data); break; case IPL2TP_AVP_ACCM: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != 10); avph = &avp->accm.head; data += sizeof(Ip_u16); avp->accm.send_accm = IP_GET_NTOHL(data); data += sizeof(Ip_u32); avp->accm.receive_accm = IP_GET_NTOHL(data); break; case IPL2TP_AVP_ROUTER_ID: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != 4); avph = &avp->router_id.head; avp->router_id.value = IP_GET_NTOHL(data); /**/ avp->v3_seen = IP_TRUE; break; case IPL2TP_AVP_ASSIGNED_CONTROL_CONNECTION_ID: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != 4); avph = &avp->assigned_control_connection_id.head; avp->assigned_control_connection_id.value = IP_GET_NTOHL(data); /**/ avp->v3_seen = IP_TRUE; break; case IPL2TP_AVP_PSEUDOWIRE_CAPABILITIES_LIST: { Ip_size_t i; Ip_u16 *pw_types; avph = &avp->pseudo_wires_list.head; ipl2tp_util_vector_create(&avp->pseudo_wires_list.vec, data, val_size); IPL2TP_AVP_PARSE_CHECK_NOMEM(avp->pseudo_wires_list.vec.array); /* Convert to host byte order */ pw_types = (Ip_u16 *)avp->pseudo_wires_list.vec.array; if (val_size > 0) { for (i = 0; i < avp->pseudo_wires_list.vec.size / 2; i++) { /* Fixup */ pw_types[i] = ip_htons(pw_types[i]); } } /**/ avp->v3_seen = IP_TRUE; } break; case IPL2TP_AVP_LOCAL_SESSION_ID: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != 4); avph = &avp->local_session_id.head; avp->local_session_id.value = IP_GET_NTOHL(data); break; case IPL2TP_AVP_REMOTE_SESSION_ID: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != 4); avph = &avp->remote_session_id.head; avp->remote_session_id.value = IP_GET_NTOHL(data); break; case IPL2TP_AVP_ASSIGNED_COOKIE: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != 0 && val_size != 4 && val_size != 8); avph = &avp->assigned_cookie.head; if (val_size) { ipl2tp_util_vector_create(&avp->assigned_cookie.vec, data, val_size); IPL2TP_AVP_PARSE_CHECK_NOMEM(avp->assigned_cookie.vec.array); } break; case IPL2TP_AVP_REMOTE_END_ID: avph = &avp->remote_end_id.head; ipl2tp_util_vector_create(&avp->remote_end_id.vec, data, val_size); IPL2TP_AVP_PARSE_CHECK_NOMEM(avp->remote_end_id.vec.array); break; case IPL2TP_AVP_PSEUDOWIRE_TYPE: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != 2); avph = &avp->pseudo_wire_type.head; avp->pseudo_wire_type.value = IP_GET_NTOHS(data); break; case IPL2TP_AVP_L2_SPECIFIC_SUBLAYER: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != 2); avph = &avp->l2_sublayer.head; avp->l2_sublayer.value = IP_GET_NTOHS(data); break; case IPL2TP_AVP_DATA_SEQUENCING: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != 2); avph = &avp->data_sequencing.head; avp->data_sequencing.value = IP_GET_NTOHS(data); break; case IPL2TP_AVP_CIRCUIT_STATUS: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != 2); avph = &avp->circuit_status.head; avp->circuit_status.value = IP_GET_NTOHS(data); break; case IPL2TP_AVP_PREFERRED_LANGUAGE: { char *cp = ipl2tp_util_strdup(data, val_size); IPL2TP_AVP_PARSE_CHECK_NOMEM(cp); IPL2TP_SAFE_FREE(avp->preferred_language.value); avph = &avp->preferred_language.head; avp->preferred_language.value = cp; break; } case IPL2TP_AVP_CONTROL_MESSAGE_AUTH_NONCE: avph = &avp->authentication_nonce.head; ipl2tp_util_vector_create(&avp->authentication_nonce.vec, data, val_size); IPL2TP_AVP_PARSE_CHECK_NOMEM(avp->authentication_nonce.vec.array); break; case IPL2TP_AVP_TX_CONNECT_SPEED64: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != 8); avph = &avp->tx_connect_speed64.head; IP_U64_SET(avp->tx_connect_speed64.value, IP_GET_NTOHL(data), IP_GET_NTOHL(data + 4)); break; case IPL2TP_AVP_RX_CONNECT_SPEED64: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != 8); avph = &avp->rx_connect_speed64.head; IP_U64_SET(avp->rx_connect_speed64.value, IP_GET_NTOHL(data), IP_GET_NTOHL(data + 4)); break; case IPL2TP_AVP_MESSAGE_DIGEST: { Ipl2tp_status message_digest = IPL2TP_STATUS_IGNORE; /* Only treat it 'magically' when in SCCRQ */ if (avp->message_type.value == IPL2TP_MSG_SCCRQ) { /* We're in SCCRQ, teardown is the only option */ message_digest = IPL2TP_STATUS_TEARDOWN; } /* We must be directly after the message type */ if (avp_no != 1) { IPCOM_LOG2(INFO, "%s: Discarding %s due to badly placed message digest", ipl2tp_versions[avp->version], ipl2tp_message_name(avp->message_type.value)); return message_digest; } avph = &avp->message_digest.head; avp->message_digest.type = *data; /* Done for this loop */ if (hidden == 0) break; switch (avp->message_digest.type) { case IPL2TP_MIB_AUTH_MD5: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != (MD5_DIGEST_LENGTH + 1)); /* Don't */ if (ipl2tp_avp_hmac_md5_verify(tunnel, message_base, message_length, data + 1) == IP_FALSE) { IPCOM_LOG2(INFO, "%s: Discarding %s due to bad message digest", ipl2tp_versions[avp->version], ipl2tp_message_name(avp->message_type.value)); return message_digest; } else { IPCOM_LOG2(DEBUG, "%s: %s good MD5 message digest", ipl2tp_versions[avp->version], ipl2tp_message_name(avp->message_type.value)); } break; case IPL2TP_MIB_AUTH_SHA: IPL2TP_AVP_PARSE_CHECK_MALFORMED(val_size != (SHA_DIGEST_LENGTH + 1)); /* Don't */ if (ipl2tp_avp_hmac_sha_verify(tunnel, message_base, message_length, data + 1) == IP_FALSE) { IPCOM_LOG2(INFO, "%s: Discarding %s due to bad message digest", ipl2tp_versions[avp->version], ipl2tp_message_name(avp->message_type.value)); return message_digest; } else { IPCOM_LOG2(DEBUG, "%s: %s good SHA message digest", ipl2tp_versions[avp->version], ipl2tp_message_name(avp->message_type.value)); } break; default: /* Unknown Hash method */ IPCOM_LOG3(INFO, "%s: Discarding %s due to unknown hash method (%u)", ipl2tp_versions[avp->version], ipl2tp_message_name(avp->message_type.value), avp->message_digest.type); return message_digest; } } break; case IPL2TP_AVP_APPLICATION_CODE: default: IPL2TP_AVP_PARSE_ERROUT(IPL2TP_STATUS_BADMTYPE, IPL2TP_ERR_UNKNOWN_M_AVP, "unknown"); } /* Assign header stuff if we got a valid AVP */ if (avph != IP_NULL) { avph->ok = 1; avph->mandatory = (Ip_u8)((flags & IPL2TP_AVP_FLAG_MANDATORY) != 0); avph->vendor_id = vendor_id; BITSET(&avp->map, avp_type); } continue; errout: if (ipl2tp_avp_mandatory(tunnel, avp, flags, avp_type, errout_status, errout_code, errout_message)) { return IPL2TP_STATUS_TEARDOWN; } continue; } return ipl2tp_avp_validate(avp, hidden); } /* *=========================================================================== * ipl2tp_avp_delete *=========================================================================== * Description: Free all memory allocated in an AVP. * Parameters: avp - AVP descriptor. * Returns: * */ IP_GLOBAL void ipl2tp_avp_delete(Ipl2tp_avp *avp) { if (avp != IP_NULL) { IPL2TP_SAFE_FREE(avp->random_vector.vec.array); IPL2TP_SAFE_FREE(avp->result_code.error_message); IPL2TP_SAFE_FREE(avp->host_name.value); IPL2TP_SAFE_FREE(avp->vendor_name.value); IPL2TP_SAFE_FREE(avp->challenge.vec.array); IPL2TP_SAFE_FREE(avp->q931_cause_code.advisory_msg); IPL2TP_SAFE_FREE(avp->called_number.value); IPL2TP_SAFE_FREE(avp->calling_number.value); IPL2TP_SAFE_FREE(avp->sub_address.value); IPL2TP_SAFE_FREE(avp->private_group_id.value); IPL2TP_SAFE_FREE(avp->initial_received_lcp_confreq.vec.array); IPL2TP_SAFE_FREE(avp->last_sent_lcp_confreq.vec.array); IPL2TP_SAFE_FREE(avp->last_received_lcp_confreq.vec.array); IPL2TP_SAFE_FREE(avp->proxy_authen_name.value); IPL2TP_SAFE_FREE(avp->proxy_authen_challenge.vec.array); IPL2TP_SAFE_FREE(avp->proxy_authen_response.vec.array); IPL2TP_SAFE_FREE(avp->tie_breaker.vec.array); IPL2TP_SAFE_FREE(avp->challenge_response.vec.array); /* v3 L2TP AVP */ IPL2TP_SAFE_FREE(avp->pseudo_wires_list.vec.array); IPL2TP_SAFE_FREE(avp->preferred_language.value); IPL2TP_SAFE_FREE(avp->authentication_nonce.vec.array); IPL2TP_SAFE_FREE(avp->assigned_cookie.vec.array); IPL2TP_SAFE_FREE(avp->remote_end_id.vec.array); IPL2TP_SAFE_FREE(avp); } } /* *=========================================================================== * ipl2tp_avp_challenge_size *=========================================================================== * Description: Calculate the length of the challenge * Parameters: * Returns: The length of a challenge. * */ IP_GLOBAL Ip_size_t ipl2tp_avp_challenge_size(void) { Ip_size_t len; #if IPL2TP_MIN_CHALLENGE_LENGTH == IPL2TP_MAX_CHALLENGE_LENGTH len = IPL2TP_MIN_CHALLENGE_LENGTH; #else len = IPL2TP_MIN_CHALLENGE_LENGTH + (ipcom_port_random() % (IPL2TP_MAX_CHALLENGE_LENGTH - IPL2TP_MIN_CHALLENGE_LENGTH)); #endif return len; } /* *=========================================================================== * ipl2tp_avp_challenge_init *=========================================================================== * Description: Initialize challenge for tunnel. * Parameters: vec - Data to create challenge in, * tunnel - Tunnel to create challenge for * id - ID of request. * Returns: * */ IP_GLOBAL void ipl2tp_avp_challenge_init(Ipl2tp_vector *vec, Ipl2tp_tunnel *tunnel, Ip_u8 id) { Ip_u8 digest[MD5_DIGEST_LENGTH]; ipl2tp_util_vector_random(vec, ipl2tp_avp_challenge_size()); /* Increment to get ID for reply */ id++; /* Calculate and remember expected MD5 checksum */ ipl2tp_avp_md5(digest, tunnel, &id, 1, vec->array, vec->size); /* Create a vector of the response */ ipl2tp_util_vector_create(&tunnel->expected_response, digest, MD5_DIGEST_LENGTH); } /* *=========================================================================== * ipl2tp_avp_challenge_response_init *=========================================================================== * Description: Initialize challenge response for tunnel. * Parameters: digest - Where to create the digest. * tunnel - The tunnel we're currently working with * avp - The AVP structure. * Returns: IPL2TP_STATUS_OK if it went well. * */ IP_GLOBAL Ipl2tp_status ipl2tp_avp_challenge_response_init(Ipl2tp_vector *digest, Ipl2tp_tunnel *tunnel, Ipl2tp_avp *avp) { Ip_u8 id = (Ip_u8)(avp->message_type.value + 1); Ip_u8 tmpdigest[MD5_DIGEST_LENGTH]; /* Clear */ digest->array = IP_NULL; digest->size = 0; /* Ignore if no challenge received */ if (avp->challenge.head.ok == 0) { return IPL2TP_STATUS_IGNORE; } /* Ignore if no secret is configured */ if (tunnel->attr.secret.size == 0) { IPL2TP_TUNNEL_RESULT_SET(tunnel, IPL2TP_STATUS_BADAUTH, IPL2TP_RES_StopCCN_NOT_AUTHORIZED, 0, "No authentication secret configured"); IPCOM_LOG3(WARNING, "%s(%d,%s): No authentication secret configured", ipl2tp_versions[tunnel->local.key.version], (int)tunnel->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr)); return IPL2TP_STATUS_ERROR; } IPCOM_LOG3(DEBUG, "%s(%d,%s): Responding to challenge", ipl2tp_versions[tunnel->local.key.version], (int)tunnel->local.key.ident, ipl2tp_util_ntoa(&tunnel->remote.addr)); ipl2tp_avp_md5(tmpdigest, tunnel, &id, 1, avp->challenge.vec.array, avp->challenge.vec.size); /* Create the vector */ ipl2tp_util_vector_create(digest, tmpdigest, MD5_DIGEST_LENGTH); return IPL2TP_STATUS_OK; } /* *=========================================================================== * ipl2tp_avp_challenge_verify *=========================================================================== * Description: Verify challenge for tunnel. * Parameters: avp - Challenge to verify * tunnel - Tunnel to create challenge for * Returns: * */ IP_GLOBAL Ip_bool ipl2tp_avp_challenge_verify(Ipl2tp_avp *avp, Ipl2tp_tunnel *tunnel) { if (avp->challenge_response.vec.size != tunnel->expected_response.size) return IP_FALSE; if (ipcom_memcmp(avp->challenge_response.vec.array, tunnel->expected_response.array, MD5_DIGEST_LENGTH) != 0) return IP_FALSE; return IP_TRUE; } /* *=========================================================================== * ipl2tp_avp_hmac_md5_digest *=========================================================================== * Description: Calculate MD5 digest for a given buffer and tunnel * Parameters: tunnel - Tunnel to create digest for * buffer - To calculate digest over. * size - Size of buffer * nonce1 - The first nonce * nonce2 - The second nonce * digest - Pointer to digest. * Returns: * */ IP_GLOBAL Ip_bool ipl2tp_avp_hmac_md5_digest(Ipl2tp_tunnel *tunnel, void *buffer, Ip_size_t size, Ipl2tp_vector *nonce1, Ipl2tp_vector *nonce2, void *digest) { MD5_CTX i_ctx; MD5_CTX o_ctx; Ipl2tp_vector secret; Ip_size_t i; /* Only once */ if (ipcom_once(&ipl2tp_avp_hmac_once, ipl2tp_avp_hmac_setup, IP_NULL) != IPCOM_SUCCESS) return IP_FALSE; /**/ if (tunnel->local.key.version != IPL2TP_VERSION_L2TPV3) return IP_FALSE; /**/ if (tunnel->attr.secret.size == 0) return IP_FALSE; /**/ if (tunnel->v.v3.msg_digest_shared_key.size == 0) { /* Create the shared key */ ipl2tp_avp_hmac_msg_digest_shared_key(tunnel); } /* Clear the digest */ ipcom_memset(digest, 0, MD5_DIGEST_LENGTH); /* Duplicate the secret */ secret.array = IP_NULL; ipl2tp_util_vector_dup(&secret, &tunnel->v.v3.msg_digest_shared_key); /* Sanity */ if (secret.array == IP_NULL) return IP_FALSE; /* Compute the I pad. */ for (i = 0; i < secret.size; i++) secret.array[i] ^= HMAC_IPAD; MD5_Init(&i_ctx); MD5_Update(&i_ctx, secret.array, secret.size); MD5_Update(&i_ctx, ipl2tp_avp_hmac_ipad, 64 - secret.size); /* We do have a nonce */ if (nonce1 != IP_NULL && nonce1->size != 0 && nonce2 != IP_NULL && nonce2->size != 0) { /* Done deal */ MD5_Update(&i_ctx, nonce1->array, nonce1->size); MD5_Update(&i_ctx, nonce2->array, nonce2->size); } MD5_Update(&i_ctx, buffer, size); MD5_Final(digest, &i_ctx); /* Compute the O pad. */ for (i = 0; i < secret.size; i++) secret.array[i] ^= (HMAC_IPAD ^ HMAC_OPAD); MD5_Init(&o_ctx); MD5_Update(&o_ctx, secret.array, secret.size); MD5_Update(&o_ctx, ipl2tp_avp_hmac_opad, 64 - secret.size); /* Outer HMAC */ MD5_Update(&o_ctx, digest, MD5_DIGEST_LENGTH); MD5_Final(digest, &o_ctx); IPL2TP_SAFE_FREE(secret.array); return IP_TRUE; } /* *=========================================================================== * ipl2tp_avp_hmac_sha_digest *=========================================================================== * Description: Calculate SHA digest for a given buffer and tunnel * Parameters: tunnel - Tunnel to create digest for * buffer - To calculate digest over. * size - Size of buffer * nonce1 - The first nonce * nonce2 - The second nonce * digest - Pointer to digest. * Returns: * */ IP_GLOBAL Ip_bool ipl2tp_avp_hmac_sha_digest(Ipl2tp_tunnel *tunnel, void *buffer, Ip_size_t size, Ipl2tp_vector *nonce1, Ipl2tp_vector *nonce2, void *digest) { SHA_CTX i_ctx; SHA_CTX o_ctx; Ipl2tp_vector secret; Ip_size_t i; /* Only once */ if (ipcom_once(&ipl2tp_avp_hmac_once, ipl2tp_avp_hmac_setup, IP_NULL) != IPCOM_SUCCESS) return IP_FALSE; /**/ if (tunnel->local.key.version != IPL2TP_VERSION_L2TPV3) return IP_FALSE; /**/ if (tunnel->attr.secret.size == 0) return IP_FALSE; /**/ if (tunnel->v.v3.msg_digest_shared_key.size == 0) { /* Create the shared key */ ipl2tp_avp_hmac_msg_digest_shared_key(tunnel); } /* Clear the digest */ ipcom_memset(digest, 0, SHA_DIGEST_LENGTH); /* Duplicate the secret */ secret.array = IP_NULL; ipl2tp_util_vector_dup(&secret, &tunnel->v.v3.msg_digest_shared_key); /* Sanity */ if (secret.array == IP_NULL) return IP_FALSE; /* Compute the I pad. */ for (i = 0; i < secret.size; i++) secret.array[i] ^= HMAC_IPAD; SHA1_Init(&i_ctx); SHA1_Update(&i_ctx, secret.array, secret.size); SHA1_Update(&i_ctx, ipl2tp_avp_hmac_ipad, 64 - secret.size); /* We do have a nonce */ if (nonce1 != IP_NULL && nonce1->size != 0 && nonce2 != IP_NULL && nonce2->size != 0) { /* Done deal */ SHA1_Update(&i_ctx, nonce1->array, nonce1->size); SHA1_Update(&i_ctx, nonce2->array, nonce2->size); } SHA1_Update(&i_ctx, buffer, size); SHA1_Final(digest, &i_ctx); /* Compute the O pad. */ for (i = 0; i < secret.size; i++) secret.array[i] ^= (HMAC_IPAD ^ HMAC_OPAD); SHA1_Init(&o_ctx); SHA1_Update(&o_ctx, secret.array, secret.size); SHA1_Update(&o_ctx, ipl2tp_avp_hmac_opad, 64 - secret.size); /* Outer HMAC */ SHA1_Update(&o_ctx, digest, SHA_DIGEST_LENGTH); SHA1_Final(digest, &o_ctx); IPL2TP_SAFE_FREE(secret.array); return IP_TRUE; } /* *=========================================================================== * ipl2tp_avp_set_protocol_version *=========================================================================== * Description: Enter protocol version AVP. * Parameters: cont - Message container * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_protocol_version(Ipl2tp_message *cont) { Ip_u8 avp_data[2]; /* Initialize the data */ avp_data[0] = IPL2TP_PROTOCOL_VER; avp_data[1] = IPL2TP_PROTOCOL_REV; ipl2tp_avp_set(cont, IPL2TP_AVP_PROTOCOL_VERSION, IPL2TP_AVP_FLAG_MANDATORY, avp_data, 2); } /* *=========================================================================== * ipl2tp_avp_set_assigned_tunnel_id *=========================================================================== * Description: Set assigned tunnel ID AVP. * Parameters: cont - Message container * value - The value * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_assigned_tunnel_id(Ipl2tp_message *cont, Ipl2tp_ident value) { Ip_u16 avp_value = ip_htons((Ip_u16)value); ipl2tp_avp_set(cont, IPL2TP_AVP_ASSIGNED_TUNNEL_ID, IPL2TP_AVP_FLAG_MANDATORY | IPL2TP_AVP_FLAG_HIDDEN, &avp_value, 2); } /* *=========================================================================== * ipl2tp_avp_set_host_name *=========================================================================== * Description: Enter host name AVP. * Parameters: cont - Message container * value - The value. * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_host_name(Ipl2tp_message *cont, CONST char *value) { ipl2tp_avp_set(cont, IPL2TP_AVP_HOST_NAME, IPL2TP_AVP_FLAG_MANDATORY, (void *)value, (Ip_u16)(ipcom_strlen(value) + 1)); } /* *=========================================================================== * ipl2tp_avp_set_framing_capabilities *=========================================================================== * Description: Enter framing capabilities AVP. * Parameters: cont - Message container * value - The framing capabilities * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_framing_capabilities(Ipl2tp_message *cont, Ip_u32 value) { Ip_u32 bits = ip_htonl(ipl2tp_pw_framing2bits(value)); ipl2tp_avp_set(cont, IPL2TP_AVP_FRAMING_CAPABILITIES, IPL2TP_AVP_FLAG_MANDATORY | IPL2TP_AVP_FLAG_HIDDEN, &bits, 4); } /* *=========================================================================== * ipl2tp_avp_set_message_type *=========================================================================== * Description: Enter message type AVP. * Parameters: cont - Message container * value - AVP Type * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_message_type(Ipl2tp_message *cont, Ip_u16 value) { value = ip_htons(value); ipl2tp_avp_set(cont, IPL2TP_AVP_MESSAGE_TYPE, IPL2TP_AVP_FLAG_MANDATORY, &value, 2); } /* *=========================================================================== * ipl2tp_avp_set_bearer_capabilities *=========================================================================== * Description: Enter bearer capabilities AVP. * Parameters: cont - Message container * value - Bearer capabilities * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_bearer_capabilities(Ipl2tp_message *cont, Ip_u32 value) { Ip_u32 bits = ip_htonl(ipl2tp_pw_bearer2bits(value)); ipl2tp_avp_set(cont, IPL2TP_AVP_BEARER_CAPABILITIES, IPL2TP_AVP_FLAG_MANDATORY | IPL2TP_AVP_FLAG_HIDDEN, &bits, 4); } /* *=========================================================================== * ipl2tp_avp_set_receive_window_size *=========================================================================== * Description: Enter receive window size AVP. * Parameters: cont - The message container * value - The value * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_receive_window_size(Ipl2tp_message *cont, Ip_u16 value) { value = ip_htons(value); ipl2tp_avp_set(cont, IPL2TP_AVP_RECEIVE_WINDOW_SIZE, IPL2TP_AVP_FLAG_MANDATORY | IPL2TP_AVP_FLAG_HIDDEN, &value, 2); } /* *=========================================================================== * ipl2tp_avp_set_challenge *=========================================================================== * Description: Enter challenge AVP. * Parameters: cont - Message container * vec - The vector * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_challenge(Ipl2tp_message *cont, Ipl2tp_vector *vec) { ipl2tp_avp_set(cont, IPL2TP_AVP_CHALLENGE, IPL2TP_AVP_FLAG_MANDATORY | IPL2TP_AVP_FLAG_HIDDEN, vec->array, vec->size); } /* *=========================================================================== * ipl2tp_avp_set_firmware_revision *=========================================================================== * Description: Enter firmware revision AVP. * Parameters: cont - The message container * value - The firmware revision. * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_firmware_revision(Ipl2tp_message *cont, Ip_u16 value) { value = ip_htons(value); ipl2tp_avp_set(cont, IPL2TP_AVP_FIRMWARE_REVISION, IPL2TP_AVP_FLAG_HIDDEN, &value, 2); } /* *=========================================================================== * ipl2tp_avp_set_vendor_name *=========================================================================== * Description: Enter vendor name AVP. * Parameters: cont - Message container * value - vendor name string * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_vendor_name(Ipl2tp_message *cont, CONST char *value) { ipl2tp_avp_set(cont, IPL2TP_AVP_VENDOR_NAME, IPL2TP_AVP_FLAG_HIDDEN, (void *)value, (Ip_u16)(ipcom_strlen(value) + 1)); } /* *=========================================================================== * ipl2tp_avp_set_tie_breaker *=========================================================================== * Description: Enter tie breaker AVP. * Parameters: cont - Message container * vec - Data. * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_tie_breaker(Ipl2tp_message *cont, Ipl2tp_vector *vec) { ipl2tp_avp_set(cont, IPL2TP_AVP_TIE_BREAKER, IPL2TP_AVP_FLAG_NONE, vec->array, vec->size); } /* *=========================================================================== * ipl2tp_avp_set_result_code *=========================================================================== * Description: Set result code. * Parameters: cont - Message container * result_code - The result code * error_code - The error code * error_message - The error message * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_result_code(Ipl2tp_message *cont, Ip_u16 result_code, Ip_u16 error_code, CONST char *error_message) { Ipl2tp_avp_message_header avp_hdr; struct Ip_iovec avp_iovec[3]; Ip_u16 i = 0; /* Initialize the AVP header */ IPL2TP_AVP_INITIALIZE(&avp_hdr, IPL2TP_AVP_RESULT_CODE, IPL2TP_AVP_FLAG_MANDATORY); /* Prepare the data */ result_code = ip_htons(result_code); /* Setup the io vector */ avp_iovec[i].iov_base = &result_code; avp_iovec[i].iov_len = 2; i++; if (error_code || error_message != IP_NULL) { error_code = ip_htons(error_code); avp_iovec[i].iov_base = &error_code; avp_iovec[i].iov_len = 2; i++; if (error_message != IP_NULL) { avp_iovec[i].iov_base = (void *)error_message; avp_iovec[i].iov_len = ipcom_strlen(error_message) + 1; i++; } } /* Update the AVP. */ ipl2tp_avp_update(cont, &avp_hdr, avp_iovec, i); } /* *=========================================================================== * ipl2tp_avp_set_challenge_response *=========================================================================== * Description: Enter challenge response AVP. * Parameters: cont - Pointer to message. * value - Pointer to 16 byte challenge repsonse * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_challenge_response(Ipl2tp_message *cont, Ipl2tp_vector *value) { ipl2tp_avp_set(cont, IPL2TP_AVP_CHALLENGE_RESPONSE, IPL2TP_AVP_FLAG_MANDATORY | IPL2TP_AVP_FLAG_HIDDEN, value->array, value->size); } /* *=========================================================================== * ipl2tp_avp_set_assigned_session_id *=========================================================================== * Description: Enter assigned session ID AVP. * Parameters: cont - Message container * value - assigned session id * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_assigned_session_id(Ipl2tp_message *cont, Ipl2tp_ident value) { Ip_u16 v16 = ip_htons((Ip_u16)value); ipl2tp_avp_set(cont, IPL2TP_AVP_ASSIGNED_SESSION_ID, IPL2TP_AVP_FLAG_MANDATORY | IPL2TP_AVP_FLAG_HIDDEN, &v16, 2); } /* *=========================================================================== * ipl2tp_avp_set_call_serial_number *=========================================================================== * Description: Enter call serial number AVP. * Parameters: cont - The message container * value - The value. * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_call_serial_number(Ipl2tp_message *cont, Ip_u32 value) { value = ip_htonl(value); ipl2tp_avp_set(cont, IPL2TP_AVP_CALL_SERIAL_NUMBER, IPL2TP_AVP_FLAG_MANDATORY | IPL2TP_AVP_FLAG_HIDDEN, &value, 4); } /* *=========================================================================== * ipl2tp_avp_set_called_number *=========================================================================== * Description: Enter called number AVP. * Parameters: cont - The message container * value - The value. * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_called_number(Ipl2tp_message *cont, CONST char *value) { ipl2tp_avp_set(cont, IPL2TP_AVP_CALLED_NUMBER, IPL2TP_AVP_FLAG_MANDATORY | IPL2TP_AVP_FLAG_HIDDEN, (void *)value, (Ip_u16)(ipcom_strlen(value) + 1)); } /* *=========================================================================== * ipl2tp_avp_set_calling_number *=========================================================================== * Description: Enter calling number AVP. * Parameters: cont - Message container * value - The value * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_calling_number(Ipl2tp_message *cont, CONST char *value) { ipl2tp_avp_set(cont, IPL2TP_AVP_CALLING_NUMBER, IPL2TP_AVP_FLAG_MANDATORY | IPL2TP_AVP_FLAG_HIDDEN, (void *)value, (Ip_u16)(ipcom_strlen(value) + 1)); } /* *=========================================================================== * ipl2tp_avp_set_physical_channel_id *=========================================================================== * Description: Enter physical channel ID AVP. * Parameters: cont - The container * value - The value * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_physical_channel_id(Ipl2tp_message *cont, Ip_u32 value) { value = ip_htonl(value); ipl2tp_avp_set(cont, IPL2TP_AVP_PHYSICAL_CHANNEL_ID, IPL2TP_AVP_FLAG_HIDDEN, &value, 4); } /* *=========================================================================== * ipl2tp_avp_set_sub_address *=========================================================================== * Description: Enter sub address AVP. * Parameters: cont - The message container * value - The value. * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_sub_address(Ipl2tp_message *cont, CONST char *value) { ipl2tp_avp_set(cont, IPL2TP_AVP_SUB_ADDRESS, IPL2TP_AVP_FLAG_MANDATORY | IPL2TP_AVP_FLAG_HIDDEN, (void *)value, (Ip_u16)(ipcom_strlen(value) + 1)); } /* *=========================================================================== * ipl2tp_avp_set_framing_type *=========================================================================== * Description: Enter framing type AVP. * Parameters: cont - Container * value - The value * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_framing_type(Ipl2tp_message *cont, Ip_u32 value) { Ip_u32 bits = ip_htonl(ipl2tp_pw_framing2bits(value)); ipl2tp_avp_set(cont, IPL2TP_AVP_FRAMING_TYPE, IPL2TP_AVP_FLAG_MANDATORY | IPL2TP_AVP_FLAG_HIDDEN, &bits, 4); } /* *=========================================================================== * ipl2tp_avp_set_tx_connect_speed *=========================================================================== * Description: Enter Tx connect speed AVP. * Parameters: cont - Container * value - The value * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_tx_connect_speed(Ipl2tp_message *cont, Ip_u32 value) { value = ip_htonl(value); ipl2tp_avp_set(cont, IPL2TP_AVP_TX_CONNECT_SPEED, IPL2TP_AVP_FLAG_MANDATORY | IPL2TP_AVP_FLAG_HIDDEN, &value, 4); } /* *=========================================================================== * ipl2tp_avp_set_private_group_id *=========================================================================== * Description: Enter private group ID AVP. * Parameters: cont - Container * value - The value * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_private_group_id(Ipl2tp_message *cont, CONST char *value) { ipl2tp_avp_set(cont, IPL2TP_AVP_PRIVATE_GROUP_ID, IPL2TP_AVP_FLAG_HIDDEN, (void *)value, (Ip_u16)(ipcom_strlen(value) + 1)); } /* *=========================================================================== * ipl2tp_avp_set_rx_connect_speed *=========================================================================== * Description: Enter Rx connect speed AVP. * Parameters: cont - Container * value - The value * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_rx_connect_speed(Ipl2tp_message *cont, Ip_u32 value) { value = ip_htonl(value); ipl2tp_avp_set(cont, IPL2TP_AVP_RX_CONNECT_SPEED, IPL2TP_AVP_FLAG_HIDDEN, &value, 4); } /* *=========================================================================== * ipl2tp_avp_set_sequencing_required *=========================================================================== * Description: Enter sequencing required AVP. * Parameters: cont - Container * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_sequencing_required(Ipl2tp_message *cont) { Ipl2tp_avp_message_header avp_hdr; /* Initialize the AVP header */ IPL2TP_AVP_INITIALIZE(&avp_hdr, IPL2TP_AVP_SEQUENCING_REQUIRED, IPL2TP_AVP_FLAG_MANDATORY); /* Update the AVP. */ ipl2tp_avp_update(cont, &avp_hdr, IP_NULL, 0); } /* *=========================================================================== * ipl2tp_avp_set_bearer_type *=========================================================================== * Description: Enter bearer type AVP. * Parameters: cont - Container * value - The value * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_bearer_type(Ipl2tp_message *cont, Ip_u32 value) { Ip_u32 bits = ip_htonl(ipl2tp_pw_bearer2bits(value)); ipl2tp_avp_set(cont, IPL2TP_AVP_BEARER_TYPE, IPL2TP_AVP_FLAG_MANDATORY | IPL2TP_AVP_FLAG_HIDDEN, &bits, 4); } /* *=========================================================================== * ipl2tp_avp_set_initial_received_lcp_confreq *=========================================================================== * Description: Enter initial received CONFREQ AVP. * Parameters: cont - Container * value - The value * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_initial_received_lcp_confreq(Ipl2tp_message *cont, CONST Ipl2tp_vector *value) { ipl2tp_avp_set(cont, IPL2TP_AVP_INITIAL_RECEIVED_LCP_CONFREQ, IPL2TP_AVP_FLAG_HIDDEN, value->array, (Ip_u16) (value->size)); } /* *=========================================================================== * ipl2tp_avp_set_last_sent_lcp_confreq *=========================================================================== * Description: Enter last sent CONFREQ AVP. * Parameters: cont - Container * value - The value * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_last_sent_lcp_confreq(Ipl2tp_message *cont, CONST Ipl2tp_vector *value) { ipl2tp_avp_set(cont, IPL2TP_AVP_LAST_SENT_LCP_CONFREQ, IPL2TP_AVP_FLAG_HIDDEN, value->array, (Ip_u16) (value->size)); } /* *=========================================================================== * ipl2tp_avp_set_last_received_lcp_confreq *=========================================================================== * Description: Enter last received CONFREQ AVP. * Parameters: cont - Container * value - The value * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_last_received_lcp_confreq(Ipl2tp_message *cont, CONST Ipl2tp_vector *value) { ipl2tp_avp_set(cont, IPL2TP_AVP_LAST_RECEIVED_LCP_CONFREQ, IPL2TP_AVP_FLAG_HIDDEN, value->array, (Ip_u16) (value->size)); } /* *=========================================================================== * ipl2tp_avp_set_proxy_authen_type *=========================================================================== * Description: Enter proxy authen type AVP. * Parameters: cont - Container * value - The value * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_proxy_authen_type(Ipl2tp_message *cont, Ip_u16 value) { value = ip_htons(value); ipl2tp_avp_set(cont, IPL2TP_AVP_PROXY_AUTHEN_TYPE, IPL2TP_AVP_FLAG_HIDDEN, &value, 2); } /* *=========================================================================== * ipl2tp_avp_set_proxy_authen_name *=========================================================================== * Description: Enter proxy authen name AVP. * Parameters: cont - Container * value - The value * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_proxy_authen_name(Ipl2tp_message *cont, CONST char *value) { ipl2tp_avp_set(cont, IPL2TP_AVP_PROXY_AUTHEN_NAME, IPL2TP_AVP_FLAG_HIDDEN, (void *)value, (Ip_u16)(ipcom_strlen(value) + 1)); } /* *=========================================================================== * ipl2tp_avp_set_proxy_authen_challenge *=========================================================================== * Description: Enter proxy authen challenge AVP. * Parameters: cont - Container * vec - The value * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_proxy_authen_challenge(Ipl2tp_message *cont, Ipl2tp_vector *vec) { ipl2tp_avp_set(cont, IPL2TP_AVP_PROXY_AUTHEN_CHALLENGE, IPL2TP_AVP_FLAG_HIDDEN, vec->array, vec->size); } /* *=========================================================================== * ipl2tp_avp_set_proxy_authen_id *=========================================================================== * Description: Enter proxy authen id AVP. * Parameters: cont - Container * value - The value * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_proxy_authen_id(Ipl2tp_message *cont, Ip_u16 value) { value = ip_htons(value); ipl2tp_avp_set(cont, IPL2TP_AVP_PROXY_AUTHEN_ID, IPL2TP_AVP_FLAG_HIDDEN, &value, 2); } /* *=========================================================================== * ipl2tp_avp_set_proxy_authen_response *=========================================================================== * Description: Enter proxy authen response AVP. * Parameters: cont - Container * vec - The value * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_proxy_authen_response(Ipl2tp_message *cont, Ipl2tp_vector *vec) { ipl2tp_avp_set(cont, IPL2TP_AVP_PROXY_AUTHEN_RESPONSE, IPL2TP_AVP_FLAG_HIDDEN, vec->array, vec->size); } /* *=========================================================================== * ipl2tp_avp_set_digest *=========================================================================== * Description: Set the digest. * Parameters: cont - The message container * type - Type of digest. * hs - V2/V3 discovery? * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_digest(Ipl2tp_message *cont, Ip_u8 type, Ip_bool hs) { char buf[32]; Ip_u16 flags = IPL2TP_AVP_HANDSHAKE(hs, IPL2TP_AVP_FLAG_NONE); Ip_u16 size = 1; switch (type) { case IPL2TP_MIB_AUTH_MD5: /* MD5 */ size += MD5_DIGEST_LENGTH; break; case IPL2TP_MIB_AUTH_SHA: size += SHA_DIGEST_LENGTH; break; default: /* Unknown digest type */ return; } /* The digest must be 0 during hash */ ipcom_memset(buf, 0, size); /* Store the buffer */ buf[0] = type; /* Store the digest */ cont->admin.digest = ipl2tp_avp_set(cont, IPL2TP_AVP_MESSAGE_DIGEST, flags, buf, size); /* Skip the one byte type */ cont->admin.digest = (Ip_u8 *)cont->admin.digest + 1; } /* *=========================================================================== * ipl2tp_avp_set_control_message_auth_nonce *=========================================================================== * Description: Add the control message authentication nonce * Parameters: cont - The message container * vec - Nonce * hs - Handshake attempt? * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_control_message_auth_nonce(Ipl2tp_message *cont, Ipl2tp_vector *vec, Ip_bool hs) { Ip_u16 flags = IPL2TP_AVP_HANDSHAKE(hs, IPL2TP_AVP_FLAG_NONE); ipl2tp_avp_set(cont, IPL2TP_AVP_CONTROL_MESSAGE_AUTH_NONCE, flags, vec->array, vec->size); } /* *=========================================================================== * ipl2tp_avp_set_router_id *=========================================================================== * Description: Set the router identifier * Parameters: cont - The message container * value - The value * hs - Is this a handshake attempt. * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_router_id(Ipl2tp_message *cont, Ip_u32 value, Ip_bool hs) { Ip_u16 flags = IPL2TP_AVP_HANDSHAKE(hs, IPL2TP_AVP_FLAG_NONE); value = ip_htonl(value); ipl2tp_avp_set(cont, IPL2TP_AVP_ROUTER_ID, flags, &value, 4); } /* *=========================================================================== * ipl2tp_avp_set_assigned_control_connection_id *=========================================================================== * Description: Set the assigned control connection identifier * Parameters: cont - The message container * value - The value * hs - Is this a handshake attempt. * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_assigned_control_connection_id(Ipl2tp_message *cont, Ip_u32 value, Ip_bool hs) { Ip_u16 flags = IPL2TP_AVP_HANDSHAKE(hs, IPL2TP_AVP_FLAG_HIDDEN); value = ip_htonl(value); ipl2tp_avp_set(cont, IPL2TP_AVP_ASSIGNED_CONTROL_CONNECTION_ID, flags, &value, 4); } /* *=========================================================================== * ipl2tp_avp_set_pseudowire_capabilities_list *=========================================================================== * Description: Set the pseudowire capabilities AVP * Parameters: cont - The message container * vec - The value array * hs - Is this a handshake attempt. * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_pseudowire_capabilities_list(Ipl2tp_message *cont, Ipl2tp_vector *vec, Ip_bool hs) { Ip_u16 flags = IPL2TP_AVP_HANDSHAKE(hs, IPL2TP_AVP_FLAG_HIDDEN); Ip_u16 pw[8]; Ip_u16 *pw_orig = (Ip_u16 *)vec->array; Ip_size_t i; /* Create a network byte order list */ for (i = 0; i < vec->size / 2; i++) { pw[i] = ip_htons(pw_orig[i]); } ipl2tp_avp_set(cont, IPL2TP_AVP_PSEUDOWIRE_CAPABILITIES_LIST, flags, pw, vec->size); } /* *=========================================================================== * ipl2tp_avp_set_preferred_language *=========================================================================== * Description: Set the preferred language AVP * Parameters: cont - Container * value - The value * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_preferred_language(Ipl2tp_message *cont, CONST char *value) { ipl2tp_avp_set(cont, IPL2TP_AVP_PREFERRED_LANGUAGE, IPL2TP_AVP_FLAG_HIDDEN, (void *)value, (Ip_u16)(ipcom_strlen(value) + 1)); } /* *=========================================================================== * ipl2tp_avp_set_local_session_id *=========================================================================== * Description: Set the local session id * Parameters: cont - The message container * value - The value * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_local_session_id(Ipl2tp_message *cont, Ip_u32 value) { value = ip_htonl(value); ipl2tp_avp_set(cont, IPL2TP_AVP_LOCAL_SESSION_ID, IPL2TP_AVP_FLAG_MANDATORY|IPL2TP_AVP_FLAG_HIDDEN, &value, 4); } /* *=========================================================================== * ipl2tp_avp_set_remote_session_id *=========================================================================== * Description: Set the remote session id * Parameters: cont - The message container * value - The value * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_remote_session_id(Ipl2tp_message *cont, Ip_u32 value) { value = ip_htonl(value); ipl2tp_avp_set(cont, IPL2TP_AVP_REMOTE_SESSION_ID, IPL2TP_AVP_FLAG_MANDATORY|IPL2TP_AVP_FLAG_HIDDEN, &value, 4); } /* *=========================================================================== * ipl2tp_avp_set_remote_end_id *=========================================================================== * Description: Set the remote end id * Parameters: cont - The message container * vec - The value * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_remote_end_id(Ipl2tp_message *cont, Ipl2tp_vector *vec) { ipl2tp_avp_set(cont, IPL2TP_AVP_REMOTE_END_ID, IPL2TP_AVP_FLAG_MANDATORY|IPL2TP_AVP_FLAG_HIDDEN, vec->array, vec->size); } /* *=========================================================================== * ipl2tp_avp_set_pseudowire_type *=========================================================================== * Description: Set the pseudowire type * Parameters: cont - The message container * value - The value * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_pseudowire_type(Ipl2tp_message *cont, Ip_u16 value) { value = ip_htons(value); ipl2tp_avp_set(cont, IPL2TP_AVP_PSEUDOWIRE_TYPE, IPL2TP_AVP_FLAG_MANDATORY|IPL2TP_AVP_FLAG_HIDDEN, &value, 2); } /* *=========================================================================== * ipl2tp_avp_set_circuit_status *=========================================================================== * Description: Set the circuit status * Parameters: cont - The message container * value - The value * first - First circuit value. * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_circuit_status(Ipl2tp_message *cont, Ip_u16 value, Ip_bool first) { if (value == IPL2TP_MIB_UP) value = IPL2TP_ATTR_CIRCUIT_STATUS_ACTIVE; else value = 0; /* */ if (first) value |= IPL2TP_ATTR_CIRCUIT_STATUS_NEW; /**/ value = ip_htons(value); ipl2tp_avp_set(cont, IPL2TP_AVP_CIRCUIT_STATUS, IPL2TP_AVP_FLAG_MANDATORY|IPL2TP_AVP_FLAG_HIDDEN, &value, 2); } /* *=========================================================================== * ipl2tp_avp_set_assigned_cookie *=========================================================================== * Description: Set the assigned cookie * Parameters: cont - The message container * vec - The value * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_assigned_cookie(Ipl2tp_message *cont, Ipl2tp_vector *vec) { ipl2tp_avp_set(cont, IPL2TP_AVP_ASSIGNED_COOKIE, IPL2TP_AVP_FLAG_MANDATORY|IPL2TP_AVP_FLAG_HIDDEN, vec->array, vec->size); } /* *=========================================================================== * ipl2tp_avp_set_data_sequencing *=========================================================================== * Description: Set the data sequencing mode. * Parameters: cont - The message container * value - The value * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_data_sequencing(Ipl2tp_message *cont, Ip_u16 value) { value = ip_htons(value); ipl2tp_avp_set(cont, IPL2TP_AVP_DATA_SEQUENCING, IPL2TP_AVP_FLAG_MANDATORY|IPL2TP_AVP_FLAG_HIDDEN, &value, 2); } /* *=========================================================================== * ipl2tp_avp_set_l2_sublayer *=========================================================================== * Description: Set the L2 sublayer * Parameters: cont - The message container * value - The value * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_l2_sublayer(Ipl2tp_message *cont, Ip_u16 value) { value = ip_htons(value); ipl2tp_avp_set(cont, IPL2TP_AVP_L2_SPECIFIC_SUBLAYER, IPL2TP_AVP_FLAG_MANDATORY|IPL2TP_AVP_FLAG_HIDDEN, &value, 2); } /* *=========================================================================== * ipl2tp_avp_set_tx_connect_speed64 *=========================================================================== * Description: Set the 64 bit TX connect speed * Parameters: cont - The message container * value - The value * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_tx_connect_speed64(Ipl2tp_message *cont, Ip_u64 *value) { Ip_u8 data[8]; IP_SET_HTONL(data, IP_U64_GETHI(*value)); IP_SET_HTONL(data + 4, IP_U64_GETLO(*value)); ipl2tp_avp_set(cont, IPL2TP_AVP_TX_CONNECT_SPEED64, IPL2TP_AVP_FLAG_HIDDEN, data, 8); } /* *=========================================================================== * ipl2tp_avp_set_rx_connect_speed64 *=========================================================================== * Description: Set the 64 bit RX connect speed * Parameters: cont - The message container * value - The value * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_rx_connect_speed64(Ipl2tp_message *cont, Ip_u64 *value) { Ip_u8 data[8]; IP_SET_HTONL(data, IP_U64_GETHI(*value)); IP_SET_HTONL(data + 4, IP_U64_GETLO(*value)); ipl2tp_avp_set(cont, IPL2TP_AVP_RX_CONNECT_SPEED64, IPL2TP_AVP_FLAG_HIDDEN, data, 8); } /* *=========================================================================== * ipl2tp_avp_set_minimum_bps *=========================================================================== * Description: Set the minimum bps value. * Parameters: cont - The message container * value - The value * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_minimum_bps(struct Ipl2tp_message *cont, Ip_u32 value) { value = ip_htonl(value); ipl2tp_avp_set(cont, IPL2TP_AVP_MINIMUM_BPS, IPL2TP_AVP_FLAG_MANDATORY|IPL2TP_AVP_FLAG_HIDDEN, &value, 4); } /* *=========================================================================== * ipl2tp_avp_set_maximum_bps *=========================================================================== * Description: Set the maximum bps value. * Parameters: cont - The message container * value - The value * Returns: * */ IP_GLOBAL void ipl2tp_avp_set_maximum_bps(struct Ipl2tp_message *cont, Ip_u32 value) { value = ip_htonl(value); ipl2tp_avp_set(cont, IPL2TP_AVP_MAXIMUM_BPS, IPL2TP_AVP_FLAG_MANDATORY|IPL2TP_AVP_FLAG_HIDDEN, &value, 4); } /* **************************************************************************** * 11 PUBLIC FUNCTIONS **************************************************************************** */ /* **************************************************************************** * END OF FILE **************************************************************************** */