alias_skinny.c revision 127094
1/*-
2 * alias_skinny.c
3 *
4 * Copyright (c) 2002, 2003 MarcusCom, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * Author: Joe Marcus Clarke <marcus@FreeBSD.org>
29 *
30 * $FreeBSD: head/sys/netinet/libalias/alias_skinny.c 127094 2004-03-16 21:30:41Z des $
31 */
32
33#include <stdio.h>
34#include <string.h>
35#include <sys/types.h>
36#include <sys/socket.h>
37#include <netinet/in_systm.h>
38#include <netinet/in.h>
39#include <arpa/inet.h>
40#include <netinet/ip.h>
41#include <netinet/tcp.h>
42#include <netinet/udp.h>
43#include <unistd.h>
44
45#include "alias_local.h"
46
47/*
48 * alias_skinny.c handles the translation for the Cisco Skinny Station
49 * protocol.  Skinny typically uses TCP port 2000 to set up calls between
50 * a Cisco Call Manager and a Cisco IP phone.  When a phone comes on line,
51 * it first needs to register with the Call Manager.  To do this it sends
52 * a registration message.  This message contains the IP address of the
53 * IP phone.  This message must then be translated to reflect our global
54 * IP address.  Along with the registration message (and usually in the
55 * same packet), the phone sends an IP port message.  This message indicates
56 * the TCP port over which it will communicate.
57 *
58 * When a call is placed from the phone, the Call Manager will send an
59 * Open Receive Channel message to the phone to let the caller know someone
60 * has answered.  The phone then sends back an Open Receive Channel
61 * Acknowledgement.  In this packet, the phone sends its IP address again,
62 * and the UDP port over which the voice traffic should flow.  These values
63 * need translation.  Right after the Open Receive Channel Acknowledgement,
64 * the Call Manager sends a Start Media Transmission message indicating the
65 * call is connected.  This message contains the IP address and UDP port
66 * number of the remote (called) party.  Once this message is translated, the
67 * call can commence.  The called part sends the first UDP packet to the
68 * calling phone at the pre-arranged UDP port in the Open Receive Channel
69 * Acknowledgement.
70 *
71 * Skinny is a Cisco-proprietary protocol and is a trademark of Cisco Systems,
72 * Inc.  All rights reserved.
73*/
74
75/* #define DEBUG 1 */
76
77/* Message types that need translating */
78#define REG_MSG         0x00000001
79#define IP_PORT_MSG     0x00000002
80#define OPNRCVCH_ACK    0x00000022
81#define START_MEDIATX   0x0000008a
82
83struct skinny_header {
84	u_int32_t	len;
85	u_int32_t	reserved;
86	u_int32_t	msgId;
87};
88
89struct RegisterMessage {
90	u_int32_t	msgId;
91	char		devName   [16];
92	u_int32_t	uid;
93	u_int32_t	instance;
94	u_int32_t	ipAddr;
95	u_char		devType;
96	u_int32_t	maxStreams;
97};
98
99struct IpPortMessage {
100	u_int32_t	msgId;
101	u_int32_t	stationIpPort;	/* Note: Skinny uses 32-bit port
102					 * numbers */
103};
104
105struct OpenReceiveChannelAck {
106	u_int32_t	msgId;
107	u_int32_t	status;
108	u_int32_t	ipAddr;
109	u_int32_t	port;
110	u_int32_t	passThruPartyID;
111};
112
113struct StartMediaTransmission {
114	u_int32_t	msgId;
115	u_int32_t	conferenceID;
116	u_int32_t	passThruPartyID;
117	u_int32_t	remoteIpAddr;
118	u_int32_t	remotePort;
119	u_int32_t	MSPacket;
120	u_int32_t	payloadCap;
121	u_int32_t	precedence;
122	u_int32_t	silenceSuppression;
123	u_short		maxFramesPerPacket;
124	u_int32_t	G723BitRate;
125};
126
127typedef enum {
128	ClientToServer = 0,
129	ServerToClient = 1
130} ConvDirection;
131
132
133static int
134alias_skinny_reg_msg(struct RegisterMessage *reg_msg, struct ip *pip,
135    struct tcphdr *tc, struct alias_link *link,
136    ConvDirection direction)
137{
138	reg_msg->ipAddr = (u_int32_t) GetAliasAddress(link).s_addr;
139
140	tc->th_sum = 0;
141	tc->th_sum = TcpChecksum(pip);
142
143	return 0;
144}
145
146static int
147alias_skinny_startmedia(struct StartMediaTransmission *start_media,
148    struct ip *pip, struct tcphdr *tc,
149    struct alias_link *link, u_int32_t localIpAddr,
150    ConvDirection direction)
151{
152	struct in_addr dst, src;
153
154	dst.s_addr = start_media->remoteIpAddr;
155	src.s_addr = localIpAddr;
156
157	/*
158	 * XXX I should probably handle in bound global translations as
159	 * well.
160	 */
161
162	return 0;
163}
164
165static int
166alias_skinny_port_msg(struct IpPortMessage *port_msg, struct ip *pip,
167    struct tcphdr *tc, struct alias_link *link,
168    ConvDirection direction)
169{
170	port_msg->stationIpPort = (u_int32_t) ntohs(GetAliasPort(link));
171
172	tc->th_sum = 0;
173	tc->th_sum = TcpChecksum(pip);
174
175	return 0;
176}
177
178static int
179alias_skinny_opnrcvch_ack(struct libalias *la, struct OpenReceiveChannelAck *opnrcvch_ack,
180    struct ip *pip, struct tcphdr *tc,
181    struct alias_link *link, u_int32_t * localIpAddr,
182    ConvDirection direction)
183{
184	struct in_addr null_addr;
185	struct alias_link *opnrcv_link;
186	u_int32_t localPort;
187
188	*localIpAddr = (u_int32_t) opnrcvch_ack->ipAddr;
189	localPort = opnrcvch_ack->port;
190
191	null_addr.s_addr = INADDR_ANY;
192	opnrcv_link = FindUdpTcpOut(la, pip->ip_src, null_addr,
193	    htons((u_short) opnrcvch_ack->port), 0,
194	    IPPROTO_UDP, 1);
195	opnrcvch_ack->ipAddr = (u_int32_t) GetAliasAddress(opnrcv_link).s_addr;
196	opnrcvch_ack->port = (u_int32_t) ntohs(GetAliasPort(opnrcv_link));
197
198	tc->th_sum = 0;
199	tc->th_sum = TcpChecksum(pip);
200
201	return 0;
202}
203
204void
205AliasHandleSkinny(struct libalias *la, struct ip *pip, struct alias_link *link)
206{
207	int hlen, tlen, dlen;
208	struct tcphdr *tc;
209	u_int32_t msgId, len, t, lip;
210	struct skinny_header *sd;
211	int orig_len, skinny_hdr_len = sizeof(struct skinny_header);
212	ConvDirection direction;
213
214	tc = (struct tcphdr *)((char *)pip + (pip->ip_hl << 2));
215	hlen = (pip->ip_hl + tc->th_off) << 2;
216	tlen = ntohs(pip->ip_len);
217	dlen = tlen - hlen;
218
219	sd = (struct skinny_header *)((char *)pip + hlen);
220
221	/*
222	 * XXX This direction is reserved for future use.  I still need to
223	 * handle the scenario where the call manager is on the inside, and
224	 * the calling phone is on the global outside.
225	 */
226	if (ntohs(tc->th_dport) == la->skinnyPort) {
227		direction = ClientToServer;
228	} else if (ntohs(tc->th_sport) == la->skinnyPort) {
229		direction = ServerToClient;
230	} else {
231#ifdef DEBUG
232		fprintf(stderr,
233		    "PacketAlias/Skinny: Invalid port number, not a Skinny packet\n");
234#endif
235		return;
236	}
237
238	orig_len = dlen;
239	/*
240	 * Skinny packets can contain many messages.  We need to loop
241	 * through the packet using len to determine message boundaries.
242	 * This comes into play big time with port messages being in the
243	 * same packet as register messages.  Also, open receive channel
244	 * acks are usually buried in a pakcet some 400 bytes long.
245	 */
246	while (dlen >= skinny_hdr_len) {
247		len = (sd->len);
248		msgId = (sd->msgId);
249		t = len;
250
251		if (t < 0 || t > orig_len || t > dlen) {
252#ifdef DEBUG
253			fprintf(stderr,
254			    "PacketAlias/Skinny: Not a skinny packet, invalid length \n");
255#endif
256			return;
257		}
258		switch (msgId) {
259		case REG_MSG: {
260			struct RegisterMessage *reg_mesg;
261
262			if (len < sizeof(struct RegisterMessage)) {
263#ifdef DEBUG
264				fprintf(stderr,
265				    "PacketAlias/Skinny: Not a skinny packet, bad registration message\n");
266#endif
267				return;
268			}
269			reg_mesg = (struct RegisterMessage *)&sd->msgId;
270#ifdef DEBUG
271			fprintf(stderr,
272			    "PacketAlias/Skinny: Received a register message");
273#endif
274			alias_skinny_reg_msg(reg_mesg, pip, tc, link, direction);
275			break;
276		}
277		case IP_PORT_MSG: {
278			struct IpPortMessage *port_mesg;
279
280			if (len < sizeof(struct IpPortMessage)) {
281#ifdef DEBUG
282				fprintf(stderr,
283				    "PacketAlias/Skinny: Not a skinny packet, port message\n");
284#endif
285				return;
286			}
287#ifdef DEBUG
288			fprintf(stderr
289			    "PacketAlias/Skinny: Received ipport message\n");
290#endif
291			port_mesg = (struct IpPortMessage *)&sd->msgId;
292			alias_skinny_port_msg(port_mesg, pip, tc, link, direction);
293			break;
294		}
295		case OPNRCVCH_ACK: {
296			struct OpenReceiveChannelAck *opnrcvchn_ack;
297
298			if (len < sizeof(struct OpenReceiveChannelAck)) {
299#ifdef DEBUG
300				fprintf(stderr,
301				    "PacketAlias/Skinny: Not a skinny packet, packet,OpnRcvChnAckMsg\n");
302#endif
303				return;
304			}
305#ifdef DEBUG
306			fprintf(stderr,
307			    "PacketAlias/Skinny: Received open rcv channel msg\n");
308#endif
309			opnrcvchn_ack = (struct OpenReceiveChannelAck *)&sd->msgId;
310			alias_skinny_opnrcvch_ack(la, opnrcvchn_ack, pip, tc, link, &lip, direction);
311			break;
312		}
313		case START_MEDIATX: {
314			struct StartMediaTransmission *startmedia_tx;
315
316			if (len < sizeof(struct StartMediaTransmission)) {
317#ifdef DEBUG
318				fprintf(stderr,
319				    "PacketAlias/Skinny: Not a skinny packet,StartMediaTx Message\n");
320#endif
321				return;
322			}
323#ifdef DEBUG
324			fprintf(stderr,
325			    "PacketAlias/Skinny: Received start media trans msg\n");
326#endif
327			startmedia_tx = (struct StartMediaTransmission *)&sd->msgId;
328			alias_skinny_startmedia(startmedia_tx, pip, tc, link, lip, direction);
329			break;
330		}
331		default:
332			break;
333		}
334		/* Place the pointer at the next message in the packet. */
335		dlen -= len + (skinny_hdr_len - sizeof(msgId));
336		sd = (struct skinny_header *)(((char *)&sd->msgId) + len);
337	}
338}
339