blob: cba8a7a3fdd41ff7a3a285841b747286f082cad9 [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>
Harald Welte923a3bd2009-02-14 12:51:36 +000031
Pablo Neira Ayuso136f4532011-03-22 16:47:59 +010032#include <osmocom/core/select.h>
33#include <osmocom/core/timer.h>
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +010034#include <osmocom/core/linuxlist.h>
Harald Welte4a88a492014-08-20 23:46:40 +020035#include <osmocom/gsm/protocol/ipaccess.h>
36#include <osmocom/gsm/ipa.h>
Neels Hofmeyrc0164792017-09-04 15:15:32 +020037#include <osmocom/bsc/gsm_data.h>
Harald Welte37881962009-04-30 15:15:37 +000038
Neels Hofmeyr2a9ac192017-12-25 18:43:40 +010039static struct {
40 const char *ifname;
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +010041 bool list_view;
Neels Hofmeyr2a9ac192017-12-25 18:43:40 +010042} cmdline_opts = {
43 .ifname = NULL,
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +010044 .list_view = false,
Neels Hofmeyr2a9ac192017-12-25 18:43:40 +010045};
46
47static void print_help()
48{
49 printf("\n");
50 printf("Usage: abisip-find [-l] [<interface-name>]\n");
51 printf(" <interface-name> Specify the outgoing network interface,\n"
52 " e.g. 'eth0'\n");
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +010053 printf(" -l --list-view Instead of printing received responses,\n"
54 " output a sorted list of currently present\n"
55 " base stations and change events.\n");
Neels Hofmeyr2a9ac192017-12-25 18:43:40 +010056}
57
58static void handle_options(int argc, char **argv)
59{
60 while (1) {
61 int option_index = 0, c;
62 static struct option long_options[] = {
63 {"help", 0, 0, 'h'},
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +010064 {"list-view", 0, 0, 'l'},
Neels Hofmeyr2a9ac192017-12-25 18:43:40 +010065 {0, 0, 0, 0}
66 };
67
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +010068 c = getopt_long(argc, argv, "hl",
Neels Hofmeyr2a9ac192017-12-25 18:43:40 +010069 long_options, &option_index);
70 if (c == -1)
71 break;
72
73 switch (c) {
74 case 'h':
75 print_help();
76 exit(EXIT_SUCCESS);
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +010077 case 'l':
78 cmdline_opts.list_view = true;
79 break;
Neels Hofmeyr2a9ac192017-12-25 18:43:40 +010080 default:
81 /* catch unknown options *as well as* missing arguments. */
82 fprintf(stderr, "Error in command line options. Exiting. Try --help.\n");
83 exit(EXIT_FAILURE);
84 break;
85 }
86 }
87
88 if (argc - optind > 0)
89 cmdline_opts.ifname = argv[optind++];
90
91 if (argc - optind > 0) {
92 fprintf(stderr, "Error: too many arguments\n");
93 print_help();
94 exit(EXIT_FAILURE);
95 }
96}
97
Harald Weltee26d0792009-08-08 11:47:20 +020098static int udp_sock(const char *ifname)
Harald Welte923a3bd2009-02-14 12:51:36 +000099{
100 int fd, rc, bc = 1;
101 struct sockaddr_in sa;
102
103 fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
104 if (fd < 0)
105 return fd;
106
Harald Weltee26d0792009-08-08 11:47:20 +0200107 if (ifname) {
Nikola Kolev10bad102014-05-08 12:45:20 +0300108#ifdef __FreeBSD__
109 rc = setsockopt(fd, SOL_SOCKET, IP_RECVIF, ifname,
110 strlen(ifname));
111#else
Harald Weltee26d0792009-08-08 11:47:20 +0200112 rc = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname,
113 strlen(ifname));
Nikola Kolev10bad102014-05-08 12:45:20 +0300114#endif
Harald Weltee26d0792009-08-08 11:47:20 +0200115 if (rc < 0)
116 goto err;
117 }
118
Holger Hans Peter Freytherd5c270e2013-07-03 10:19:37 +0200119 memset(&sa, 0, sizeof(sa));
Harald Welte923a3bd2009-02-14 12:51:36 +0000120 sa.sin_family = AF_INET;
121 sa.sin_port = htons(3006);
122 sa.sin_addr.s_addr = INADDR_ANY;
Harald Welte923a3bd2009-02-14 12:51:36 +0000123
124 rc = bind(fd, (struct sockaddr *)&sa, sizeof(sa));
125 if (rc < 0)
126 goto err;
127
128 rc = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &bc, sizeof(bc));
129 if (rc < 0)
130 goto err;
131
132#if 0
Harald Welte81cff3c2009-08-08 12:14:53 +0200133 /* we cannot bind, since the response packets don't come from
134 * the broadcast address */
135 sa.sin_family = AF_INET;
136 sa.sin_port = htons(3006);
137 inet_aton("255.255.255.255", &sa.sin_addr);
138
Harald Welte923a3bd2009-02-14 12:51:36 +0000139 rc = connect(fd, (struct sockaddr *)&sa, sizeof(sa));
140 if (rc < 0)
141 goto err;
Holger Hans Peter Freytheracf8a0c2010-03-29 08:47:44 +0200142#endif
Harald Welte923a3bd2009-02-14 12:51:36 +0000143 return fd;
144
145err:
146 close(fd);
147 return rc;
148}
149
Holger Hans Peter Freytheracf8a0c2010-03-29 08:47:44 +0200150const unsigned char find_pkt[] = { 0x00, 0x0b+8, IPAC_PROTO_IPACCESS, 0x00,
Harald Welte4f361fc2009-02-15 15:32:53 +0000151 IPAC_MSGT_ID_GET,
152 0x01, IPAC_IDTAG_MACADDR,
153 0x01, IPAC_IDTAG_IPADDR,
154 0x01, IPAC_IDTAG_UNIT,
155 0x01, IPAC_IDTAG_LOCATION1,
156 0x01, IPAC_IDTAG_LOCATION2,
157 0x01, IPAC_IDTAG_EQUIPVERS,
158 0x01, IPAC_IDTAG_SWVERSION,
159 0x01, IPAC_IDTAG_UNITNAME,
160 0x01, IPAC_IDTAG_SERNR,
161 };
Harald Welte923a3bd2009-02-14 12:51:36 +0000162
163
164static int bcast_find(int fd)
165{
166 struct sockaddr_in sa;
167
168 sa.sin_family = AF_INET;
169 sa.sin_port = htons(3006);
170 inet_aton("255.255.255.255", &sa.sin_addr);
171
172 return sendto(fd, find_pkt, sizeof(find_pkt), 0, (struct sockaddr *) &sa, sizeof(sa));
173}
174
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +0100175static char *parse_response(void *ctx, unsigned char *buf, int len)
Harald Welte923a3bd2009-02-14 12:51:36 +0000176{
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200177 uint8_t t_len;
178 uint8_t t_tag;
179 uint8_t *cur = buf;
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +0100180 char *out = talloc_zero_size(ctx, 512);
Harald Welte923a3bd2009-02-14 12:51:36 +0000181
Harald Welte923a3bd2009-02-14 12:51:36 +0000182 while (cur < buf + len) {
183 t_len = *cur++;
184 t_tag = *cur++;
185
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +0100186 out = talloc_asprintf_append(out, "%s='%s' ", ipa_ccm_idtag_name(t_tag), cur);
Harald Welte923a3bd2009-02-14 12:51:36 +0000187
188 cur += t_len;
189 }
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +0100190
191 return out;
192}
193
194struct base_station {
195 struct llist_head entry;
196 char *line;
197 time_t timestamp;
198};
199
200LLIST_HEAD(base_stations);
201
202void *ctx = NULL;
203
204void print_timestamp()
205{
206 time_t now = time(NULL);
207 printf("\n\n----- %s\n", ctime(&now));
208}
209
210struct base_station *base_station_parse(unsigned char *buf, int len)
211{
212 struct base_station *new_bs = talloc_zero(ctx, struct base_station);
213 new_bs->line = parse_response(new_bs, buf, len);
214 new_bs->timestamp = time(NULL);
215 return new_bs;
216}
217
218bool base_stations_add(struct base_station *new_bs)
219{
220 struct base_station *bs;
221
222 llist_for_each_entry(bs, &base_stations, entry) {
223 int c = strcmp(new_bs->line, bs->line);
224 if (!c) {
225 /* entry already exists. */
226 bs->timestamp = new_bs->timestamp;
227 return false;
228 }
229
230 if (c < 0) {
231 /* found the place to add the entry */
232 break;
233 }
234 }
235
236 print_timestamp();
237 printf("New:\n%s\n", new_bs->line);
238
239 llist_add_tail(&new_bs->entry, &bs->entry);
240 return true;
241}
242
243bool base_stations_timeout()
244{
245 struct base_station *bs, *next_bs;
246 time_t now = time(NULL);
247 bool changed = false;
248
249 llist_for_each_entry_safe(bs, next_bs, &base_stations, entry) {
250 if (now - bs->timestamp < 10)
251 continue;
252 print_timestamp();
253 printf("LOST:\n%s\n", bs->line);
254
255 llist_del(&bs->entry);
256 talloc_free(bs);
257 changed = true;
258 }
259 return changed;
260}
261
262void base_stations_print()
263{
264 struct base_station *bs;
265 int count = 0;
266
267 print_timestamp();
268 llist_for_each_entry(bs, &base_stations, entry) {
269 printf("%3d: %s\n", count, bs->line);
270 count++;
271 }
272 printf("\nTotal: %d\n", count);
273}
274
275static void base_stations_bump(bool known_changed)
276{
277 bool changed = known_changed;
278 if (base_stations_timeout())
279 changed = true;
280
281 if (changed)
282 base_stations_print();
283}
284
285static void handle_response(unsigned char *buf, int len)
286{
287 static unsigned int responses = 0;
288 responses++;
289
290 if (cmdline_opts.list_view) {
291 bool changed = false;
292 struct base_station *bs = base_station_parse(buf, len);
293 if (base_stations_add(bs))
294 changed = true;
295 else
296 talloc_free(bs);
297 base_stations_bump(changed);
298 printf("RX: %u \r", responses);
299 fflush(stdout);
300 } else {
301 char *line = parse_response(ctx, buf, len);
302 printf(line);
303 printf("\n");
304 talloc_free(line);
305 }
Harald Welte923a3bd2009-02-14 12:51:36 +0000306}
307
308static int read_response(int fd)
309{
310 unsigned char buf[255];
311 struct sockaddr_in sa;
312 int len;
313 socklen_t sa_len = sizeof(sa);
314
315 len = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sa, &sa_len);
316 if (len < 0)
317 return len;
318
Maxc04c6ed2016-11-10 18:29:50 +0100319 /* 2 bytes length, 1 byte protocol */
320 if (buf[2] != IPAC_PROTO_IPACCESS)
Harald Welte81cff3c2009-08-08 12:14:53 +0200321 return 0;
322
323 if (buf[4] != IPAC_MSGT_ID_RESP)
324 return 0;
325
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +0100326 handle_response(buf+6, len-6);
327 return 0;
Harald Welte923a3bd2009-02-14 12:51:36 +0000328}
329
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200330static int bfd_cb(struct osmo_fd *bfd, unsigned int flags)
Harald Welte923a3bd2009-02-14 12:51:36 +0000331{
332 if (flags & BSC_FD_READ)
333 return read_response(bfd->fd);
334 if (flags & BSC_FD_WRITE) {
335 bfd->when &= ~BSC_FD_WRITE;
336 return bcast_find(bfd->fd);
337 }
338 return 0;
339}
340
Pablo Neira Ayusobf540cb2011-05-06 12:11:06 +0200341static struct osmo_timer_list timer;
Harald Welte923a3bd2009-02-14 12:51:36 +0000342
343static void timer_cb(void *_data)
344{
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200345 struct osmo_fd *bfd = _data;
Harald Welte923a3bd2009-02-14 12:51:36 +0000346
347 bfd->when |= BSC_FD_WRITE;
348
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +0100349 base_stations_bump(false);
350
Pablo Neira Ayusobf540cb2011-05-06 12:11:06 +0200351 osmo_timer_schedule(&timer, 5, 0);
Harald Welte923a3bd2009-02-14 12:51:36 +0000352}
353
354int main(int argc, char **argv)
355{
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200356 struct osmo_fd bfd;
Harald Welte923a3bd2009-02-14 12:51:36 +0000357 int rc;
358
Neels Hofmeyr89ade632017-12-25 19:13:13 +0100359 printf("abisip-find (C) 2009-2010 by Harald Welte\n");
360 printf(" (C) 2017 by sysmocom - s.f.m.c. GmbH\n");
Harald Welte923a3bd2009-02-14 12:51:36 +0000361 printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n");
362
Neels Hofmeyr2a9ac192017-12-25 18:43:40 +0100363 handle_options(argc, argv);
364
365 if (!cmdline_opts.ifname)
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +0100366 fprintf(stdout, "- You might need to specify the outgoing\n"
367 " network interface, e.g. ``%s eth0''\n", argv[0]);
368 if (!cmdline_opts.list_view)
369 fprintf(stdout, "- You may find the --list-view option convenient.\n");
Harald Welte042401c2009-06-29 10:43:04 +0200370
Harald Welte923a3bd2009-02-14 12:51:36 +0000371 bfd.cb = bfd_cb;
372 bfd.when = BSC_FD_READ | BSC_FD_WRITE;
Neels Hofmeyr2a9ac192017-12-25 18:43:40 +0100373 bfd.fd = udp_sock(cmdline_opts.ifname);
Harald Welte042401c2009-06-29 10:43:04 +0200374 if (bfd.fd < 0) {
375 perror("Cannot create local socket for broadcast udp");
376 exit(1);
377 }
Harald Welte923a3bd2009-02-14 12:51:36 +0000378
Harald Welte8d359652016-11-26 14:57:23 +0100379 rc = osmo_fd_register(&bfd);
380 if (rc < 0) {
381 fprintf(stderr, "Cannot register FD\n");
382 exit(1);
383 }
Harald Welte923a3bd2009-02-14 12:51:36 +0000384
Pablo Neira Ayuso51215762017-05-08 20:57:52 +0200385 osmo_timer_setup(&timer, timer_cb, &bfd);
Pablo Neira Ayusobf540cb2011-05-06 12:11:06 +0200386 osmo_timer_schedule(&timer, 5, 0);
Harald Welte923a3bd2009-02-14 12:51:36 +0000387
388 printf("Trying to find ip.access BTS by broadcast UDP...\n");
389
390 while (1) {
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200391 rc = osmo_select_main(0);
Harald Welte923a3bd2009-02-14 12:51:36 +0000392 if (rc < 0)
393 exit(3);
394 }
395
396 exit(0);
397}
398