alias_skinny.c revision 145932
1204076Spjd/*- 2204076Spjd * alias_skinny.c 3204076Spjd * 4204076Spjd * Copyright (c) 2002, 2003 MarcusCom, Inc. 5204076Spjd * All rights reserved. 6204076Spjd * 7204076Spjd * Redistribution and use in source and binary forms, with or without 8204076Spjd * modification, are permitted provided that the following conditions 9204076Spjd * are met: 10204076Spjd * 1. Redistributions of source code must retain the above copyright 11204076Spjd * notice, this list of conditions and the following disclaimer. 12204076Spjd * 2. Redistributions in binary form must reproduce the above copyright 13204076Spjd * notice, this list of conditions and the following disclaimer in the 14204076Spjd * documentation and/or other materials provided with the distribution. 15204076Spjd * 16204076Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17204076Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18204076Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19204076Spjd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20204076Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21204076Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22204076Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23204076Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24204076Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25204076Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26204076Spjd * SUCH DAMAGE. 27204076Spjd * 28204076Spjd * Author: Joe Marcus Clarke <marcus@FreeBSD.org> 29204076Spjd * 30204076Spjd * $FreeBSD: head/sys/netinet/libalias/alias_skinny.c 145932 2005-05-05 21:55:17Z glebius $ 31204076Spjd */ 32204076Spjd 33204076Spjd#ifdef _KERNEL 34204076Spjd#include <sys/param.h> 35204076Spjd#else 36204076Spjd#include <sys/types.h> 37204076Spjd#include <sys/socket.h> 38204076Spjd#include <stdio.h> 39204076Spjd#include <string.h> 40204076Spjd#include <unistd.h> 41204076Spjd#include <arpa/inet.h> 42204076Spjd#endif 43204076Spjd 44204076Spjd#include <netinet/in_systm.h> 45204076Spjd#include <netinet/in.h> 46204076Spjd#include <netinet/ip.h> 47204076Spjd#include <netinet/tcp.h> 48204076Spjd#include <netinet/udp.h> 49204076Spjd 50204076Spjd#ifdef _KERNEL 51204076Spjd#include <netinet/libalias/alias.h> 52204076Spjd#include <netinet/libalias/alias_local.h> 53204076Spjd#else 54204076Spjd#include "alias_local.h" 55204076Spjd#endif 56204076Spjd 57204076Spjd/* 58204076Spjd * alias_skinny.c handles the translation for the Cisco Skinny Station 59204076Spjd * protocol. Skinny typically uses TCP port 2000 to set up calls between 60204076Spjd * a Cisco Call Manager and a Cisco IP phone. When a phone comes on line, 61204076Spjd * it first needs to register with the Call Manager. To do this it sends 62204076Spjd * a registration message. This message contains the IP address of the 63204076Spjd * IP phone. This message must then be translated to reflect our global 64204076Spjd * IP address. Along with the registration message (and usually in the 65204076Spjd * same packet), the phone sends an IP port message. This message indicates 66204076Spjd * the TCP port over which it will communicate. 67204076Spjd * 68204076Spjd * When a call is placed from the phone, the Call Manager will send an 69204076Spjd * Open Receive Channel message to the phone to let the caller know someone 70204076Spjd * has answered. The phone then sends back an Open Receive Channel 71204076Spjd * Acknowledgement. In this packet, the phone sends its IP address again, 72204076Spjd * and the UDP port over which the voice traffic should flow. These values 73204076Spjd * need translation. Right after the Open Receive Channel Acknowledgement, 74204076Spjd * the Call Manager sends a Start Media Transmission message indicating the 75204076Spjd * call is connected. This message contains the IP address and UDP port 76204076Spjd * number of the remote (called) party. Once this message is translated, the 77204076Spjd * call can commence. The called part sends the first UDP packet to the 78204076Spjd * calling phone at the pre-arranged UDP port in the Open Receive Channel 79204076Spjd * Acknowledgement. 80204076Spjd * 81204076Spjd * Skinny is a Cisco-proprietary protocol and is a trademark of Cisco Systems, 82204076Spjd * Inc. All rights reserved. 83204076Spjd*/ 84204076Spjd 85204076Spjd/* #define DEBUG 1 */ 86204076Spjd 87204076Spjd/* Message types that need translating */ 88204076Spjd#define REG_MSG 0x00000001 89204076Spjd#define IP_PORT_MSG 0x00000002 90204076Spjd#define OPNRCVCH_ACK 0x00000022 91204076Spjd#define START_MEDIATX 0x0000008a 92204076Spjd 93204076Spjdstruct skinny_header { 94204177Spjd u_int32_t len; 95204076Spjd u_int32_t reserved; 96204076Spjd u_int32_t msgId; 97204177Spjd}; 98204177Spjd 99204177Spjdstruct RegisterMessage { 100204076Spjd u_int32_t msgId; 101204076Spjd char devName [16]; 102204076Spjd u_int32_t uid; 103204076Spjd u_int32_t instance; 104204076Spjd u_int32_t ipAddr; 105204076Spjd u_char devType; 106204076Spjd u_int32_t maxStreams; 107204076Spjd}; 108204076Spjd 109204076Spjdstruct IpPortMessage { 110204076Spjd u_int32_t msgId; 111204076Spjd u_int32_t stationIpPort; /* Note: Skinny uses 32-bit port 112204076Spjd * numbers */ 113204076Spjd}; 114204076Spjd 115204076Spjdstruct OpenReceiveChannelAck { 116204076Spjd u_int32_t msgId; 117204076Spjd u_int32_t status; 118204076Spjd u_int32_t ipAddr; 119204076Spjd u_int32_t port; 120204076Spjd u_int32_t passThruPartyID; 121204076Spjd}; 122204076Spjd 123204076Spjdstruct StartMediaTransmission { 124204076Spjd u_int32_t msgId; 125204076Spjd u_int32_t conferenceID; 126204076Spjd u_int32_t passThruPartyID; 127204076Spjd u_int32_t remoteIpAddr; 128204076Spjd u_int32_t remotePort; 129204076Spjd u_int32_t MSPacket; 130204076Spjd u_int32_t payloadCap; 131204076Spjd u_int32_t precedence; 132204076Spjd u_int32_t silenceSuppression; 133204076Spjd u_short maxFramesPerPacket; 134204076Spjd u_int32_t G723BitRate; 135204076Spjd}; 136204076Spjd 137204076Spjdtypedef enum { 138204076Spjd ClientToServer = 0, 139204076Spjd ServerToClient = 1 140204076Spjd} ConvDirection; 141204076Spjd 142204076Spjd 143204076Spjdstatic int 144204076Spjdalias_skinny_reg_msg(struct RegisterMessage *reg_msg, struct ip *pip, 145204076Spjd struct tcphdr *tc, struct alias_link *lnk, 146204076Spjd ConvDirection direction) 147204076Spjd{ 148204076Spjd (void)direction; 149204076Spjd 150204076Spjd reg_msg->ipAddr = (u_int32_t) GetAliasAddress(lnk).s_addr; 151204076Spjd 152204076Spjd tc->th_sum = 0; 153204076Spjd tc->th_sum = TcpChecksum(pip); 154204076Spjd 155204076Spjd return (0); 156204076Spjd} 157204076Spjd 158204076Spjdstatic int 159204076Spjdalias_skinny_startmedia(struct StartMediaTransmission *start_media, 160204076Spjd struct ip *pip, struct tcphdr *tc, 161204076Spjd struct alias_link *lnk, u_int32_t localIpAddr, 162204076Spjd ConvDirection direction) 163204076Spjd{ 164204076Spjd struct in_addr dst, src; 165204076Spjd 166204076Spjd (void)pip; 167204076Spjd (void)tc; 168204076Spjd (void)lnk; 169204076Spjd (void)direction; 170204076Spjd 171204076Spjd dst.s_addr = start_media->remoteIpAddr; 172204076Spjd src.s_addr = localIpAddr; 173204076Spjd 174204076Spjd /* 175204076Spjd * XXX I should probably handle in bound global translations as 176204076Spjd * well. 177204076Spjd */ 178204076Spjd 179204076Spjd return (0); 180204076Spjd} 181204076Spjd 182204076Spjdstatic int 183204076Spjdalias_skinny_port_msg(struct IpPortMessage *port_msg, struct ip *pip, 184204076Spjd struct tcphdr *tc, struct alias_link *lnk, 185204076Spjd ConvDirection direction) 186204076Spjd{ 187204076Spjd (void)direction; 188204076Spjd 189204076Spjd port_msg->stationIpPort = (u_int32_t) ntohs(GetAliasPort(lnk)); 190204076Spjd 191204076Spjd tc->th_sum = 0; 192204076Spjd tc->th_sum = TcpChecksum(pip); 193204076Spjd 194204076Spjd return (0); 195204076Spjd} 196204076Spjd 197204076Spjdstatic int 198204076Spjdalias_skinny_opnrcvch_ack(struct libalias *la, struct OpenReceiveChannelAck *opnrcvch_ack, 199204076Spjd struct ip *pip, struct tcphdr *tc, 200204076Spjd struct alias_link *lnk, u_int32_t * localIpAddr, 201204076Spjd ConvDirection direction) 202204076Spjd{ 203204076Spjd struct in_addr null_addr; 204204076Spjd struct alias_link *opnrcv_lnk; 205204076Spjd u_int32_t localPort; 206204076Spjd 207204076Spjd (void)lnk; 208204076Spjd (void)direction; 209204076Spjd 210204076Spjd *localIpAddr = (u_int32_t) opnrcvch_ack->ipAddr; 211204076Spjd localPort = opnrcvch_ack->port; 212204076Spjd 213204076Spjd null_addr.s_addr = INADDR_ANY; 214204076Spjd opnrcv_lnk = FindUdpTcpOut(la, pip->ip_src, null_addr, 215204076Spjd htons((u_short) opnrcvch_ack->port), 0, 216204076Spjd IPPROTO_UDP, 1); 217204076Spjd opnrcvch_ack->ipAddr = (u_int32_t) GetAliasAddress(opnrcv_lnk).s_addr; 218204076Spjd opnrcvch_ack->port = (u_int32_t) ntohs(GetAliasPort(opnrcv_lnk)); 219204076Spjd 220204076Spjd tc->th_sum = 0; 221204076Spjd tc->th_sum = TcpChecksum(pip); 222204076Spjd 223204076Spjd return (0); 224204076Spjd} 225204076Spjd 226204076Spjdvoid 227204076SpjdAliasHandleSkinny(struct libalias *la, struct ip *pip, struct alias_link *lnk) 228204076Spjd{ 229204076Spjd size_t hlen, tlen, dlen; 230204076Spjd struct tcphdr *tc; 231204076Spjd u_int32_t msgId, t, len, lip; 232204076Spjd struct skinny_header *sd; 233204076Spjd size_t orig_len, skinny_hdr_len = sizeof(struct skinny_header); 234204076Spjd ConvDirection direction; 235204076Spjd 236204076Spjd tc = (struct tcphdr *)ip_next(pip); 237204076Spjd hlen = (pip->ip_hl + tc->th_off) << 2; 238204076Spjd tlen = ntohs(pip->ip_len); 239204076Spjd dlen = tlen - hlen; 240204076Spjd 241204076Spjd sd = (struct skinny_header *)tcp_next(tc); 242204076Spjd 243204076Spjd /* 244204076Spjd * XXX This direction is reserved for future use. I still need to 245204076Spjd * handle the scenario where the call manager is on the inside, and 246204076Spjd * the calling phone is on the global outside. 247204076Spjd */ 248204076Spjd if (ntohs(tc->th_dport) == la->skinnyPort) { 249204076Spjd direction = ClientToServer; 250204076Spjd } else if (ntohs(tc->th_sport) == la->skinnyPort) { 251204076Spjd direction = ServerToClient; 252204076Spjd } else { 253204076Spjd#ifdef DEBUG 254204076Spjd fprintf(stderr, 255204076Spjd "PacketAlias/Skinny: Invalid port number, not a Skinny packet\n"); 256204076Spjd#endif 257204076Spjd return; 258204076Spjd } 259204076Spjd 260204076Spjd orig_len = dlen; 261204076Spjd /* 262204076Spjd * Skinny packets can contain many messages. We need to loop 263204076Spjd * through the packet using len to determine message boundaries. 264204076Spjd * This comes into play big time with port messages being in the 265204076Spjd * same packet as register messages. Also, open receive channel 266204076Spjd * acks are usually buried in a pakcet some 400 bytes long. 267204076Spjd */ 268204076Spjd while (dlen >= skinny_hdr_len) { 269204076Spjd len = (sd->len); 270204076Spjd msgId = (sd->msgId); 271204076Spjd t = len; 272204076Spjd 273204076Spjd if (t > orig_len || t > dlen) { 274210869Spjd#ifdef DEBUG 275204076Spjd fprintf(stderr, 276 "PacketAlias/Skinny: Not a skinny packet, invalid length \n"); 277#endif 278 return; 279 } 280 switch (msgId) { 281 case REG_MSG: { 282 struct RegisterMessage *reg_mesg; 283 284 if (len < (int)sizeof(struct RegisterMessage)) { 285#ifdef DEBUG 286 fprintf(stderr, 287 "PacketAlias/Skinny: Not a skinny packet, bad registration message\n"); 288#endif 289 return; 290 } 291 reg_mesg = (struct RegisterMessage *)&sd->msgId; 292#ifdef DEBUG 293 fprintf(stderr, 294 "PacketAlias/Skinny: Received a register message"); 295#endif 296 alias_skinny_reg_msg(reg_mesg, pip, tc, lnk, direction); 297 break; 298 } 299 case IP_PORT_MSG: { 300 struct IpPortMessage *port_mesg; 301 302 if (len < (int)sizeof(struct IpPortMessage)) { 303#ifdef DEBUG 304 fprintf(stderr, 305 "PacketAlias/Skinny: Not a skinny packet, port message\n"); 306#endif 307 return; 308 } 309#ifdef DEBUG 310 fprintf(stderr, 311 "PacketAlias/Skinny: Received ipport message\n"); 312#endif 313 port_mesg = (struct IpPortMessage *)&sd->msgId; 314 alias_skinny_port_msg(port_mesg, pip, tc, lnk, direction); 315 break; 316 } 317 case OPNRCVCH_ACK: { 318 struct OpenReceiveChannelAck *opnrcvchn_ack; 319 320 if (len < (int)sizeof(struct OpenReceiveChannelAck)) { 321#ifdef DEBUG 322 fprintf(stderr, 323 "PacketAlias/Skinny: Not a skinny packet, packet,OpnRcvChnAckMsg\n"); 324#endif 325 return; 326 } 327#ifdef DEBUG 328 fprintf(stderr, 329 "PacketAlias/Skinny: Received open rcv channel msg\n"); 330#endif 331 opnrcvchn_ack = (struct OpenReceiveChannelAck *)&sd->msgId; 332 alias_skinny_opnrcvch_ack(la, opnrcvchn_ack, pip, tc, lnk, &lip, direction); 333 break; 334 } 335 case START_MEDIATX: { 336 struct StartMediaTransmission *startmedia_tx; 337 338 if (len < (int)sizeof(struct StartMediaTransmission)) { 339#ifdef DEBUG 340 fprintf(stderr, 341 "PacketAlias/Skinny: Not a skinny packet,StartMediaTx Message\n"); 342#endif 343 return; 344 } 345#ifdef DEBUG 346 fprintf(stderr, 347 "PacketAlias/Skinny: Received start media trans msg\n"); 348#endif 349 startmedia_tx = (struct StartMediaTransmission *)&sd->msgId; 350 alias_skinny_startmedia(startmedia_tx, pip, tc, lnk, lip, direction); 351 break; 352 } 353 default: 354 break; 355 } 356 /* Place the pointer at the next message in the packet. */ 357 dlen -= len + (skinny_hdr_len - sizeof(msgId)); 358 sd = (struct skinny_header *)(((char *)&sd->msgId) + len); 359 } 360} 361