blob: 2f8d80f16948820d659e9c97d6fd49789995a0c8 [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>
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 */
Harald Welte923a3bd2009-02-14 12:51:36 +000020
21#include <unistd.h>
22#include <stdio.h>
23#include <stdlib.h>
Harald Welte923a3bd2009-02-14 12:51:36 +000024#include <sys/socket.h>
25#include <netinet/in.h>
26#include <arpa/inet.h>
Neels Hofmeyr2a9ac192017-12-25 18:43:40 +010027#include <getopt.h>
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +010028#include <time.h>
29#include <talloc.h>
Harald Welte923a3bd2009-02-14 12:51:36 +000030
Pablo Neira Ayuso136f4532011-03-22 16:47:59 +010031#include <osmocom/core/select.h>
32#include <osmocom/core/timer.h>
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +010033#include <osmocom/core/linuxlist.h>
Harald Welte4a88a492014-08-20 23:46:40 +020034#include <osmocom/gsm/protocol/ipaccess.h>
35#include <osmocom/gsm/ipa.h>
Neels Hofmeyrc0164792017-09-04 15:15:32 +020036#include <osmocom/bsc/gsm_data.h>
Harald Welte37881962009-04-30 15:15:37 +000037
Neels Hofmeyr2a9ac192017-12-25 18:43:40 +010038static struct {
39 const char *ifname;
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +010040 bool list_view;
Neels Hofmeyr2a9ac192017-12-25 18:43:40 +010041} cmdline_opts = {
42 .ifname = NULL,
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +010043 .list_view = false,
Neels Hofmeyr2a9ac192017-12-25 18:43:40 +010044};
45
46static void print_help()
47{
48 printf("\n");
49 printf("Usage: abisip-find [-l] [<interface-name>]\n");
50 printf(" <interface-name> Specify the outgoing network interface,\n"
51 " e.g. 'eth0'\n");
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +010052 printf(" -l --list-view Instead of printing received responses,\n"
53 " output a sorted list of currently present\n"
54 " base stations and change events.\n");
Neels Hofmeyr2a9ac192017-12-25 18:43:40 +010055}
56
57static void handle_options(int argc, char **argv)
58{
59 while (1) {
60 int option_index = 0, c;
61 static struct option long_options[] = {
62 {"help", 0, 0, 'h'},
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +010063 {"list-view", 0, 0, 'l'},
Neels Hofmeyr2a9ac192017-12-25 18:43:40 +010064 {0, 0, 0, 0}
65 };
66
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +010067 c = getopt_long(argc, argv, "hl",
Neels Hofmeyr2a9ac192017-12-25 18:43:40 +010068 long_options, &option_index);
69 if (c == -1)
70 break;
71
72 switch (c) {
73 case 'h':
74 print_help();
75 exit(EXIT_SUCCESS);
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +010076 case 'l':
77 cmdline_opts.list_view = true;
78 break;
Neels Hofmeyr2a9ac192017-12-25 18:43:40 +010079 default:
80 /* catch unknown options *as well as* missing arguments. */
81 fprintf(stderr, "Error in command line options. Exiting. Try --help.\n");
82 exit(EXIT_FAILURE);
83 break;
84 }
85 }
86
87 if (argc - optind > 0)
88 cmdline_opts.ifname = argv[optind++];
89
90 if (argc - optind > 0) {
91 fprintf(stderr, "Error: too many arguments\n");
92 print_help();
93 exit(EXIT_FAILURE);
94 }
95}
96
Harald Weltee26d0792009-08-08 11:47:20 +020097static int udp_sock(const char *ifname)
Harald Welte923a3bd2009-02-14 12:51:36 +000098{
99 int fd, rc, bc = 1;
100 struct sockaddr_in sa;
101
102 fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
103 if (fd < 0)
104 return fd;
105
Harald Weltee26d0792009-08-08 11:47:20 +0200106 if (ifname) {
Nikola Kolev10bad102014-05-08 12:45:20 +0300107#ifdef __FreeBSD__
108 rc = setsockopt(fd, SOL_SOCKET, IP_RECVIF, ifname,
109 strlen(ifname));
110#else
Harald Weltee26d0792009-08-08 11:47:20 +0200111 rc = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname,
112 strlen(ifname));
Nikola Kolev10bad102014-05-08 12:45:20 +0300113#endif
Harald Weltee26d0792009-08-08 11:47:20 +0200114 if (rc < 0)
115 goto err;
116 }
117
Holger Hans Peter Freytherd5c270e2013-07-03 10:19:37 +0200118 memset(&sa, 0, sizeof(sa));
Harald Welte923a3bd2009-02-14 12:51:36 +0000119 sa.sin_family = AF_INET;
120 sa.sin_port = htons(3006);
121 sa.sin_addr.s_addr = INADDR_ANY;
Harald Welte923a3bd2009-02-14 12:51:36 +0000122
123 rc = bind(fd, (struct sockaddr *)&sa, sizeof(sa));
124 if (rc < 0)
125 goto err;
126
127 rc = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &bc, sizeof(bc));
128 if (rc < 0)
129 goto err;
130
131#if 0
Harald Welte81cff3c2009-08-08 12:14:53 +0200132 /* we cannot bind, since the response packets don't come from
133 * the broadcast address */
134 sa.sin_family = AF_INET;
135 sa.sin_port = htons(3006);
136 inet_aton("255.255.255.255", &sa.sin_addr);
137
Harald Welte923a3bd2009-02-14 12:51:36 +0000138 rc = connect(fd, (struct sockaddr *)&sa, sizeof(sa));
139 if (rc < 0)
140 goto err;
Holger Hans Peter Freytheracf8a0c2010-03-29 08:47:44 +0200141#endif
Harald Welte923a3bd2009-02-14 12:51:36 +0000142 return fd;
143
144err:
145 close(fd);
146 return rc;
147}
148
Holger Hans Peter Freytheracf8a0c2010-03-29 08:47:44 +0200149const unsigned char find_pkt[] = { 0x00, 0x0b+8, IPAC_PROTO_IPACCESS, 0x00,
Harald Welte4f361fc2009-02-15 15:32:53 +0000150 IPAC_MSGT_ID_GET,
151 0x01, IPAC_IDTAG_MACADDR,
152 0x01, IPAC_IDTAG_IPADDR,
153 0x01, IPAC_IDTAG_UNIT,
154 0x01, IPAC_IDTAG_LOCATION1,
155 0x01, IPAC_IDTAG_LOCATION2,
156 0x01, IPAC_IDTAG_EQUIPVERS,
157 0x01, IPAC_IDTAG_SWVERSION,
158 0x01, IPAC_IDTAG_UNITNAME,
159 0x01, IPAC_IDTAG_SERNR,
160 };
Harald Welte923a3bd2009-02-14 12:51:36 +0000161
162
163static int bcast_find(int fd)
164{
165 struct sockaddr_in sa;
166
167 sa.sin_family = AF_INET;
168 sa.sin_port = htons(3006);
169 inet_aton("255.255.255.255", &sa.sin_addr);
170
171 return sendto(fd, find_pkt, sizeof(find_pkt), 0, (struct sockaddr *) &sa, sizeof(sa));
172}
173
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +0100174static char *parse_response(void *ctx, unsigned char *buf, int len)
Harald Welte923a3bd2009-02-14 12:51:36 +0000175{
Holger Hans Peter Freytherc42ad8b2011-04-18 17:04:00 +0200176 uint8_t t_len;
177 uint8_t t_tag;
178 uint8_t *cur = buf;
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +0100179 char *out = talloc_zero_size(ctx, 512);
Harald Welte923a3bd2009-02-14 12:51:36 +0000180
Harald Welte923a3bd2009-02-14 12:51:36 +0000181 while (cur < buf + len) {
182 t_len = *cur++;
183 t_tag = *cur++;
184
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +0100185 out = talloc_asprintf_append(out, "%s='%s' ", ipa_ccm_idtag_name(t_tag), cur);
Harald Welte923a3bd2009-02-14 12:51:36 +0000186
187 cur += t_len;
188 }
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +0100189
190 return out;
191}
192
193struct base_station {
194 struct llist_head entry;
195 char *line;
196 time_t timestamp;
197};
198
199LLIST_HEAD(base_stations);
200
201void *ctx = NULL;
202
203void print_timestamp()
204{
205 time_t now = time(NULL);
206 printf("\n\n----- %s\n", ctime(&now));
207}
208
209struct base_station *base_station_parse(unsigned char *buf, int len)
210{
211 struct base_station *new_bs = talloc_zero(ctx, struct base_station);
212 new_bs->line = parse_response(new_bs, buf, len);
213 new_bs->timestamp = time(NULL);
214 return new_bs;
215}
216
217bool base_stations_add(struct base_station *new_bs)
218{
219 struct base_station *bs;
220
221 llist_for_each_entry(bs, &base_stations, entry) {
222 int c = strcmp(new_bs->line, bs->line);
223 if (!c) {
224 /* entry already exists. */
225 bs->timestamp = new_bs->timestamp;
226 return false;
227 }
228
229 if (c < 0) {
230 /* found the place to add the entry */
231 break;
232 }
233 }
234
235 print_timestamp();
236 printf("New:\n%s\n", new_bs->line);
237
238 llist_add_tail(&new_bs->entry, &bs->entry);
239 return true;
240}
241
242bool base_stations_timeout()
243{
244 struct base_station *bs, *next_bs;
245 time_t now = time(NULL);
246 bool changed = false;
247
248 llist_for_each_entry_safe(bs, next_bs, &base_stations, entry) {
249 if (now - bs->timestamp < 10)
250 continue;
251 print_timestamp();
252 printf("LOST:\n%s\n", bs->line);
253
254 llist_del(&bs->entry);
255 talloc_free(bs);
256 changed = true;
257 }
258 return changed;
259}
260
261void base_stations_print()
262{
263 struct base_station *bs;
264 int count = 0;
265
266 print_timestamp();
267 llist_for_each_entry(bs, &base_stations, entry) {
268 printf("%3d: %s\n", count, bs->line);
269 count++;
270 }
271 printf("\nTotal: %d\n", count);
272}
273
274static void base_stations_bump(bool known_changed)
275{
276 bool changed = known_changed;
277 if (base_stations_timeout())
278 changed = true;
279
280 if (changed)
281 base_stations_print();
282}
283
284static void handle_response(unsigned char *buf, int len)
285{
286 static unsigned int responses = 0;
287 responses++;
288
289 if (cmdline_opts.list_view) {
290 bool changed = false;
291 struct base_station *bs = base_station_parse(buf, len);
292 if (base_stations_add(bs))
293 changed = true;
294 else
295 talloc_free(bs);
296 base_stations_bump(changed);
297 printf("RX: %u \r", responses);
298 fflush(stdout);
299 } else {
300 char *line = parse_response(ctx, buf, len);
301 printf(line);
302 printf("\n");
303 talloc_free(line);
304 }
Harald Welte923a3bd2009-02-14 12:51:36 +0000305}
306
307static int read_response(int fd)
308{
309 unsigned char buf[255];
310 struct sockaddr_in sa;
311 int len;
312 socklen_t sa_len = sizeof(sa);
313
314 len = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sa, &sa_len);
315 if (len < 0)
316 return len;
317
Maxc04c6ed2016-11-10 18:29:50 +0100318 /* 2 bytes length, 1 byte protocol */
319 if (buf[2] != IPAC_PROTO_IPACCESS)
Harald Welte81cff3c2009-08-08 12:14:53 +0200320 return 0;
321
322 if (buf[4] != IPAC_MSGT_ID_RESP)
323 return 0;
324
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +0100325 handle_response(buf+6, len-6);
326 return 0;
Harald Welte923a3bd2009-02-14 12:51:36 +0000327}
328
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200329static int bfd_cb(struct osmo_fd *bfd, unsigned int flags)
Harald Welte923a3bd2009-02-14 12:51:36 +0000330{
331 if (flags & BSC_FD_READ)
332 return read_response(bfd->fd);
333 if (flags & BSC_FD_WRITE) {
334 bfd->when &= ~BSC_FD_WRITE;
335 return bcast_find(bfd->fd);
336 }
337 return 0;
338}
339
Pablo Neira Ayusobf540cb2011-05-06 12:11:06 +0200340static struct osmo_timer_list timer;
Harald Welte923a3bd2009-02-14 12:51:36 +0000341
342static void timer_cb(void *_data)
343{
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200344 struct osmo_fd *bfd = _data;
Harald Welte923a3bd2009-02-14 12:51:36 +0000345
346 bfd->when |= BSC_FD_WRITE;
347
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +0100348 base_stations_bump(false);
349
Pablo Neira Ayusobf540cb2011-05-06 12:11:06 +0200350 osmo_timer_schedule(&timer, 5, 0);
Harald Welte923a3bd2009-02-14 12:51:36 +0000351}
352
353int main(int argc, char **argv)
354{
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200355 struct osmo_fd bfd;
Harald Welte923a3bd2009-02-14 12:51:36 +0000356 int rc;
357
Harald Welte28e9f602016-02-18 11:15:16 +0100358 printf("abisip-find (C) 2009 by Harald Welte\n");
Harald Welte923a3bd2009-02-14 12:51:36 +0000359 printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n");
360
Neels Hofmeyr2a9ac192017-12-25 18:43:40 +0100361 handle_options(argc, argv);
362
363 if (!cmdline_opts.ifname)
Neels Hofmeyr5bf1e152017-12-25 18:22:18 +0100364 fprintf(stdout, "- You might need to specify the outgoing\n"
365 " network interface, e.g. ``%s eth0''\n", argv[0]);
366 if (!cmdline_opts.list_view)
367 fprintf(stdout, "- You may find the --list-view option convenient.\n");
Harald Welte042401c2009-06-29 10:43:04 +0200368
Harald Welte923a3bd2009-02-14 12:51:36 +0000369 bfd.cb = bfd_cb;
370 bfd.when = BSC_FD_READ | BSC_FD_WRITE;
Neels Hofmeyr2a9ac192017-12-25 18:43:40 +0100371 bfd.fd = udp_sock(cmdline_opts.ifname);
Harald Welte042401c2009-06-29 10:43:04 +0200372 if (bfd.fd < 0) {
373 perror("Cannot create local socket for broadcast udp");
374 exit(1);
375 }
Harald Welte923a3bd2009-02-14 12:51:36 +0000376
Harald Welte8d359652016-11-26 14:57:23 +0100377 rc = osmo_fd_register(&bfd);
378 if (rc < 0) {
379 fprintf(stderr, "Cannot register FD\n");
380 exit(1);
381 }
Harald Welte923a3bd2009-02-14 12:51:36 +0000382
Pablo Neira Ayuso51215762017-05-08 20:57:52 +0200383 osmo_timer_setup(&timer, timer_cb, &bfd);
Pablo Neira Ayusobf540cb2011-05-06 12:11:06 +0200384 osmo_timer_schedule(&timer, 5, 0);
Harald Welte923a3bd2009-02-14 12:51:36 +0000385
386 printf("Trying to find ip.access BTS by broadcast UDP...\n");
387
388 while (1) {
Pablo Neira Ayuso4db92992011-05-06 12:11:23 +0200389 rc = osmo_select_main(0);
Harald Welte923a3bd2009-02-14 12:51:36 +0000390 if (rc < 0)
391 exit(3);
392 }
393
394 exit(0);
395}
396