The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
sccp.c
Go to the documentation of this file.
1 /*
2  * @copyright (c) 2016, Network RADIUS SAS (license@networkradius.com)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  * * Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * * Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  * * Neither the name of Network RADIUS SAS nor the
13  * names of its contributors may be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 /**
29  * $Id: 7911a8da148463176e4344fea79a2347e63d30f6 $
30  * @file rlm_sigtran/sccp.c
31  * @brief Implement SCCP/TCAP glue layer
32  *
33  * @author Arran Cudbard-Bell
34  *
35  * @copyright 2016 Network RADIUS SAS (license@networkradius.com)
36  */
37 #define LOG_PREFIX "sigtran - osmocom thread"
38 
39 #include <osmocom/core/talloc.h>
40 
41 #include <freeradius-devel/server/base.h>
42 #include <freeradius-devel/util/debug.h>
43 #include <osmocom/core/msgb.h>
44 #include <osmocom/core/utils.h>
45 
46 #include "sigtran.h"
47 
48 #undef DEBUG
49 
50 #include "libosmo-m3ua/include/cellmgr_debug.h"
51 #include "libosmo-m3ua/include/mtp_data.h"
52 #include "libosmo-m3ua/include/sctp_m3ua.h"
53 
54 static uint32_t last_txn_id = 0; //!< Global transaction ID
55 static fr_rb_tree_t *txn_tree = NULL; //!< Global transaction tree... Should really be per module.
57 
58 /** Compare rounds of a transaction
59  *
60  */
61 static int sigtran_txn_cmp(void const *one, void const *two)
62 {
63  sigtran_transaction_t const *a = one; /* May be stack allocated */
64  sigtran_transaction_t const *b = two; /* May be stack allocated */
65 
66  CMP_RETURN(a, b, ctx.otid);
67 
68  return CMP(a->ctx.invoke_id, b->ctx.invoke_id);
69 }
70 
71 static void sigtran_tcap_timeout(void *data)
72 {
73  sigtran_transaction_t *txn = talloc_get_type_abort(data, sigtran_transaction_t);
74 
75  ERROR("OTID %u Invoke ID %u timeout", txn->ctx.otid, txn->ctx.invoke_id);
76 
77  /*
78  * Remove the outstanding transaction
79  */
80  if (!fr_rb_delete(txn_tree, txn)) ERROR("Transaction removed before timeout");
81 
82  txn->response.type = SIGTRAN_RESPONSE_FAIL;
83 
84  if (sigtran_event_submit(txn->ctx.ofd, txn) < 0) {
85  ERROR("Failed informing event client of result: %s", fr_syserror(errno));
86  return;
87  }
88 }
89 
90 /** Send a request with static MAP data in it
91  *
92  * SCCP will add its headers and call sigtran_sccp_outgoing
93  *
94  * @return
95  * - 0 on success.
96  * - <0 on failure.
97  */
98 int sigtran_tcap_outgoing(UNUSED struct msgb *msg_in, void *ctx, sigtran_transaction_t *txn, UNUSED struct osmo_fd *ofd)
99 {
100  static uint8_t tcap_map_raw_v2[] = {
101  0x62, 0x43, 0x48, 0x01, 0x01, 0x6b, 0x80, 0x28, /* 0x00 */
102  0x80, 0x06, 0x07, 0x00, 0x11, 0x86, 0x05, 0x01, /* 0x08 */
103  0x01, 0x01, 0xa0, 0x80, 0x60, 0x80, 0x80, 0x02, /* 0x10 */
104  0x07, 0x80, 0xa1, 0x80, 0x06, 0x07, 0x04, 0x00, /* 0x18 */
105  0x00, 0x01, 0x00, 0x0e, 0x02, 0x00, 0x00, 0x00, /* 0x20 (0x24 is version)*/
106  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, /* 0x30 (0x35 is invoke ID) */
107  0x14, 0xa1, 0x80, 0x02, 0x01, 0x03, 0x02, 0x01, /* 0x38 (0x3c is IMSI len, 0x3d-0x44 IMSI) */
108  0x38, 0x04, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x40 */
109  0xff, 0xff, 0xff, 0x00, 0x00 }; /* 0x48 */
110 
111  static uint8_t tcap_map_raw_v3[] = {
112  0x62, 0x48, 0x48, 0x01, 0x01, 0x6b, 0x80, 0x28, /* 0x00 */
113  0x80, 0x06, 0x07, 0x00, 0x11, 0x86, 0x05, 0x01, /* 0x08 */
114  0x01, 0x01, 0xa0, 0x80, 0x60, 0x80, 0x80, 0x02, /* 0x10 */
115  0x07, 0x80, 0xa1, 0x80, 0x06, 0x07, 0x04, 0x00, /* 0x18 */
116  0x00, 0x01, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x00, /* 0x20 (0x24 is version)*/
117  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, /* 0x28 */
118  0x19, 0xa1, 0x80, 0x02, 0x01, 0x01, 0x02, 0x01, /* 0x30 (0x35 is invoke ID) */
119  0x38, 0x30, 0x0d, 0x80, 0x00, 0x00, 0x00, 0x00, /* 0x38 (0x3c is IMSI len, 0x3d-0x44 IMSI) */
120  0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x01, /* 0x40 */
121  0x00, 0x00 }; /* 0x48 */
122 
124  talloc_get_type_abort(txn->request.data, sigtran_map_send_auth_info_req_t);
125 
126  struct msgb *msg;
127 
128  sigtran_conn_t *conn = talloc_get_type_abort(ctx, sigtran_conn_t);
129  struct mtp_m3ua_client_link *m3ua_client = talloc_get_type_abort(conn->mtp3_link->data,
130  struct mtp_m3ua_client_link);
131 
132  fr_assert(req->imsi);
133 
134  if (!mtp_m3ua_link_is_up(m3ua_client)) {
135  ERROR("Link not yet active, dropping the request");
136 
137  return -1;
138  }
139 
141  ERROR("Too many outstanding requests, dropping the request");
142 
143  return -1;
144  }
145 
146  switch (req->version) {
147  case 2:
148  DEBUG4("Allocating buffer for MAP v2, %zu bytes", sizeof(tcap_map_raw_v2));
149  msg = msgb_alloc(sizeof(tcap_map_raw_v2), "sccp: tcap_map");
150  msg->l3h = msgb_put(msg, sizeof(tcap_map_raw_v2));
151  memcpy(msg->l3h, tcap_map_raw_v2, sizeof(tcap_map_raw_v2));
152 
153  *(msg->l3h + 0x3a) = talloc_array_length(req->imsi);
154  memcpy(msg->l3h + 0x3b, req->imsi, talloc_array_length(req->imsi));
155 // RHEXDUMP(0, msg->l3h, sizeof(tcap_map_raw_v2), "MAPv2 Request");
156 
157  break;
158 
159  case 3:
160  DEBUG4("Allocating buffer for MAP v3, %zu bytes", sizeof(tcap_map_raw_v3));
161  msg = msgb_alloc(sizeof(tcap_map_raw_v3), "sccp: tcap_map");
162  msg->l3h = msgb_put(msg, sizeof(tcap_map_raw_v3));
163  memcpy(msg->l3h, tcap_map_raw_v3, sizeof(tcap_map_raw_v3));
164 
165  *(msg->l3h + 0x3c) = talloc_array_length(req->imsi);
166  memcpy(msg->l3h + 0x3d, req->imsi, talloc_array_length(req->imsi));
167 // RHEXDUMP(0, msg->l3h, sizeof(tcap_map_raw_v3), "MAPv3 Request");
168 
169  break;
170 
171  default:
172  fr_assert_fail(NULL);
173  return -1;
174  }
175 
176  /*
177  * Set the transaction ID
178  */
179  txn->ctx.otid = (last_txn_id++) & UINT8_MAX; /* 8 bit for now */
180 
181  txn->ctx.invoke_id++; /* Needs to be two operations */
182  txn->ctx.invoke_id &= 0x7f; /* Invoke ID is 7bits */
183  DEBUG2("Sending request with OTID %u Invoke ID %u", txn->ctx.otid, txn->ctx.invoke_id);
184 
185  if (!fr_rb_insert(txn_tree, txn)) {
186  ERROR("Failed inserting transaction, maybe at txn limit?");
187 
188  msgb_free(msg);
189  return -1;
190  }
191 
192  /*
193  * Set OTID and Invoke ID in the packet
194  */
195  *(msg->l3h + 0x04) = txn->ctx.otid;
196  *(msg->l3h + 0x35) = txn->ctx.invoke_id;
197 
198  sccp_write(msg, &conn->conf->sccp_calling_sockaddr, &conn->conf->sccp_called_sockaddr,
199  SCCP_PROTOCOL_RETURN_MESSAGE << 4 | SCCP_PROTOCOL_CLASS_0, ctx); /* Class is connectionless (ish) */
200 
201  msgb_free(msg);
202 
203  txn->ctx.timer.data = txn;
204  txn->ctx.timer.cb = sigtran_tcap_timeout;
205 
206  osmo_timer_schedule(&txn->ctx.timer, 1, 0);
207 
208  return 0;
209 }
210 
211 /** Incoming data
212  *
213  * This should be called by the SCCP functions to give us result data
214  */
215 static int sigtran_tcap_incoming(struct msgb *msg, UNUSED unsigned int length, UNUSED void *ctx)
216 {
217  sigtran_vector_t *vec;
218  uint8_t *tcap = msg->l3h;
219  uint8_t *p, *end;
220  size_t len = (size_t)msgb_l3len(msg);
221 
222  sigtran_transaction_t find, *found;
224 
227 
228  struct osmo_fd *ofd;
229  sigtran_vector_t **last;
230 
231  memset(&find, 0, sizeof(find));
232 
233 // sigtran_conn_t *conn = talloc_get_type_abort(ctx, sigtran_conn_t);
234 
235  DEBUG3("Got %zu bytes of L4 data", (size_t)msgb_l3len(msg));
236 // log_request_hex(L_DBG, L_DBG_LVL_3, request, msg->l3h, (size_t)msgb_l3len(msg));
237 
238  find.ctx.otid = *(msg->l3h + 0x5);
239 
240  // find.ctx.invoke_id = *(msg->l3h + 0x34);
241  find.ctx.invoke_id = 1; /* Always 1 for now... */
242  DEBUG2("Received response with DTID %u Invoke ID %u", find.ctx.otid, find.ctx.invoke_id);
243 
244  /*
245  * Lookup the transaction in our tree of outstanding transactions
246  */
247  found = fr_rb_find(txn_tree, &find);
248  if (!found) {
249  /*
250  * Not an error, could be a retransmission
251  */
252  ERROR("No outstanding transaction with DTID %u Invoke ID %u", find.ctx.otid, find.ctx.invoke_id);
253  return 0;
254  }
255  if (!fr_rb_delete(txn_tree, found)) { /* Remove the outstanding transaction */
256  ERROR("Failed removing transaction");
257  fr_assert(0);
258  }
259 
260  txn = talloc_get_type_abort(found, sigtran_transaction_t);
261  req = talloc_get_type_abort(txn->request.data, sigtran_map_send_auth_info_req_t);
262  ofd = txn->ctx.ofd;
263  osmo_timer_del(&txn->ctx.timer); /* Remove the timeout timer */
264 
265  MEM(res = talloc_zero(txn, sigtran_map_send_auth_info_res_t));
266  txn->response.type = SIGTRAN_RESPONSE_OK;
267  txn->response.data = res;
268  last = &res->vector; /* Head of vector list */
269 
270 #define sigtran_memdup(_x) \
271  do { \
272  p++; \
273  DEBUG4("Start 0x%02x len %u", (unsigned int)(tcap - p), p[0]); \
274  if (p[0] >= (len - (p - tcap))) { \
275  ERROR("Invalid length %u specified for vector component", p[0]); \
276  return -1; \
277  } \
278  vec->_x = talloc_memdup(vec, p + 1, p[0]); \
279  talloc_set_type(vec->_x, uint8_t); \
280  p += p[0] + 1; \
281  } while (0)
282 
283  end = tcap + msgb_l3len(msg);
284 
285  /*
286  * And now pretend to parse the response by looking at
287  * fixed offsets in the response data...
288  *
289  * Umm.. fixme?
290  */
291  if (req->version == 2) {
292  p = tcap + 0x40;
293  while (p < end) {
294  if ((p[0] != 0x30) || (p[1] != 0x22)) {
295  DEBUG4("Breaking out of parsing loop at %x", (uint32_t)(p - tcap));
296  break;
297  }
298  p += 2;
299 
300  MEM(vec = talloc_zero(res, sigtran_vector_t));
302 
303  sigtran_memdup(sim.rand);
304  sigtran_memdup(sim.sres);
305  sigtran_memdup(sim.kc);
306 
307  *last = vec;
308  last = &(vec->next);
309  }
310  } else if (req->version == 3) {
311  p = tcap + 0x40; /* fixed offset for now */
312 
313  MEM(vec = talloc_zero(res, sigtran_vector_t));
315  sigtran_memdup(umts.rand);
316  sigtran_memdup(umts.xres);
317  sigtran_memdup(umts.ck);
318  sigtran_memdup(umts.ik);
319  sigtran_memdup(umts.authn);
320 
321  *last = vec;
322  }
323 
324  if (sigtran_event_submit(ofd, txn) < 0) {
325  ERROR("Failed informing event client of result: %s", fr_syserror(errno));
326  return -1;
327  }
328 
329  return 0;
330 }
331 
332 /** Wrapper to pass data down to MTP3 layer for processing
333  *
334  * This is the write callback for the SCCP Code.
335  */
336 static void sigtran_sccp_outgoing(UNUSED struct sccp_connection *sscp_conn,
337  struct msgb *msg, UNUSED void *write_ctx, void *ctx)
338 {
339  sigtran_conn_t *conn = talloc_get_type_abort(ctx, sigtran_conn_t);
340 
341  mtp_link_set_submit_sccp_data(conn->mtp3_link_set, -1, msg->l2h, msgb_l2len(msg));
342 
343  msgb_free(msg); /* Apparently our responsibility to free this message */
344 }
345 
346 /** Wrapper to pass data off to libsccp for processing
347  *
348  * @param set Link set data was received on.
349  * @param msg Data from the lower layer.
350  * @param sls Link number the data was received on.
351  */
352 void sigtran_sccp_incoming(UNUSED struct mtp_link_set *set, struct msgb *msg, UNUSED int sls)
353 {
354  sccp_system_incoming(msg);
355 }
356 
357 /** Initialise libscctp
358  *
359  */
361 {
362  sccp_set_log_area(DSCCP);
363 
364  sccp_system_init(sigtran_sccp_outgoing, NULL); /* Set write callback */
365  sccp_set_variant(SCCP_VARIANT_ANSI);
366  sccp_set_read(&conn->conf->sccp_calling_sockaddr, sigtran_tcap_incoming, conn); /* Set data_available callback */
367 
368  return 0;
369 }
370 
372 {
373  if (txn_tree) {
374  txn_tree_inst++;
375  return 0;
376  }
377 
379  if (!txn_tree) return -1;
380 
381  txn_tree_inst++;
382  return 0;
383 }
384 
386 {
387  if (--txn_tree_inst > 0) return;
388 
390  txn_tree = NULL;
391 }
log_entry msg
Definition: acutest.h:794
#define CMP_RETURN(_a, _b, _field)
Return if the comparison is not 0 (is unequal)
Definition: build.h:119
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
Definition: build.h:110
#define UNUSED
Definition: build.h:313
#define fr_assert_fail(_msg,...)
Calls panic_action ifndef NDEBUG, else logs error.
Definition: debug.h:214
#define ERROR(fmt,...)
Definition: dhcpclient.c:41
#define DEBUG3(_fmt,...)
Definition: log.h:266
#define DEBUG4(_fmt,...)
Definition: log.h:267
talloc_free(reap)
unsigned int uint32_t
Definition: merged_model.c:33
unsigned char uint8_t
Definition: merged_model.c:30
unsigned long int size_t
Definition: merged_model.c:25
#define UINT8_MAX
Definition: merged_model.c:32
int sigtran_event_submit(struct osmo_fd *ofd, sigtran_transaction_t *txn)
Send response.
Definition: event.c:283
#define DEBUG2(fmt,...)
Definition: radclient.h:43
uint32_t fr_rb_num_elements(fr_rb_tree_t *tree)
Return how many nodes there are in a tree.
Definition: rb.c:775
bool fr_rb_insert(fr_rb_tree_t *tree, void const *data)
Insert data into a tree.
Definition: rb.c:624
bool fr_rb_delete(fr_rb_tree_t *tree, void const *data)
Remove node and free data (if a free function was specified)
Definition: rb.c:736
void * fr_rb_find(fr_rb_tree_t const *tree, void const *data)
Find an element in the tree, returning the data, not the node.
Definition: rb.c:576
#define fr_rb_inline_talloc_alloc(_ctx, _type, _field, _data_cmp, _data_free)
Allocs a red black that verifies elements are of a specific talloc type.
Definition: rb.h:246
The main red black tree structure.
Definition: rb.h:73
static uint32_t txn_tree_inst
Definition: sccp.c:56
void sigtran_sccp_incoming(UNUSED struct mtp_link_set *set, struct msgb *msg, UNUSED int sls)
Wrapper to pass data off to libsccp for processing.
Definition: sccp.c:352
#define sigtran_memdup(_x)
void sigtran_sccp_global_free(void)
Definition: sccp.c:385
static void sigtran_sccp_outgoing(UNUSED struct sccp_connection *sscp_conn, struct msgb *msg, UNUSED void *write_ctx, void *ctx)
Wrapper to pass data down to MTP3 layer for processing.
Definition: sccp.c:336
static fr_rb_tree_t * txn_tree
Global transaction tree... Should really be per module.
Definition: sccp.c:55
static uint32_t last_txn_id
Global transaction ID.
Definition: sccp.c:54
int sigtran_tcap_outgoing(UNUSED struct msgb *msg_in, void *ctx, sigtran_transaction_t *txn, UNUSED struct osmo_fd *ofd)
Send a request with static MAP data in it.
Definition: sccp.c:98
static int sigtran_tcap_incoming(struct msgb *msg, UNUSED unsigned int length, UNUSED void *ctx)
Incoming data.
Definition: sccp.c:215
int sigtran_sscp_init(sigtran_conn_t *conn)
Initialise libscctp.
Definition: sccp.c:360
static void sigtran_tcap_timeout(void *data)
Definition: sccp.c:71
static int sigtran_txn_cmp(void const *one, void const *two)
Compare rounds of a transaction.
Definition: sccp.c:61
int sigtran_sccp_global_init(void)
Definition: sccp.c:371
Declarations for various sigtran functions.
@ SIGTRAN_VECTOR_TYPE_UMTS_QUINTUPLETS
RAND, XRES, CK, IK, AUTN.
Definition: sigtran.h:66
@ SIGTRAN_VECTOR_TYPE_SIM_TRIPLETS
RAND, SRES, Kc.
Definition: sigtran.h:65
@ SIGTRAN_RESPONSE_OK
Request succeeded.
Definition: sigtran.h:55
@ SIGTRAN_RESPONSE_FAIL
Request failed.
Definition: sigtran.h:58
struct mtp_link_set * mtp3_link_set
Definition: sigtran.h:184
uint8_t * imsi
BCD encoded IMSI.
Definition: sigtran.h:193
struct sigtran_transaction::@164 response
struct sigtran_transaction::@163 request
sigtran_vector_type_t type
Type of vector returned.
Definition: sigtran.h:218
struct sockaddr_sccp sccp_calling_sockaddr
Parsed version of the above.
Definition: sigtran.h:169
sigtran_conn_conf_t * conf
Definition: sigtran.h:181
uint8_t version
Application context version.
Definition: sigtran.h:194
struct sigtran_transaction::@165 ctx
struct mtp_link * mtp3_link
Definition: sigtran.h:185
sigtran_vector_t * vector
Linked list of vectors.
Definition: sigtran.h:228
sigtran_vector_t * next
Next vector in list.
Definition: sigtran.h:220
struct sockaddr_sccp sccp_called_sockaddr
Parsed version of the above.
Definition: sigtran.h:171
Represents a connection to a remote SS7 entity.
Definition: sigtran.h:180
MAP send auth info request.
Definition: sigtran.h:191
MAP send auth info response.
Definition: sigtran.h:226
Request and response from the event loop.
Definition: sigtran.h:75
Authentication vector returned by HLR.
Definition: sigtran.h:203
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition: syserror.c:243
static fr_slen_t data
Definition: value.h:1259