blob: dd9b0c09f5adb7e4c3e57db974cbc349a0615e9f [file] [log] [blame]
Harald Welte338e3b32012-11-20 22:22:04 +01001/* SMPP vty interface */
2
3/* (C) 2012 by Harald Welte <laforge@gnumonks.org>
4 * All Rights Reserved
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
Harald Weltee07b6a72012-11-23 19:02:37 +010021#include <ctype.h>
Harald Welte338e3b32012-11-20 22:22:04 +010022#include <string.h>
Harald Weltee07b6a72012-11-23 19:02:37 +010023#include <errno.h>
Harald Welte338e3b32012-11-20 22:22:04 +010024#include <netdb.h>
25#include <sys/socket.h>
26
27#include <osmocom/vty/command.h>
28#include <osmocom/vty/buffer.h>
29#include <osmocom/vty/vty.h>
30
31#include <osmocom/core/linuxlist.h>
32#include <osmocom/core/utils.h>
33#include <osmocom/core/talloc.h>
34
35#include <openbsc/vty.h>
36
37#include "smpp_smsc.h"
38
39struct smsc *smsc_from_vty(struct vty *v);
40
41static struct cmd_node smpp_node = {
42 SMPP_NODE,
43 "%s(config-smpp)# ",
44 1,
45};
46
47static struct cmd_node esme_node = {
48 SMPP_ESME_NODE,
49 "%s(config-smpp-esme)# ",
50 1,
51};
52
53DEFUN(cfg_smpp, cfg_smpp_cmd,
54 "smpp", "Configure SMPP SMS Interface")
55{
56 vty->node = SMPP_NODE;
57
58 return CMD_SUCCESS;
59}
60
61DEFUN(cfg_smpp_port, cfg_smpp_port_cmd,
62 "local-tcp-port <1-65535>",
63 "Set the local TCP port on which we listen for SMPP\n"
64 "TCP port number")
65{
66 struct smsc *smsc = smsc_from_vty(vty);
67 uint16_t port = atoi(argv[0]);
68 int rc;
69
70 rc = smpp_smsc_init(smsc, port);
71 if (rc < 0) {
72 vty_out(vty, "%% Cannot bind to new port %u nor to "
73 "old port %u%s", port, smsc->listen_port, VTY_NEWLINE);
74 return CMD_WARNING;
75 }
76
77 if (port != smsc->listen_port) {
78 vty_out(vty, "%% Cannot bind to new port %u, staying on old"
79 "port %u%s", port, smsc->listen_port, VTY_NEWLINE);
80 return CMD_WARNING;
81 }
82
83 return CMD_SUCCESS;
84}
85
86DEFUN(cfg_smpp_sys_id, cfg_smpp_sys_id_cmd,
Harald Welteb4b21f52013-07-21 16:00:28 +080087 "system-id ID",
88 "Set the System ID of this SMSC\n"
89 "Alphanumeric SMSC System ID\n")
Harald Welte338e3b32012-11-20 22:22:04 +010090{
91 struct smsc *smsc = smsc_from_vty(vty);
92
93 if (strlen(argv[0])+1 > sizeof(smsc->system_id))
94 return CMD_WARNING;
95
96 strcpy(smsc->system_id, argv[0]);
97
98 return CMD_SUCCESS;
99}
100
101DEFUN(cfg_smpp_policy, cfg_smpp_policy_cmd,
102 "policy (accept-all|closed)",
103 "Set the authentication policy of this SMSC\n"
104 "Accept all SMPP connections independeint of system ID / passwd\n"
105 "Accept only SMPP connections from ESMEs explicitly configured")
106{
107 struct smsc *smsc = smsc_from_vty(vty);
108
109 if (!strcmp(argv[0], "accept-all"))
110 smsc->accept_all = 1;
111 else
112 smsc->accept_all = 0;
113
114 return CMD_SUCCESS;
115}
116
117
118static int config_write_smpp(struct vty *vty)
119{
120 struct smsc *smsc = smsc_from_vty(vty);
121
122 vty_out(vty, "smpp%s", VTY_NEWLINE);
123 vty_out(vty, " local-tcp-port %u%s", smsc->listen_port, VTY_NEWLINE);
Holger Hans Peter Freytherae9d8d32013-07-14 08:50:57 +0200124 if (strlen(smsc->system_id) > 0)
Harald Welteb862cef2013-01-24 09:54:04 +0100125 vty_out(vty, " system-id %s%s", smsc->system_id, VTY_NEWLINE);
Harald Welte338e3b32012-11-20 22:22:04 +0100126 vty_out(vty, " policy %s%s",
127 smsc->accept_all ? "accept-all" : "closed", VTY_NEWLINE);
128
129 return CMD_SUCCESS;
130}
131
132DEFUN(cfg_esme, cfg_esme_cmd,
Harald Welteb4b21f52013-07-21 16:00:28 +0800133 "esme NAME",
134 "Configure a particular ESME\n"
135 "Alphanumeric System ID of the ESME to be configured\n")
Harald Welte338e3b32012-11-20 22:22:04 +0100136{
137 struct smsc *smsc = smsc_from_vty(vty);
138 struct osmo_smpp_acl *acl;
139 const char *id = argv[0];
140
141 if (strlen(id) > 16) {
142 vty_out(vty, "%% System ID cannot be more than 16 "
143 "characters long%s", VTY_NEWLINE);
144 return CMD_WARNING;
145 }
146 acl = smpp_acl_by_system_id(smsc, id);
147 if (!acl) {
148 acl = smpp_acl_alloc(smsc, id);
149 if (!acl)
150 return CMD_WARNING;
151 }
152
153 vty->index = acl;
154 vty->index_sub = &acl->description;
155 vty->node = SMPP_ESME_NODE;
156
157 return CMD_SUCCESS;
158}
159
160DEFUN(cfg_no_esme, cfg_no_esme_cmd,
Harald Welteb4b21f52013-07-21 16:00:28 +0800161 "no esme NAME",
162 NO_STR "Remove ESME configuration\n"
163 "Alphanumeric System ID of the ESME to be removed\n")
Harald Welte338e3b32012-11-20 22:22:04 +0100164{
165 struct smsc *smsc = smsc_from_vty(vty);
166 struct osmo_smpp_acl *acl;
167 const char *id = argv[0];
168
169 acl = smpp_acl_by_system_id(smsc, id);
170 if (!acl) {
171 vty_out(vty, "%% ESME with system id '%s' unknown%s",
172 id, VTY_NEWLINE);
173 return CMD_WARNING;
174 }
175
176 /* FIXME: close the connection, free data structure, etc. */
177
178 smpp_acl_delete(acl);
179
180 return CMD_SUCCESS;
181}
182
183
184DEFUN(cfg_esme_passwd, cfg_esme_passwd_cmd,
Harald Welteb4b21f52013-07-21 16:00:28 +0800185 "password PASSWORD",
186 "Set the password for this ESME\n"
187 "Alphanumeric password string\n")
Harald Welte338e3b32012-11-20 22:22:04 +0100188{
189 struct osmo_smpp_acl *acl = vty->index;
190
191 if (strlen(argv[0])+1 > sizeof(acl->passwd))
192 return CMD_WARNING;
193
194 strcpy(acl->passwd, argv[0]);
195
196 return CMD_SUCCESS;
197}
198
199DEFUN(cfg_esme_no_passwd, cfg_esme_no_passwd_cmd,
Harald Welteb4b21f52013-07-21 16:00:28 +0800200 "no password",
201 NO_STR "Remove the password for this ESME\n")
Harald Welte338e3b32012-11-20 22:22:04 +0100202{
203 struct osmo_smpp_acl *acl = vty->index;
204
205 memset(acl->passwd, 0, sizeof(acl->passwd));
206
207 return CMD_SUCCESS;
208}
209
Harald Weltee07b6a72012-11-23 19:02:37 +0100210static int osmo_is_digits(const char *str)
211{
212 int i;
213 for (i = 0; i < strlen(str); i++) {
214 if (!isdigit(str[i]))
215 return 0;
216 }
217 return 1;
218}
219
220static const struct value_string route_errstr[] = {
221 { -EEXIST, "Route already exists" },
222 { -ENODEV, "Route does not exist" },
223 { -ENOMEM, "No memory" },
224 { -EINVAL, "Invalid" },
225 { 0, NULL }
226};
227
228static const struct value_string smpp_ton_str_short[] = {
229 { TON_Unknown, "unknown" },
230 { TON_International, "international" },
231 { TON_National, "national" },
232 { TON_Network_Specific, "network" },
233 { TON_Subscriber_Number,"subscriber" },
234 { TON_Alphanumeric, "alpha" },
235 { TON_Abbreviated, "abbrev" },
236 { 0, NULL }
237};
238
239static const struct value_string smpp_npi_str_short[] = {
240 { NPI_Unknown, "unknown" },
241 { NPI_ISDN_E163_E164, "isdn" },
242 { NPI_Data_X121, "x121" },
243 { NPI_Telex_F69, "f69" },
244 { NPI_Land_Mobile_E212, "e212" },
245 { NPI_National, "national" },
246 { NPI_Private, "private" },
247 { NPI_ERMES, "ermes" },
248 { NPI_Internet_IP, "ip" },
249 { NPI_WAP_Client_Id, "wap" },
250 { 0, NULL }
251};
252
253
254#define SMPP_ROUTE_STR "Configure a route for MO-SMS to be sent to this ESME\n"
Harald Welteb4b21f52013-07-21 16:00:28 +0800255#define SMPP_ROUTE_P_STR SMPP_ROUTE_STR "Prefix-match route\n"
Harald Weltee07b6a72012-11-23 19:02:37 +0100256#define SMPP_PREFIX_STR "Destination number prefix\n"
257
258#define TON_CMD "(unknown|international|national|network|subscriber|alpha|abbrev)"
259#define NPI_CMD "(unknown|isdn|x121|f69|e212|national|private|ermes|ip|wap)"
Harald Welteb4b21f52013-07-21 16:00:28 +0800260#define TON_STR "Unknown type-of-number\n" \
261 "International type-of-number\n" \
262 "National type-of-number\n" \
263 "Network specific type-of-number\n" \
264 "Subscriber type-of-number\n" \
265 "Alphanumeric type-of-number\n" \
266 "Abbreviated type-of-number\n"
267#define NPI_STR "Unknown numbering plan\n" \
268 "ISDN (E.164) numbering plan\n" \
269 "X.121 numbering plan\n" \
270 "F.69 numbering plan\n" \
271 "E.212 numbering plan\n" \
272 "National numbering plan\n" \
273 "Private numbering plan\n" \
274 "ERMES numbering plan\n" \
275 "IP numbering plan\n" \
276 "WAP numbeing plan\n"
Harald Weltee07b6a72012-11-23 19:02:37 +0100277
278DEFUN(cfg_esme_route_pfx, cfg_esme_route_pfx_cmd,
279 "route prefix " TON_CMD " " NPI_CMD " PREFIX",
280 SMPP_ROUTE_P_STR TON_STR NPI_STR SMPP_PREFIX_STR)
Harald Welte338e3b32012-11-20 22:22:04 +0100281{
282 struct osmo_smpp_acl *acl = vty->index;
Harald Weltee07b6a72012-11-23 19:02:37 +0100283 struct osmo_smpp_addr pfx;
284 int rc;
Harald Welte338e3b32012-11-20 22:22:04 +0100285
Harald Weltee07b6a72012-11-23 19:02:37 +0100286 /* check if DESTINATION is all-digits */
287 if (!osmo_is_digits(argv[2])) {
288 vty_out(vty, "%% PREFIX has to be numeric%s", VTY_NEWLINE);
289 return CMD_WARNING;
290 }
291
292 pfx.ton = get_string_value(smpp_ton_str_short, argv[0]);
293 pfx.npi = get_string_value(smpp_npi_str_short, argv[1]);
294 snprintf(pfx.addr, sizeof(pfx.addr), "%s", argv[2]);
295
296 rc = smpp_route_pfx_add(acl, &pfx);
297 if (rc < 0) {
298 vty_out(vty, "%% error adding prefix route: %s%s",
299 get_value_string(route_errstr, rc), VTY_NEWLINE);
300 return CMD_WARNING;
301 }
Harald Welte338e3b32012-11-20 22:22:04 +0100302
303 return CMD_SUCCESS;
304}
305
Harald Weltee07b6a72012-11-23 19:02:37 +0100306DEFUN(cfg_esme_no_route_pfx, cfg_esme_no_route_pfx_cmd,
307 "no route prefix " TON_CMD " " NPI_CMD " PREFIX",
308 NO_STR SMPP_ROUTE_P_STR TON_STR NPI_STR SMPP_PREFIX_STR)
309{
310 struct osmo_smpp_acl *acl = vty->index;
311 struct osmo_smpp_addr pfx;
312 int rc;
313
314 /* check if DESTINATION is all-digits */
315 if (!osmo_is_digits(argv[2])) {
316 vty_out(vty, "%% PREFIX has to be numeric%s", VTY_NEWLINE);
317 return CMD_WARNING;
318 }
319
320 pfx.ton = get_string_value(smpp_ton_str_short, argv[0]);
321 pfx.npi = get_string_value(smpp_npi_str_short, argv[1]);
322 snprintf(pfx.addr, sizeof(pfx.addr), "%s", argv[2]);
323
324 rc = smpp_route_pfx_del(acl, &pfx);
325 if (rc < 0) {
326 vty_out(vty, "%% error removing prefix route: %s%s",
327 get_value_string(route_errstr, rc), VTY_NEWLINE);
328 return CMD_WARNING;
329 }
330
331 return CMD_SUCCESS;
332
333}
334
335
Harald Welte338e3b32012-11-20 22:22:04 +0100336DEFUN(cfg_esme_defaultroute, cfg_esme_defaultroute_cmd,
337 "default-route",
338 "Set this ESME as default-route for all SMS to unknown destinations")
339{
340 struct osmo_smpp_acl *acl = vty->index;
341
342 acl->default_route = 1;
343
Harald Weltee07b6a72012-11-23 19:02:37 +0100344 if (!acl->smsc->def_route)
345 acl->smsc->def_route = acl;
346
Harald Welte338e3b32012-11-20 22:22:04 +0100347 return CMD_SUCCESS;
348}
349
350DEFUN(cfg_no_esme_defaultroute, cfg_esme_no_defaultroute_cmd,
351 "no default-route", NO_STR
Harald Welteb4b21f52013-07-21 16:00:28 +0800352 "Remove this ESME as default-route for all SMS to unknown destinations")
Harald Welte338e3b32012-11-20 22:22:04 +0100353{
354 struct osmo_smpp_acl *acl = vty->index;
355
356 acl->default_route = 0;
357
358 /* remove currently active default route, if it was created by
359 * this ACL */
Harald Weltee07b6a72012-11-23 19:02:37 +0100360 if (acl->smsc->def_route && acl->smsc->def_route == acl)
Harald Welte338e3b32012-11-20 22:22:04 +0100361 acl->smsc->def_route = NULL;
362
363 return CMD_SUCCESS;
364}
365
Harald Welte6a399ef2013-09-02 16:41:00 +0200366DEFUN(cfg_esme_del_src_imsi, cfg_esme_del_src_imsi_cmd,
367 "deliver-src-imsi",
368 "Enable the use of IMSI as source addres in DELIVER")
369{
370 struct osmo_smpp_acl *acl = vty->index;
371
372 acl->deliver_src_imsi = 1;
373
374 return CMD_SUCCESS;
375}
376
377DEFUN(cfg_esme_no_del_src_imsi, cfg_esme_no_del_src_imsi_cmd,
378 "no deliver-src-imsi", NO_STR
379 "Disable the use of IMSI as source addres in DELIVER")
380{
381 struct osmo_smpp_acl *acl = vty->index;
382
383 acl->deliver_src_imsi = 0;
384
385 return CMD_SUCCESS;
386}
387
Harald Welte3f786002013-03-13 15:29:27 +0100388DEFUN(cfg_esme_osmo_ext, cfg_esme_osmo_ext_cmd,
389 "osmocom-extensions",
390 "Enable the use of Osmocom SMPP Extensions for this ESME")
391{
392 struct osmo_smpp_acl *acl = vty->index;
393
394 acl->osmocom_ext = 1;
395
396 return CMD_SUCCESS;
397}
398
399DEFUN(cfg_esme_no_osmo_ext, cfg_esme_no_osmo_ext_cmd,
400 "no osmocom-extensions", NO_STR
401 "Disable the use of Osmocom SMPP Extensions for this ESME")
402{
403 struct osmo_smpp_acl *acl = vty->index;
404
405 acl->osmocom_ext = 0;
406
407 return CMD_SUCCESS;
408}
409
Harald Weltec75ed6d2013-05-28 20:58:02 +0200410DEFUN(cfg_esme_dcs_transp, cfg_esme_dcs_transp_cmd,
411 "dcs-transparent",
412 "Enable the transparent pass-through of TP-DCS to SMPP DataCoding")
413{
414 struct osmo_smpp_acl *acl = vty->index;
415
416 acl->dcs_transparent = 1;
417
418 return CMD_SUCCESS;
419}
420
421DEFUN(cfg_esme_no_dcs_transp, cfg_esme_no_dcs_transp_cmd,
422 "no dcs-transparent", NO_STR
423 "Disable the transparent pass-through of TP-DCS to SMPP DataCoding")
424{
425 struct osmo_smpp_acl *acl = vty->index;
426
427 acl->dcs_transparent = 0;
428
429 return CMD_SUCCESS;
430}
431
432
Harald Welte338e3b32012-11-20 22:22:04 +0100433static void dump_one_esme(struct vty *vty, struct osmo_esme *esme)
434{
435 char host[128], serv[128];
436
437 host[0] = 0;
438 serv[0] = 0;
439 getnameinfo((const struct sockaddr *) &esme->sa, esme->sa_len,
440 host, sizeof(host), serv, sizeof(serv), NI_NUMERICSERV);
441
442 vty_out(vty, "ESME System ID: %s, Password: %s, SMPP Version %02x%s",
Harald Weltee07b6a72012-11-23 19:02:37 +0100443 esme->system_id, esme->acl ? esme->acl->passwd : "",
444 esme->smpp_version, VTY_NEWLINE);
Harald Welte338e3b32012-11-20 22:22:04 +0100445 vty_out(vty, " Connected from: %s:%s%s", host, serv, VTY_NEWLINE);
Harald Weltee07b6a72012-11-23 19:02:37 +0100446 if (esme->smsc->def_route == esme->acl)
447 vty_out(vty, " Is current default route%s", VTY_NEWLINE);
Harald Welte338e3b32012-11-20 22:22:04 +0100448}
449
450DEFUN(show_esme, show_esme_cmd,
451 "show smpp esme",
452 SHOW_STR "SMPP Interface\n" "SMPP Extrenal SMS Entity\n")
453{
454 struct smsc *smsc = smsc_from_vty(vty);
455 struct osmo_esme *esme;
456
457 llist_for_each_entry(esme, &smsc->esme_list, list)
458 dump_one_esme(vty, esme);
459
460 return CMD_SUCCESS;
461}
462
Harald Weltee07b6a72012-11-23 19:02:37 +0100463static void write_esme_route_single(struct vty *vty, struct osmo_smpp_route *r)
464{
465 switch (r->type) {
466 case SMPP_ROUTE_PREFIX:
467 vty_out(vty, " route prefix %s ",
468 get_value_string(smpp_ton_str_short, r->u.prefix.ton));
469 vty_out(vty, "%s %s%s",
470 get_value_string(smpp_npi_str_short, r->u.prefix.npi),
471 r->u.prefix.addr, VTY_NEWLINE);
472 break;
473 case SMPP_ROUTE_NONE:
474 break;
475 }
476}
477
Harald Welte338e3b32012-11-20 22:22:04 +0100478static void config_write_esme_single(struct vty *vty, struct osmo_smpp_acl *acl)
479{
Harald Weltee07b6a72012-11-23 19:02:37 +0100480 struct osmo_smpp_route *r;
481
Harald Welte338e3b32012-11-20 22:22:04 +0100482 vty_out(vty, " esme %s%s", acl->system_id, VTY_NEWLINE);
483 if (strlen(acl->passwd))
484 vty_out(vty, " password %s%s", acl->passwd, VTY_NEWLINE);
485 if (acl->default_route)
486 vty_out(vty, " default-route%s", VTY_NEWLINE);
Harald Weltee07b6a72012-11-23 19:02:37 +0100487 if (acl->deliver_src_imsi)
488 vty_out(vty, " deliver-src-imsi%s", VTY_NEWLINE);
Harald Welte3f786002013-03-13 15:29:27 +0100489 if (acl->osmocom_ext)
490 vty_out(vty, " osmocom-extensions%s", VTY_NEWLINE);
Harald Weltec75ed6d2013-05-28 20:58:02 +0200491 if (acl->dcs_transparent)
492 vty_out(vty, " dcs-transparent%s", VTY_NEWLINE);
Harald Weltee07b6a72012-11-23 19:02:37 +0100493
494 llist_for_each_entry(r, &acl->route_list, list)
495 write_esme_route_single(vty, r);
Harald Welte338e3b32012-11-20 22:22:04 +0100496}
497
498static int config_write_esme(struct vty *v)
499{
500 struct smsc *smsc = smsc_from_vty(v);
501 struct osmo_smpp_acl *acl;
502
503 llist_for_each_entry(acl, &smsc->acl_list, list)
504 config_write_esme_single(v, acl);
505
506 return CMD_SUCCESS;
507}
508
509int smpp_vty_init(void)
510{
511 install_node(&smpp_node, config_write_smpp);
512 install_default(SMPP_NODE);
513 install_element(CONFIG_NODE, &cfg_smpp_cmd);
514
515 install_element(SMPP_NODE, &cfg_smpp_port_cmd);
516 install_element(SMPP_NODE, &cfg_smpp_sys_id_cmd);
517 install_element(SMPP_NODE, &cfg_smpp_policy_cmd);
518 install_element(SMPP_NODE, &cfg_esme_cmd);
519 install_element(SMPP_NODE, &cfg_no_esme_cmd);
520
521 install_node(&esme_node, config_write_esme);
522 install_default(SMPP_ESME_NODE);
523 install_element(SMPP_ESME_NODE, &cfg_esme_passwd_cmd);
524 install_element(SMPP_ESME_NODE, &cfg_esme_no_passwd_cmd);
Harald Weltee07b6a72012-11-23 19:02:37 +0100525 install_element(SMPP_ESME_NODE, &cfg_esme_route_pfx_cmd);
526 install_element(SMPP_ESME_NODE, &cfg_esme_no_route_pfx_cmd);
Harald Welte338e3b32012-11-20 22:22:04 +0100527 install_element(SMPP_ESME_NODE, &cfg_esme_defaultroute_cmd);
528 install_element(SMPP_ESME_NODE, &cfg_esme_no_defaultroute_cmd);
Harald Welte6a399ef2013-09-02 16:41:00 +0200529 install_element(SMPP_ESME_NODE, &cfg_esme_del_src_imsi_cmd);
530 install_element(SMPP_ESME_NODE, &cfg_esme_no_del_src_imsi_cmd);
Harald Welte3f786002013-03-13 15:29:27 +0100531 install_element(SMPP_ESME_NODE, &cfg_esme_osmo_ext_cmd);
532 install_element(SMPP_ESME_NODE, &cfg_esme_no_osmo_ext_cmd);
Harald Weltec75ed6d2013-05-28 20:58:02 +0200533 install_element(SMPP_ESME_NODE, &cfg_esme_dcs_transp_cmd);
534 install_element(SMPP_ESME_NODE, &cfg_esme_no_dcs_transp_cmd);
Harald Welte338e3b32012-11-20 22:22:04 +0100535 install_element(SMPP_ESME_NODE, &ournode_exit_cmd);
536
537 install_element_ve(&show_esme_cmd);
538
539 return 0;
540}