blob: d94e18af182885e9928247234798e5372e0000bf [file] [log] [blame]
Harald Welte9af6ddf2011-01-01 15:25:50 +01001/* ip.access nanoBTS configuration tool */
2
3/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
Neels Hofmeyr89ade632017-12-25 19:13:13 +01004 * (C) 2017 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
Harald Welte9af6ddf2011-01-01 15:25:50 +01005 * All Rights Reserved
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
Harald Welte923a3bd2009-02-14 12:51:36 +000021
22#include <unistd.h>
23#include <stdio.h>
24#include <stdlib.h>
Harald Welte923a3bd2009-02-14 12:51:36 +000025#include <sys/socket.h>
26#include <netinet/in.h>
27#include <arpa/inet.h>
Neels Hofmeyr2a9ac192017-12-25 18:43:40 +010028#include <getopt.h>
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +010029#include <time.h>
30#include <talloc.h>
Neels Hofmeyr77a9d4e2017-12-25 19:10:10 +010031#include <errno.h>
Harald Welte923a3bd2009-02-14 12:51:36 +000032
Pablo Neira Ayuso136f4532011-03-22 16:47:59 +010033#include <osmocom/core/select.h>
34#include <osmocom/core/timer.h>
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +010035#include <osmocom/core/linuxlist.h>
Harald Welte4a88a492014-08-20 23:46:40 +020036#include <osmocom/gsm/protocol/ipaccess.h>
37#include <osmocom/gsm/ipa.h>
Neels Hofmeyrc0164792017-09-04 15:15:32 +020038#include <osmocom/bsc/gsm_data.h>
Harald Welte37881962009-04-30 15:15:37 +000039
Neels Hofmeyr2a9ac192017-12-25 18:43:40 +010040static struct {
41 const char *ifname;
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +010042 bool list_view;
Neels Hofmeyr77a9d4e2017-12-25 19:10:10 +010043 time_t list_view_timeout;
Neels Hofmeyr2a9ac192017-12-25 18:43:40 +010044} cmdline_opts = {
45 .ifname = NULL,
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +010046 .list_view = false,
Neels Hofmeyr77a9d4e2017-12-25 19:10:10 +010047 .list_view_timeout = 10,
Neels Hofmeyr2a9ac192017-12-25 18:43:40 +010048};
49
50static void print_help()
51{
52 printf("\n");
53 printf("Usage: abisip-find [-l] [<interface-name>]\n");
54 printf(" <interface-name> Specify the outgoing network interface,\n"
55 " e.g. 'eth0'\n");
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +010056 printf(" -l --list-view Instead of printing received responses,\n"
57 " output a sorted list of currently present\n"
58 " base stations and change events.\n");
Neels Hofmeyr77a9d4e2017-12-25 19:10:10 +010059 printf(" -t --timeout <s> Drop base stations after <s> seconds of\n"
60 " receiving no more replies from it.\n"
61 " Implies --list-view.\n");
Neels Hofmeyr2a9ac192017-12-25 18:43:40 +010062}
63
64static void handle_options(int argc, char **argv)
65{
66 while (1) {
67 int option_index = 0, c;
68 static struct option long_options[] = {
69 {"help", 0, 0, 'h'},
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +010070 {"list-view", 0, 0, 'l'},
Neels Hofmeyr77a9d4e2017-12-25 19:10:10 +010071 {"timeout", 1, 0, 't'},
Neels Hofmeyr2a9ac192017-12-25 18:43:40 +010072 {0, 0, 0, 0}
73 };
74
Neels Hofmeyr77a9d4e2017-12-25 19:10:10 +010075 c = getopt_long(argc, argv, "hlt:",
Neels Hofmeyr2a9ac192017-12-25 18:43:40 +010076 long_options, &option_index);
77 if (c == -1)
78 break;
79
80 switch (c) {
81 case 'h':
82 print_help();
83 exit(EXIT_SUCCESS);
Neels Hofmeyr77a9d4e2017-12-25 19:10:10 +010084 case 't':
85 errno = 0;
86 cmdline_opts.list_view_timeout = strtoul(optarg, NULL, 10);
87 if (errno) {
88 fprintf(stderr, "Invalid timeout value: %s\n", optarg);
89 exit(EXIT_FAILURE);
90 }
91 /* fall through to imply list-view: */
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +010092 case 'l':
93 cmdline_opts.list_view = true;
94 break;
Neels Hofmeyr2a9ac192017-12-25 18:43:40 +010095 default:
96 /* catch unknown options *as well as* missing arguments. */
97 fprintf(stderr, "Error in command line options. Exiting. Try --help.\n");
98 exit(EXIT_FAILURE);
99 break;
100 }
101 }
102
103 if (argc - optind > 0)
104 cmdline_opts.ifname = argv[optind++];
105
106 if (argc - optind > 0) {
107 fprintf(stderr, "Error: too many arguments\n");
108 print_help();
109 exit(EXIT_FAILURE);
110 }
111}
112
Harald Weltee26d0792009-08-08 11:47:20 +0200113static int udp_sock(const char *ifname)
Harald Welte923a3bd2009-02-14 12:51:36 +0000114{
115 int fd, rc, bc = 1;
116 struct sockaddr_in sa;
117
118 fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
119 if (fd < 0)
120 return fd;
121
Harald Weltee26d0792009-08-08 11:47:20 +0200122 if (ifname) {
Nikola Kolev10bad102014-05-08 12:45:20 +0300123#ifdef __FreeBSD__
124 rc = setsockopt(fd, SOL_SOCKET, IP_RECVIF, ifname,
125 strlen(ifname));
126#else
Harald Weltee26d0792009-08-08 11:47:20 +0200127 rc = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname,
128 strlen(ifname));
Nikola Kolev10bad102014-05-08 12:45:20 +0300129#endif
Harald Weltee26d0792009-08-08 11:47:20 +0200130 if (rc < 0)
131 goto err;
132 }
133
Holger Hans Peter Freytherd5c270e2013-07-03 10:19:37 +0200134 memset(&sa, 0, sizeof(sa));
Harald Welte923a3bd2009-02-14 12:51:36 +0000135 sa.sin_family = AF_INET;
136 sa.sin_port = htons(3006);
137 sa.sin_addr.s_addr = INADDR_ANY;
Harald Welte923a3bd2009-02-14 12:51:36 +0000138
139 rc = bind(fd, (struct sockaddr *)&sa, sizeof(sa));
140 if (rc < 0)
141 goto err;
142
143 rc = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &bc, sizeof(bc));
144 if (rc < 0)
145 goto err;
146
147#if 0
Harald Welte81cff3c2009-08-08 12:14:53 +0200148 /* we cannot bind, since the response packets don't come from
149 * the broadcast address */
150 sa.sin_family = AF_INET;
151 sa.sin_port = htons(3006);
152 inet_aton("255.255.255.255", &sa.sin_addr);
153
Harald Welte923a3bd2009-02-14 12:51:36 +0000154 rc = connect(fd, (struct sockaddr *)&sa, sizeof(sa));
155 if (rc < 0)
156 goto err;
Holger Hans Peter Freytheracf8a0c2010-03-29 08:47:44 +0200157#endif
Harald Welte923a3bd2009-02-14 12:51:36 +0000158 return fd;
159
160err:
161 close(fd);
162 return rc;
163}
164
Holger Hans Peter Freytheracf8a0c2010-03-29 08:47:44 +0200165const unsigned char find_pkt[] = { 0x00, 0x0b+8, IPAC_PROTO_IPACCESS, 0x00,
Harald Welte4f361fc2009-02-15 15:32:53 +0000166 IPAC_MSGT_ID_GET,
167 0x01, IPAC_IDTAG_MACADDR,
168 0x01, IPAC_IDTAG_IPADDR,
169 0x01, IPAC_IDTAG_UNIT,
170 0x01, IPAC_IDTAG_LOCATION1,
171 0x01, IPAC_IDTAG_LOCATION2,
172 0x01, IPAC_IDTAG_EQUIPVERS,
173 0x01, IPAC_IDTAG_SWVERSION,
174 0x01, IPAC_IDTAG_UNITNAME,
175 0x01, IPAC_IDTAG_SERNR,
176 };
Harald Welte923a3bd2009-02-14 12:51:36 +0000177
178
179static int bcast_find(int fd)
180{
181 struct sockaddr_in sa;
182
183 sa.sin_family = AF_INET;
184 sa.sin_port = htons(3006);
185 inet_aton("255.255.255.255", &sa.sin_addr);
186
187 return sendto(fd, find_pkt, sizeof(find_pkt), 0, (struct sockaddr *) &sa, sizeof(sa));
188}
189
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +0100190static char *parse_response(void *ctx, unsigned char *buf, int len)
Harald Welte923a3bd2009-02-14 12:51:36 +0000191{
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200192 uint8_t t_len;
193 uint8_t t_tag;
194 uint8_t *cur = buf;
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +0100195 char *out = talloc_zero_size(ctx, 512);
Harald Welte923a3bd2009-02-14 12:51:36 +0000196
Harald Welte923a3bd2009-02-14 12:51:36 +0000197 while (cur < buf + len) {
198 t_len = *cur++;
199 t_tag = *cur++;
200
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +0100201 out = talloc_asprintf_append(out, "%s='%s' ", ipa_ccm_idtag_name(t_tag), cur);
Harald Welte923a3bd2009-02-14 12:51:36 +0000202
203 cur += t_len;
204 }
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +0100205
206 return out;
207}
208
209struct base_station {
210 struct llist_head entry;
211 char *line;
212 time_t timestamp;
213};
214
215LLIST_HEAD(base_stations);
216
217void *ctx = NULL;
218
219void print_timestamp()
220{
221 time_t now = time(NULL);
222 printf("\n\n----- %s\n", ctime(&now));
223}
224
225struct base_station *base_station_parse(unsigned char *buf, int len)
226{
227 struct base_station *new_bs = talloc_zero(ctx, struct base_station);
228 new_bs->line = parse_response(new_bs, buf, len);
229 new_bs->timestamp = time(NULL);
230 return new_bs;
231}
232
233bool base_stations_add(struct base_station *new_bs)
234{
235 struct base_station *bs;
236
237 llist_for_each_entry(bs, &base_stations, entry) {
238 int c = strcmp(new_bs->line, bs->line);
239 if (!c) {
240 /* entry already exists. */
241 bs->timestamp = new_bs->timestamp;
242 return false;
243 }
244
245 if (c < 0) {
246 /* found the place to add the entry */
247 break;
248 }
249 }
250
251 print_timestamp();
252 printf("New:\n%s\n", new_bs->line);
253
254 llist_add_tail(&new_bs->entry, &bs->entry);
255 return true;
256}
257
258bool base_stations_timeout()
259{
260 struct base_station *bs, *next_bs;
261 time_t now = time(NULL);
262 bool changed = false;
263
264 llist_for_each_entry_safe(bs, next_bs, &base_stations, entry) {
Neels Hofmeyr77a9d4e2017-12-25 19:10:10 +0100265 if (now - bs->timestamp < cmdline_opts.list_view_timeout)
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +0100266 continue;
267 print_timestamp();
268 printf("LOST:\n%s\n", bs->line);
269
270 llist_del(&bs->entry);
271 talloc_free(bs);
272 changed = true;
273 }
274 return changed;
275}
276
277void base_stations_print()
278{
279 struct base_station *bs;
280 int count = 0;
281
282 print_timestamp();
283 llist_for_each_entry(bs, &base_stations, entry) {
284 printf("%3d: %s\n", count, bs->line);
285 count++;
286 }
287 printf("\nTotal: %d\n", count);
288}
289
290static void base_stations_bump(bool known_changed)
291{
292 bool changed = known_changed;
293 if (base_stations_timeout())
294 changed = true;
295
296 if (changed)
297 base_stations_print();
298}
299
300static void handle_response(unsigned char *buf, int len)
301{
302 static unsigned int responses = 0;
303 responses++;
304
305 if (cmdline_opts.list_view) {
306 bool changed = false;
307 struct base_station *bs = base_station_parse(buf, len);
308 if (base_stations_add(bs))
309 changed = true;
310 else
311 talloc_free(bs);
312 base_stations_bump(changed);
313 printf("RX: %u \r", responses);
314 fflush(stdout);
315 } else {
316 char *line = parse_response(ctx, buf, len);
317 printf(line);
318 printf("\n");
319 talloc_free(line);
320 }
Harald Welte923a3bd2009-02-14 12:51:36 +0000321}
322
323static int read_response(int fd)
324{
325 unsigned char buf[255];
326 struct sockaddr_in sa;
327 int len;
328 socklen_t sa_len = sizeof(sa);
329
330 len = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sa, &sa_len);
331 if (len < 0)
332 return len;
333
Maxc04c6ed2016-11-10 18:29:50 +0100334 /* 2 bytes length, 1 byte protocol */
335 if (buf[2] != IPAC_PROTO_IPACCESS)
Harald Welte81cff3c2009-08-08 12:14:53 +0200336 return 0;
337
338 if (buf[4] != IPAC_MSGT_ID_RESP)
339 return 0;
340
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +0100341 handle_response(buf+6, len-6);
342 return 0;
Harald Welte923a3bd2009-02-14 12:51:36 +0000343}
344
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200345static int bfd_cb(struct osmo_fd *bfd, unsigned int flags)
Harald Welte923a3bd2009-02-14 12:51:36 +0000346{
347 if (flags & BSC_FD_READ)
348 return read_response(bfd->fd);
349 if (flags & BSC_FD_WRITE) {
350 bfd->when &= ~BSC_FD_WRITE;
351 return bcast_find(bfd->fd);
352 }
353 return 0;
354}
355
Pablo Neira Ayusobf540cb2011-05-06 12:11:06 +0200356static struct osmo_timer_list timer;
Harald Welte923a3bd2009-02-14 12:51:36 +0000357
358static void timer_cb(void *_data)
359{
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200360 struct osmo_fd *bfd = _data;
Harald Welte923a3bd2009-02-14 12:51:36 +0000361
362 bfd->when |= BSC_FD_WRITE;
363
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +0100364 base_stations_bump(false);
365
Pablo Neira Ayusobf540cb2011-05-06 12:11:06 +0200366 osmo_timer_schedule(&timer, 5, 0);
Harald Welte923a3bd2009-02-14 12:51:36 +0000367}
368
369int main(int argc, char **argv)
370{
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200371 struct osmo_fd bfd;
Harald Welte923a3bd2009-02-14 12:51:36 +0000372 int rc;
373
Neels Hofmeyr89ade632017-12-25 19:13:13 +0100374 printf("abisip-find (C) 2009-2010 by Harald Welte\n");
375 printf(" (C) 2017 by sysmocom - s.f.m.c. GmbH\n");
Harald Welte923a3bd2009-02-14 12:51:36 +0000376 printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n");
377
Neels Hofmeyr2a9ac192017-12-25 18:43:40 +0100378 handle_options(argc, argv);
379
380 if (!cmdline_opts.ifname)
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +0100381 fprintf(stdout, "- You might need to specify the outgoing\n"
382 " network interface, e.g. ``%s eth0''\n", argv[0]);
383 if (!cmdline_opts.list_view)
384 fprintf(stdout, "- You may find the --list-view option convenient.\n");
Harald Welte042401c2009-06-29 10:43:04 +0200385
Harald Welte923a3bd2009-02-14 12:51:36 +0000386 bfd.cb = bfd_cb;
387 bfd.when = BSC_FD_READ | BSC_FD_WRITE;
Neels Hofmeyr2a9ac192017-12-25 18:43:40 +0100388 bfd.fd = udp_sock(cmdline_opts.ifname);
Harald Welte042401c2009-06-29 10:43:04 +0200389 if (bfd.fd < 0) {
390 perror("Cannot create local socket for broadcast udp");
391 exit(1);
392 }
Harald Welte923a3bd2009-02-14 12:51:36 +0000393
Harald Welte8d359652016-11-26 14:57:23 +0100394 rc = osmo_fd_register(&bfd);
395 if (rc < 0) {
396 fprintf(stderr, "Cannot register FD\n");
397 exit(1);
398 }
Harald Welte923a3bd2009-02-14 12:51:36 +0000399
Pablo Neira Ayuso51215762017-05-08 20:57:52 +0200400 osmo_timer_setup(&timer, timer_cb, &bfd);
Pablo Neira Ayusobf540cb2011-05-06 12:11:06 +0200401 osmo_timer_schedule(&timer, 5, 0);
Harald Welte923a3bd2009-02-14 12:51:36 +0000402
403 printf("Trying to find ip.access BTS by broadcast UDP...\n");
404
405 while (1) {
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200406 rc = osmo_select_main(0);
Harald Welte923a3bd2009-02-14 12:51:36 +0000407 if (rc < 0)
408 exit(3);
409 }
410
411 exit(0);
412}
413