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