alias_skinny.c revision 145921
180709Sjake/*- 280709Sjake * alias_skinny.c 380709Sjake * 480709Sjake * Copyright (c) 2002, 2003 MarcusCom, Inc. 580709Sjake * All rights reserved. 680709Sjake * 780709Sjake * Redistribution and use in source and binary forms, with or without 880709Sjake * modification, are permitted provided that the following conditions 980709Sjake * are met: 1080709Sjake * 1. Redistributions of source code must retain the above copyright 1180709Sjake * notice, this list of conditions and the following disclaimer. 1280709Sjake * 2. Redistributions in binary form must reproduce the above copyright 1380709Sjake * notice, this list of conditions and the following disclaimer in the 1481337Sobrien * documentation and/or other materials provided with the distribution. 1580709Sjake * 1680709Sjake * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1781337Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1880709Sjake * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1980709Sjake * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2080709Sjake * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2180709Sjake * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2280709Sjake * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2380709Sjake * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2480709Sjake * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2580709Sjake * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2680709Sjake * SUCH DAMAGE. 2780709Sjake * 2880709Sjake * Author: Joe Marcus Clarke <marcus@FreeBSD.org> 2980709Sjake * 3080709Sjake * $FreeBSD: head/sys/netinet/libalias/alias_skinny.c 145921 2005-05-05 19:27:32Z glebius $ 3180709Sjake */ 3280709Sjake 3382910Sjake#ifdef _KERNEL 3480709Sjake#include <sys/param.h> 3580709Sjake#else 3686527Sjake#include <sys/types.h> 3780709Sjake#include <sys/socket.h> 3891176Sjake#include <stdio.h> 3984186Sjake#include <string.h> 4080709Sjake#include <unistd.h> 4180709Sjake#include <arpa/inet.h> 4281381Sjake#endif 4381381Sjake 4481381Sjake#include <netinet/in_systm.h> 4581381Sjake#include <netinet/in.h> 4688657Sjake#include <netinet/ip.h> 4780709Sjake#include <netinet/tcp.h> 4880709Sjake#include <netinet/udp.h> 4980709Sjake 5080709Sjake#ifdef _KERNEL 5181135Stmm#include <netinet/libalias/alias_local.h> 5280709Sjake#else 5386527Sjake#include "alias_local.h" 5481614Sjake#endif 5588657Sjake 5680709Sjake/* 5780709Sjake * alias_skinny.c handles the translation for the Cisco Skinny Station 5880709Sjake * protocol. Skinny typically uses TCP port 2000 to set up calls between 5982910Sjake * a Cisco Call Manager and a Cisco IP phone. When a phone comes on line, 6089052Sjake * it first needs to register with the Call Manager. To do this it sends 6180709Sjake * a registration message. This message contains the IP address of the 6280709Sjake * IP phone. This message must then be translated to reflect our global 6380709Sjake * IP address. Along with the registration message (and usually in the 6480709Sjake * same packet), the phone sends an IP port message. This message indicates 6582010Sjake * the TCP port over which it will communicate. 6688788Sjake * 6780709Sjake * When a call is placed from the phone, the Call Manager will send an 6883756Sjake * Open Receive Channel message to the phone to let the caller know someone 6983756Sjake * has answered. The phone then sends back an Open Receive Channel 7080709Sjake * Acknowledgement. In this packet, the phone sends its IP address again, 7180709Sjake * and the UDP port over which the voice traffic should flow. These values 7280709Sjake * need translation. Right after the Open Receive Channel Acknowledgement, 7383366Sjulian * the Call Manager sends a Start Media Transmission message indicating the 7484186Sjake * call is connected. This message contains the IP address and UDP port 7591360Sjake * number of the remote (called) party. Once this message is translated, the 7683366Sjulian * call can commence. The called part sends the first UDP packet to the 7780709Sjake * calling phone at the pre-arranged UDP port in the Open Receive Channel 7880709Sjake * Acknowledgement. 7982910Sjake * 8080709Sjake * Skinny is a Cisco-proprietary protocol and is a trademark of Cisco Systems, 8181135Stmm * Inc. All rights reserved. 8281135Stmm*/ 8381135Stmm 8481135Stmm/* #define DEBUG 1 */ 8588657Sjake 8688657Sjake/* Message types that need translating */ 8791224Sjake#define REG_MSG 0x00000001 8891224Sjake#define IP_PORT_MSG 0x00000002 8981381Sjake#define OPNRCVCH_ACK 0x00000022 9089052Sjake#define START_MEDIATX 0x0000008a 9189052Sjake 9288657Sjakestruct skinny_header { 9381381Sjake u_int32_t len; 9489052Sjake u_int32_t reserved; 9589052Sjake u_int32_t msgId; 9689052Sjake}; 9788657Sjake 9888657Sjakestruct RegisterMessage { 9991224Sjake u_int32_t msgId; 10080709Sjake char devName [16]; 10191336Sjake u_int32_t uid; 10291336Sjake u_int32_t instance; 10391336Sjake u_int32_t ipAddr; 10480709Sjake u_char devType; 10580709Sjake u_int32_t maxStreams; 10680709Sjake}; 10789052Sjake 10889052Sjakestruct IpPortMessage { 10989052Sjake u_int32_t msgId; 11089052Sjake u_int32_t stationIpPort; /* Note: Skinny uses 32-bit port 11189052Sjake * numbers */ 11289052Sjake}; 11389052Sjake 11489052Sjakestruct OpenReceiveChannelAck { 11582910Sjake u_int32_t msgId; 11685244Sjake u_int32_t status; 11784186Sjake u_int32_t ipAddr; 11889052Sjake u_int32_t port; 11984186Sjake u_int32_t passThruPartyID; 12084186Sjake}; 12182910Sjake 12285244Sjakestruct StartMediaTransmission { 12385244Sjake u_int32_t msgId; 12485244Sjake u_int32_t conferenceID; 12582910Sjake u_int32_t passThruPartyID; 12682910Sjake u_int32_t remoteIpAddr; 12782910Sjake u_int32_t remotePort; 12882910Sjake u_int32_t MSPacket; 12982910Sjake u_int32_t payloadCap; 13082910Sjake u_int32_t precedence; 13182910Sjake u_int32_t silenceSuppression; 13282910Sjake u_short maxFramesPerPacket; 13388657Sjake u_int32_t G723BitRate; 13482910Sjake}; 13591224Sjake 13680709Sjaketypedef enum { 13781381Sjake ClientToServer = 0, 13880709Sjake ServerToClient = 1 13981614Sjake} ConvDirection; 14080709Sjake 14188657Sjake 14288657Sjakestatic int 14380709Sjakealias_skinny_reg_msg(struct RegisterMessage *reg_msg, struct ip *pip, 14480709Sjake struct tcphdr *tc, struct alias_link *lnk, 14584186Sjake ConvDirection direction) 14684186Sjake{ 14787702Sjhb (void)direction; 14887702Sjhb 14987702Sjhb reg_msg->ipAddr = (u_int32_t) GetAliasAddress(lnk).s_addr; 15091337Sjake 15189052Sjake tc->th_sum = 0; 15289052Sjake tc->th_sum = TcpChecksum(pip); 15389052Sjake 15480709Sjake return (0); 15584186Sjake} 15684186Sjake 15781614Sjakestatic int 15881614Sjakealias_skinny_startmedia(struct StartMediaTransmission *start_media, 15981614Sjake struct ip *pip, struct tcphdr *tc, 16081614Sjake struct alias_link *lnk, u_int32_t localIpAddr, 16181614Sjake ConvDirection direction) 16281614Sjake{ 16381614Sjake struct in_addr dst, src; 16481614Sjake 16581614Sjake (void)pip; 16681614Sjake (void)tc; 16781614Sjake (void)lnk; 16889052Sjake (void)direction; 16989052Sjake 17089052Sjake dst.s_addr = start_media->remoteIpAddr; 17189052Sjake src.s_addr = localIpAddr; 17289052Sjake 17389052Sjake /* 17489052Sjake * XXX I should probably handle in bound global translations as 17589052Sjake * well. 17681614Sjake */ 17781614Sjake 17881614Sjake return (0); 17981614Sjake} 18081614Sjake 18185244Sjakestatic int 18285244Sjakealias_skinny_port_msg(struct IpPortMessage *port_msg, struct ip *pip, 18383366Sjulian struct tcphdr *tc, struct alias_link *lnk, 18483366Sjulian ConvDirection direction) 18582910Sjake{ 18688788Sjake (void)direction; 18788788Sjake 18882910Sjake port_msg->stationIpPort = (u_int32_t) ntohs(GetAliasPort(lnk)); 18988657Sjake 19088657Sjake tc->th_sum = 0; 19182910Sjake tc->th_sum = TcpChecksum(pip); 19280709Sjake 19380709Sjake return (0); 19488657Sjake} 19588657Sjake 19683366Sjulianstatic int 19783366Sjulianalias_skinny_opnrcvch_ack(struct libalias *la, struct OpenReceiveChannelAck *opnrcvch_ack, 19883366Sjulian struct ip *pip, struct tcphdr *tc, 19983366Sjulian struct alias_link *lnk, u_int32_t * localIpAddr, 20083366Sjulian ConvDirection direction) 20183366Sjulian{ 20283366Sjulian struct in_addr null_addr; 20383366Sjulian struct alias_link *opnrcv_lnk; 20484186Sjake u_int32_t localPort; 20581135Stmm 20680709Sjake (void)lnk; 20780709Sjake (void)direction; 20882010Sjake 20980709Sjake *localIpAddr = (u_int32_t) opnrcvch_ack->ipAddr; 21082910Sjake localPort = opnrcvch_ack->port; 21182910Sjake 21282910Sjake null_addr.s_addr = INADDR_ANY; 21380709Sjake opnrcv_lnk = FindUdpTcpOut(la, pip->ip_src, null_addr, 21481381Sjake htons((u_short) opnrcvch_ack->port), 0, 21588657Sjake IPPROTO_UDP, 1); 21681381Sjake opnrcvch_ack->ipAddr = (u_int32_t) GetAliasAddress(opnrcv_lnk).s_addr; 21788657Sjake opnrcvch_ack->port = (u_int32_t) ntohs(GetAliasPort(opnrcv_lnk)); 21881381Sjake 21981135Stmm tc->th_sum = 0; 22081135Stmm tc->th_sum = TcpChecksum(pip); 22181135Stmm 22281135Stmm return (0); 22381135Stmm} 22480709Sjake 22580709Sjakevoid 22680709SjakeAliasHandleSkinny(struct libalias *la, struct ip *pip, struct alias_link *lnk) 22782910Sjake{ 22882010Sjake size_t hlen, tlen, dlen; 22988657Sjake struct tcphdr *tc; 23088657Sjake u_int32_t msgId, t, len, lip; 23188657Sjake struct skinny_header *sd; 23288657Sjake size_t orig_len, skinny_hdr_len = sizeof(struct skinny_header); 23388657Sjake ConvDirection direction; 23480709Sjake 23580709Sjake tc = (struct tcphdr *)ip_next(pip); 23680709Sjake hlen = (pip->ip_hl + tc->th_off) << 2; 23780709Sjake tlen = ntohs(pip->ip_len); 23880709Sjake dlen = tlen - hlen; 23980709Sjake 24080709Sjake sd = (struct skinny_header *)tcp_next(tc); 24180709Sjake 24280709Sjake /* 24380709Sjake * XXX This direction is reserved for future use. I still need to 24480709Sjake * handle the scenario where the call manager is on the inside, and 24580709Sjake * the calling phone is on the global outside. 24680709Sjake */ 24780709Sjake if (ntohs(tc->th_dport) == la->skinnyPort) { 24880709Sjake direction = ClientToServer; 24980709Sjake } else if (ntohs(tc->th_sport) == la->skinnyPort) { 25080709Sjake direction = ServerToClient; 25188657Sjake } else { 25288657Sjake#ifdef DEBUG 25388657Sjake fprintf(stderr, 25488657Sjake "PacketAlias/Skinny: Invalid port number, not a Skinny packet\n"); 25588657Sjake#endif 25688657Sjake return; 25780709Sjake } 25888657Sjake 25980709Sjake orig_len = dlen; 26088657Sjake /* 26188657Sjake * Skinny packets can contain many messages. We need to loop 26288657Sjake * through the packet using len to determine message boundaries. 26382910Sjake * This comes into play big time with port messages being in the 26480709Sjake * same packet as register messages. Also, open receive channel 26588788Sjake * acks are usually buried in a pakcet some 400 bytes long. 26688788Sjake */ 267 while (dlen >= skinny_hdr_len) { 268 len = (sd->len); 269 msgId = (sd->msgId); 270 t = len; 271 272 if (t > orig_len || t > dlen) { 273#ifdef DEBUG 274 fprintf(stderr, 275 "PacketAlias/Skinny: Not a skinny packet, invalid length \n"); 276#endif 277 return; 278 } 279 switch (msgId) { 280 case REG_MSG: { 281 struct RegisterMessage *reg_mesg; 282 283 if (len < (int)sizeof(struct RegisterMessage)) { 284#ifdef DEBUG 285 fprintf(stderr, 286 "PacketAlias/Skinny: Not a skinny packet, bad registration message\n"); 287#endif 288 return; 289 } 290 reg_mesg = (struct RegisterMessage *)&sd->msgId; 291#ifdef DEBUG 292 fprintf(stderr, 293 "PacketAlias/Skinny: Received a register message"); 294#endif 295 alias_skinny_reg_msg(reg_mesg, pip, tc, lnk, direction); 296 break; 297 } 298 case IP_PORT_MSG: { 299 struct IpPortMessage *port_mesg; 300 301 if (len < (int)sizeof(struct IpPortMessage)) { 302#ifdef DEBUG 303 fprintf(stderr, 304 "PacketAlias/Skinny: Not a skinny packet, port message\n"); 305#endif 306 return; 307 } 308#ifdef DEBUG 309 fprintf(stderr, 310 "PacketAlias/Skinny: Received ipport message\n"); 311#endif 312 port_mesg = (struct IpPortMessage *)&sd->msgId; 313 alias_skinny_port_msg(port_mesg, pip, tc, lnk, direction); 314 break; 315 } 316 case OPNRCVCH_ACK: { 317 struct OpenReceiveChannelAck *opnrcvchn_ack; 318 319 if (len < (int)sizeof(struct OpenReceiveChannelAck)) { 320#ifdef DEBUG 321 fprintf(stderr, 322 "PacketAlias/Skinny: Not a skinny packet, packet,OpnRcvChnAckMsg\n"); 323#endif 324 return; 325 } 326#ifdef DEBUG 327 fprintf(stderr, 328 "PacketAlias/Skinny: Received open rcv channel msg\n"); 329#endif 330 opnrcvchn_ack = (struct OpenReceiveChannelAck *)&sd->msgId; 331 alias_skinny_opnrcvch_ack(la, opnrcvchn_ack, pip, tc, lnk, &lip, direction); 332 break; 333 } 334 case START_MEDIATX: { 335 struct StartMediaTransmission *startmedia_tx; 336 337 if (len < (int)sizeof(struct StartMediaTransmission)) { 338#ifdef DEBUG 339 fprintf(stderr, 340 "PacketAlias/Skinny: Not a skinny packet,StartMediaTx Message\n"); 341#endif 342 return; 343 } 344#ifdef DEBUG 345 fprintf(stderr, 346 "PacketAlias/Skinny: Received start media trans msg\n"); 347#endif 348 startmedia_tx = (struct StartMediaTransmission *)&sd->msgId; 349 alias_skinny_startmedia(startmedia_tx, pip, tc, lnk, lip, direction); 350 break; 351 } 352 default: 353 break; 354 } 355 /* Place the pointer at the next message in the packet. */ 356 dlen -= len + (skinny_hdr_len - sizeof(msgId)); 357 sd = (struct skinny_header *)(((char *)&sd->msgId) + len); 358 } 359} 360