blob: 96447bf3739329338eb65510786969fa0fdf8530 [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,
87 "system-id ID", "Set the System ID of this SMSC")
88{
89 struct smsc *smsc = smsc_from_vty(vty);
90
91 if (strlen(argv[0])+1 > sizeof(smsc->system_id))
92 return CMD_WARNING;
93
94 strcpy(smsc->system_id, argv[0]);
95
96 return CMD_SUCCESS;
97}
98
99DEFUN(cfg_smpp_policy, cfg_smpp_policy_cmd,
100 "policy (accept-all|closed)",
101 "Set the authentication policy of this SMSC\n"
102 "Accept all SMPP connections independeint of system ID / passwd\n"
103 "Accept only SMPP connections from ESMEs explicitly configured")
104{
105 struct smsc *smsc = smsc_from_vty(vty);
106
107 if (!strcmp(argv[0], "accept-all"))
108 smsc->accept_all = 1;
109 else
110 smsc->accept_all = 0;
111
112 return CMD_SUCCESS;
113}
114
115
116static int config_write_smpp(struct vty *vty)
117{
118 struct smsc *smsc = smsc_from_vty(vty);
119
120 vty_out(vty, "smpp%s", VTY_NEWLINE);
121 vty_out(vty, " local-tcp-port %u%s", smsc->listen_port, VTY_NEWLINE);
Harald Welteb862cef2013-01-24 09:54:04 +0100122 if (smsc->system_id)
123 vty_out(vty, " system-id %s%s", smsc->system_id, VTY_NEWLINE);
Harald Welte338e3b32012-11-20 22:22:04 +0100124 vty_out(vty, " policy %s%s",
125 smsc->accept_all ? "accept-all" : "closed", VTY_NEWLINE);
126
127 return CMD_SUCCESS;
128}
129
130DEFUN(cfg_esme, cfg_esme_cmd,
131 "esme NAME", "Configure a particular ESME")
132{
133 struct smsc *smsc = smsc_from_vty(vty);
134 struct osmo_smpp_acl *acl;
135 const char *id = argv[0];
136
137 if (strlen(id) > 16) {
138 vty_out(vty, "%% System ID cannot be more than 16 "
139 "characters long%s", VTY_NEWLINE);
140 return CMD_WARNING;
141 }
142 acl = smpp_acl_by_system_id(smsc, id);
143 if (!acl) {
144 acl = smpp_acl_alloc(smsc, id);
145 if (!acl)
146 return CMD_WARNING;
147 }
148
149 vty->index = acl;
150 vty->index_sub = &acl->description;
151 vty->node = SMPP_ESME_NODE;
152
153 return CMD_SUCCESS;
154}
155
156DEFUN(cfg_no_esme, cfg_no_esme_cmd,
157 "no esme NAME", NO_STR "Remove ESME configuration")
158{
159 struct smsc *smsc = smsc_from_vty(vty);
160 struct osmo_smpp_acl *acl;
161 const char *id = argv[0];
162
163 acl = smpp_acl_by_system_id(smsc, id);
164 if (!acl) {
165 vty_out(vty, "%% ESME with system id '%s' unknown%s",
166 id, VTY_NEWLINE);
167 return CMD_WARNING;
168 }
169
170 /* FIXME: close the connection, free data structure, etc. */
171
172 smpp_acl_delete(acl);
173
174 return CMD_SUCCESS;
175}
176
177
178DEFUN(cfg_esme_passwd, cfg_esme_passwd_cmd,
179 "password PASSWORD", "Set the password for this ESME")
180{
181 struct osmo_smpp_acl *acl = vty->index;
182
183 if (strlen(argv[0])+1 > sizeof(acl->passwd))
184 return CMD_WARNING;
185
186 strcpy(acl->passwd, argv[0]);
187
188 return CMD_SUCCESS;
189}
190
191DEFUN(cfg_esme_no_passwd, cfg_esme_no_passwd_cmd,
192 "no password", NO_STR "Set the password for this ESME")
193{
194 struct osmo_smpp_acl *acl = vty->index;
195
196 memset(acl->passwd, 0, sizeof(acl->passwd));
197
198 return CMD_SUCCESS;
199}
200
Harald Weltee07b6a72012-11-23 19:02:37 +0100201static int osmo_is_digits(const char *str)
202{
203 int i;
204 for (i = 0; i < strlen(str); i++) {
205 if (!isdigit(str[i]))
206 return 0;
207 }
208 return 1;
209}
210
211static const struct value_string route_errstr[] = {
212 { -EEXIST, "Route already exists" },
213 { -ENODEV, "Route does not exist" },
214 { -ENOMEM, "No memory" },
215 { -EINVAL, "Invalid" },
216 { 0, NULL }
217};
218
219static const struct value_string smpp_ton_str_short[] = {
220 { TON_Unknown, "unknown" },
221 { TON_International, "international" },
222 { TON_National, "national" },
223 { TON_Network_Specific, "network" },
224 { TON_Subscriber_Number,"subscriber" },
225 { TON_Alphanumeric, "alpha" },
226 { TON_Abbreviated, "abbrev" },
227 { 0, NULL }
228};
229
230static const struct value_string smpp_npi_str_short[] = {
231 { NPI_Unknown, "unknown" },
232 { NPI_ISDN_E163_E164, "isdn" },
233 { NPI_Data_X121, "x121" },
234 { NPI_Telex_F69, "f69" },
235 { NPI_Land_Mobile_E212, "e212" },
236 { NPI_National, "national" },
237 { NPI_Private, "private" },
238 { NPI_ERMES, "ermes" },
239 { NPI_Internet_IP, "ip" },
240 { NPI_WAP_Client_Id, "wap" },
241 { 0, NULL }
242};
243
244
245#define SMPP_ROUTE_STR "Configure a route for MO-SMS to be sent to this ESME\n"
246#define SMPP_ROUTE_P_STR "Prefix-match route\n"
247#define SMPP_PREFIX_STR "Destination number prefix\n"
248
249#define TON_CMD "(unknown|international|national|network|subscriber|alpha|abbrev)"
250#define NPI_CMD "(unknown|isdn|x121|f69|e212|national|private|ermes|ip|wap)"
251#define TON_STR "FIXME"
252#define NPI_STR "FIXME"
253
254DEFUN(cfg_esme_route_pfx, cfg_esme_route_pfx_cmd,
255 "route prefix " TON_CMD " " NPI_CMD " PREFIX",
256 SMPP_ROUTE_P_STR TON_STR NPI_STR SMPP_PREFIX_STR)
Harald Welte338e3b32012-11-20 22:22:04 +0100257{
258 struct osmo_smpp_acl *acl = vty->index;
Harald Weltee07b6a72012-11-23 19:02:37 +0100259 struct osmo_smpp_addr pfx;
260 int rc;
Harald Welte338e3b32012-11-20 22:22:04 +0100261
Harald Weltee07b6a72012-11-23 19:02:37 +0100262 /* check if DESTINATION is all-digits */
263 if (!osmo_is_digits(argv[2])) {
264 vty_out(vty, "%% PREFIX has to be numeric%s", VTY_NEWLINE);
265 return CMD_WARNING;
266 }
267
268 pfx.ton = get_string_value(smpp_ton_str_short, argv[0]);
269 pfx.npi = get_string_value(smpp_npi_str_short, argv[1]);
270 snprintf(pfx.addr, sizeof(pfx.addr), "%s", argv[2]);
271
272 rc = smpp_route_pfx_add(acl, &pfx);
273 if (rc < 0) {
274 vty_out(vty, "%% error adding prefix route: %s%s",
275 get_value_string(route_errstr, rc), VTY_NEWLINE);
276 return CMD_WARNING;
277 }
Harald Welte338e3b32012-11-20 22:22:04 +0100278
279 return CMD_SUCCESS;
280}
281
Harald Weltee07b6a72012-11-23 19:02:37 +0100282DEFUN(cfg_esme_no_route_pfx, cfg_esme_no_route_pfx_cmd,
283 "no route prefix " TON_CMD " " NPI_CMD " PREFIX",
284 NO_STR SMPP_ROUTE_P_STR TON_STR NPI_STR SMPP_PREFIX_STR)
285{
286 struct osmo_smpp_acl *acl = vty->index;
287 struct osmo_smpp_addr pfx;
288 int rc;
289
290 /* check if DESTINATION is all-digits */
291 if (!osmo_is_digits(argv[2])) {
292 vty_out(vty, "%% PREFIX has to be numeric%s", VTY_NEWLINE);
293 return CMD_WARNING;
294 }
295
296 pfx.ton = get_string_value(smpp_ton_str_short, argv[0]);
297 pfx.npi = get_string_value(smpp_npi_str_short, argv[1]);
298 snprintf(pfx.addr, sizeof(pfx.addr), "%s", argv[2]);
299
300 rc = smpp_route_pfx_del(acl, &pfx);
301 if (rc < 0) {
302 vty_out(vty, "%% error removing prefix route: %s%s",
303 get_value_string(route_errstr, rc), VTY_NEWLINE);
304 return CMD_WARNING;
305 }
306
307 return CMD_SUCCESS;
308
309}
310
311
Harald Welte338e3b32012-11-20 22:22:04 +0100312DEFUN(cfg_esme_defaultroute, cfg_esme_defaultroute_cmd,
313 "default-route",
314 "Set this ESME as default-route for all SMS to unknown destinations")
315{
316 struct osmo_smpp_acl *acl = vty->index;
317
318 acl->default_route = 1;
319
Harald Weltee07b6a72012-11-23 19:02:37 +0100320 if (!acl->smsc->def_route)
321 acl->smsc->def_route = acl;
322
Harald Welte338e3b32012-11-20 22:22:04 +0100323 return CMD_SUCCESS;
324}
325
326DEFUN(cfg_no_esme_defaultroute, cfg_esme_no_defaultroute_cmd,
327 "no default-route", NO_STR
328 "Set this ESME as default-route for all SMS to unknown destinations")
329{
330 struct osmo_smpp_acl *acl = vty->index;
331
332 acl->default_route = 0;
333
334 /* remove currently active default route, if it was created by
335 * this ACL */
Harald Weltee07b6a72012-11-23 19:02:37 +0100336 if (acl->smsc->def_route && acl->smsc->def_route == acl)
Harald Welte338e3b32012-11-20 22:22:04 +0100337 acl->smsc->def_route = NULL;
338
339 return CMD_SUCCESS;
340}
341
342static void dump_one_esme(struct vty *vty, struct osmo_esme *esme)
343{
344 char host[128], serv[128];
345
346 host[0] = 0;
347 serv[0] = 0;
348 getnameinfo((const struct sockaddr *) &esme->sa, esme->sa_len,
349 host, sizeof(host), serv, sizeof(serv), NI_NUMERICSERV);
350
351 vty_out(vty, "ESME System ID: %s, Password: %s, SMPP Version %02x%s",
Harald Weltee07b6a72012-11-23 19:02:37 +0100352 esme->system_id, esme->acl ? esme->acl->passwd : "",
353 esme->smpp_version, VTY_NEWLINE);
Harald Welte338e3b32012-11-20 22:22:04 +0100354 vty_out(vty, " Connected from: %s:%s%s", host, serv, VTY_NEWLINE);
Harald Weltee07b6a72012-11-23 19:02:37 +0100355 if (esme->smsc->def_route == esme->acl)
356 vty_out(vty, " Is current default route%s", VTY_NEWLINE);
Harald Welte338e3b32012-11-20 22:22:04 +0100357}
358
359DEFUN(show_esme, show_esme_cmd,
360 "show smpp esme",
361 SHOW_STR "SMPP Interface\n" "SMPP Extrenal SMS Entity\n")
362{
363 struct smsc *smsc = smsc_from_vty(vty);
364 struct osmo_esme *esme;
365
366 llist_for_each_entry(esme, &smsc->esme_list, list)
367 dump_one_esme(vty, esme);
368
369 return CMD_SUCCESS;
370}
371
Harald Weltee07b6a72012-11-23 19:02:37 +0100372static void write_esme_route_single(struct vty *vty, struct osmo_smpp_route *r)
373{
374 switch (r->type) {
375 case SMPP_ROUTE_PREFIX:
376 vty_out(vty, " route prefix %s ",
377 get_value_string(smpp_ton_str_short, r->u.prefix.ton));
378 vty_out(vty, "%s %s%s",
379 get_value_string(smpp_npi_str_short, r->u.prefix.npi),
380 r->u.prefix.addr, VTY_NEWLINE);
381 break;
382 case SMPP_ROUTE_NONE:
383 break;
384 }
385}
386
Harald Welte338e3b32012-11-20 22:22:04 +0100387static void config_write_esme_single(struct vty *vty, struct osmo_smpp_acl *acl)
388{
Harald Weltee07b6a72012-11-23 19:02:37 +0100389 struct osmo_smpp_route *r;
390
Harald Welte338e3b32012-11-20 22:22:04 +0100391 vty_out(vty, " esme %s%s", acl->system_id, VTY_NEWLINE);
392 if (strlen(acl->passwd))
393 vty_out(vty, " password %s%s", acl->passwd, VTY_NEWLINE);
394 if (acl->default_route)
395 vty_out(vty, " default-route%s", VTY_NEWLINE);
Harald Weltee07b6a72012-11-23 19:02:37 +0100396 if (acl->deliver_src_imsi)
397 vty_out(vty, " deliver-src-imsi%s", VTY_NEWLINE);
398
399 llist_for_each_entry(r, &acl->route_list, list)
400 write_esme_route_single(vty, r);
Harald Welte338e3b32012-11-20 22:22:04 +0100401}
402
403static int config_write_esme(struct vty *v)
404{
405 struct smsc *smsc = smsc_from_vty(v);
406 struct osmo_smpp_acl *acl;
407
408 llist_for_each_entry(acl, &smsc->acl_list, list)
409 config_write_esme_single(v, acl);
410
411 return CMD_SUCCESS;
412}
413
414int smpp_vty_init(void)
415{
416 install_node(&smpp_node, config_write_smpp);
417 install_default(SMPP_NODE);
418 install_element(CONFIG_NODE, &cfg_smpp_cmd);
419
420 install_element(SMPP_NODE, &cfg_smpp_port_cmd);
421 install_element(SMPP_NODE, &cfg_smpp_sys_id_cmd);
422 install_element(SMPP_NODE, &cfg_smpp_policy_cmd);
423 install_element(SMPP_NODE, &cfg_esme_cmd);
424 install_element(SMPP_NODE, &cfg_no_esme_cmd);
425
426 install_node(&esme_node, config_write_esme);
427 install_default(SMPP_ESME_NODE);
428 install_element(SMPP_ESME_NODE, &cfg_esme_passwd_cmd);
429 install_element(SMPP_ESME_NODE, &cfg_esme_no_passwd_cmd);
Harald Weltee07b6a72012-11-23 19:02:37 +0100430 install_element(SMPP_ESME_NODE, &cfg_esme_route_pfx_cmd);
431 install_element(SMPP_ESME_NODE, &cfg_esme_no_route_pfx_cmd);
Harald Welte338e3b32012-11-20 22:22:04 +0100432 install_element(SMPP_ESME_NODE, &cfg_esme_defaultroute_cmd);
433 install_element(SMPP_ESME_NODE, &cfg_esme_no_defaultroute_cmd);
434 install_element(SMPP_ESME_NODE, &ournode_exit_cmd);
435
436 install_element_ve(&show_esme_cmd);
437
438 return 0;
439}