forked from PentHertz/OpenBTS-UMTS
-
Notifications
You must be signed in to change notification settings - Fork 0
/
miniggsn.cpp
666 lines (587 loc) · 23.7 KB
/
miniggsn.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
/*
* OpenBTS provides an open source alternative to legacy telco protocols and
* traditionally complex, proprietary hardware systems.
*
* Copyright 2011, 2014 Range Networks, Inc.
*
* This software is distributed under the terms of the GNU Affero General
* Public License version 3. See the COPYING and NOTICE files in the main
* directory for licensing information.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
*/
//TODO - include the TCP sequence number in the message,
// and identify the duplicate packets,
// then rerun a test downloading a single jpg
// with tossing off, to see what is happening.
// iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to 192.168.1.8
// iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
// iptables -t nat -F # Flush current tables
// iptables -t nat -L # List current tables
// ---
// Options:
// o 2 tunnels. First is read/write by the ggsn. Second is configured via linux.
// This gives us a pseudo-'device' so we can use linux forwarding.
// iptables -t nat -A POSTROUTING -out eth0 -j MASQUERADE
// iptables -A FORWARD -i tun2 -j ACCEPT
// Another advantage is the tunnel could go to a remote machine.
// Or the first tunnel could terminate back in the OpenBTS.
// o Do NAT myself.
// o Use linux NAT. Write MS->Net packets to router. Bind raw socket on 192.168.1.8 for
// all packets, looking for our own.
// NOTES:
// The ip tunnel local/remote specify the outer transport layer IP addresses in the IPIP
// protocol header prepended to packets sent on the tunnel.
// The ifconfig adds two local addresses
//
// Tunnel Example:
// Network A: net 10.0.1.0/24 router 10.0.1.1 public address 172.16.17.18 on public net C
// Network B: net 10.0.2.0/24 router 10.0.2.1 public address 172.19.20.21 on public net C
// Network A:
// ip tunnel add netb mode gre remote 172.19.20.21 local 172.16.17.18
// # Apparently this tunnel is the router.
// ip addr add 10.0.1.1 dev netb
// ip route add 10.0.2.0/24 dev netb
// Or for ipip tunneling:
// ifconfig tunl0 10.0.1.1 pointopoint 172.19.20.21
// route add -net 10.0.2.0 netmask 255.255.255.0 dev netb
// Network B:
// ip tunnel add neta mode gre remote 172.16.17.18 local 172.19.20.21
// ip addr add 10.0.2.1 dev neta
// ip addr add 10.0.1.0/24 dev neta
// Or for ipip tunneling:
// ifconfig tunl0 10.0.2.1 pointopoint 172.19.20.21
// route add -net 10.0.2.0 netmask 255.255.255.0 dev tunl0
//
//
// Another Tunnel example from IPIPNotes:
// Router_1 eth0: 1.2.3.4 - Asterisk system
// Router_2 eth0: 4.3.2.1 eth1: 10.0.0.1 NAT private network and SIP phones.
// Router_1
// ip tunnel add iptun mode ipip remote 4.3.2.1 local 10.0.1.1
// route add -net 10.0.2.0/24 dev iptun
// Router_2
// ip tunnel add iptun mode ipip remote 1.2.3.4 local 10.0.2.1
// route add -net 10.0.1.0/24 dev iptun
// Router_1
// route add -net 10.0.0.0/24 dev iptun
// route add -net 10.0.0.0/24 gw 10.0.0.1
// Note: The comment is backwards.
// Allows you to ping any device on 10.0.0.0/24 from router_1 (not from router_2)
// Allows any 10.0.0.) device to ping router1 using 10.0.1.1
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <errno.h>
#include <signal.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <poll.h>
#include <netdb.h> // pat added for gethostbyname
#include <netinet/ip.h> // pat added for IPv4 iphdr
#include <netinet/tcp.h> // pat added for tcphdr
#include <netinet/udp.h> // pat added for udphdr
#include <linux/if.h> // pat added.
#include <linux/if_tun.h> // pat added.
//#include <sys/ioctl.h> // pat added, then removed because it defines NCC used in GSMConfig.
#include <assert.h> // pat added
#include <stdarg.h> // pat added
#include <time.h> // pat added.
#include <sys/time.h>
#include <sys/types.h>
#include <wait.h>
#include "miniggsn.h"
#undef NCC // Make sure. This is defined in ioctl.h, but used as a name in GSMConfig.h.
#include "Ggsn.h"
#include <Configuration.h>
// A mini-GGSN included inside the SGSN.
// Each MS will be assigned a dynamic IP address.
// We will use one 256-wide bank of IP address, eg: 192.168.99.1 - 192.68.99.254.
// This needs to be a config option.
// The addresses are be serviced by a NAT running on this box.
// An alternative architecture would be to get IP addresses from a DHCP server
// on whatever router is doing the NAT.
// The NAT could be tied to a static IP; a dynamic IP; or if the bts has two IP addresses,
// the NAT could be tied to the second static or dynamic IP address used solely for phones;
// or tied to a tunnel that goes elsewhere.
// Since there are so many possibilibies, the nat is configured externally.
namespace SGSN {
int pdpWriteHighSide(PdpContext *pdp, unsigned char *packet, unsigned len);
int tun_fd = -1; // This is the tunnel we use to talk with the MSs.
FILE *mg_log_fp = NULL; // Extra log file for IP traffic.
int mg_debug_level = 0;
// old:
//static char const *mg_base_ip_str = "192.168.99.1"; // This did not raw bind.
//static char const *mg_base_ip_route = "192.168.99.0/24";
//static char const *mg_net_mask = "255.255.255.0"; // todo: get this from "/24" above.
static struct GgsnConfig {
unsigned mgMaxPduSize;
int mgMaxConnections; // The maximum number of PDP contexts, which is roughly
// the maximum number of simultaneous MS allowed.
unsigned mgIpTimeout; // Dont reuse a connection for this many seconds.
unsigned mgIpTossDup; // Toss duplicate packets.
} ggConfig;
// Mini-Firewall rules
struct GgsnFirewallRule {
GgsnFirewallRule *next;
uint32_t ipBasenl;
uint32_t ipMasknl;
GgsnFirewallRule(GgsnFirewallRule *wnext,uint32_t wipbasenl, uint32_t wmasknl) :
next(wnext),ipBasenl(wipbasenl),ipMasknl(wmasknl) {}
};
GgsnFirewallRule *gFirewallRules = NULL;
void addFirewallRule(uint32_t ipbasenl,uint32_t masknl) {
gFirewallRules = new GgsnFirewallRule(gFirewallRules,ipbasenl,masknl);
}
static mg_con_t *mg_cons = 0;
// Now in Utils.cpp
//const char *timestr()
//{
// struct timeval tv;
// struct tm tm;
// static char result[30];
// gettimeofday(&tv,NULL);
// localtime_r(&tv.tv_sec,&tm);
// unsigned tenths = tv.tv_usec / 100000; // Rounding down is ok.
// sprintf(result," %02d:%02d:%02d.%1d",tm.tm_hour,tm.tm_min,tm.tm_sec,tenths);
// return result;
//}
// Find a free IP connection or return NULL.
// Each PDP context activation gets a new IP address.
// The IP addresses are recycled after tiimeout.
// 7-10-2012: Each MS may ask for several IP addresses with different NSAPI.
// The IMSI+NSAPI now maps to an IP address in a semi-permanent way, so the MS
// effectively has a static IP address within the range assigned by the BTS,
// until the BTS is power cycled. The ptmsi is a unique id associated with the imsi.
mg_con_t *mg_con_find_free(uint32_t ptmsi, int nsapi)
{
// Start by looking for this specific old connection:
// TODO: This should use a map for efficiency.
int i;
mg_con_t *mgp = &mg_cons[0];
for (i=0; i < ggConfig.mgMaxConnections; i++, mgp++) {
if (mgp->mg_ptmsi == ptmsi && mgp->mg_nsapi == nsapi) {
return mgp;
}
}
// Look for an unused IP address.
double now = pat_timef();
static int mgnextindex = 0;
for (i=0; i < ggConfig.mgMaxConnections; i++) {
mgp = &mg_cons[mgnextindex];
if (++mgnextindex == ggConfig.mgMaxConnections) { mgnextindex = 0; }
if (mgp->mg_pdp == NULL) {
// Dont reuse an ip address for mg_ip_timeout.
// TCP packets will continue to arrive for an IP address
// for quite some time after it becomes inactive.
if (mgp->mg_time_last_close && mgp->mg_time_last_close + ggConfig.mgIpTimeout > now) continue;
//mgp->mg_pdp = pctx;
mgp->mg_ptmsi = ptmsi;
mgp->mg_nsapi = nsapi;
return mgp;
}
}
return NULL;
}
void mg_con_open(mg_con_t *mgp,PdpContext *pdp)
{
mgp->mg_pdp = pdp;
}
void mg_con_close(mg_con_t *mgp)
{
mgp->mg_pdp = NULL;
mgp->mg_time_last_close = pat_timef();
}
#if 0
static mg_con_t *mg_con_find_by_ctx(PdpContext *pctx)
{
int i; mg_con_t *mgp = mg_cons;
for (i=0; i < ggConfig.mgMaxConnections; i++, mgp++) {
if (mgp->mg_pdp == pctx) { return mgp; }
}
return NULL;
}
#endif
static mg_con_t *mg_con_find_by_ip(uint32_t addr)
{
int i; mg_con_t *mgp = mg_cons;
for (i=0; i < ggConfig.mgMaxConnections; i++, mgp++) {
if (mgp->mg_ip == addr) { return mgp; }
}
return NULL;
}
static bool verbose = true;
static char *packettoa(char *result,unsigned char *packet, int len)
{
struct iphdr *iph = (struct iphdr*)packet;
char nbuf1[40], nbuf2[40];
if (verbose && iph->protocol == IPPROTO_TCP) {
struct tcphdr *tcph = (struct tcphdr*) (packet + 4 * iph->ihl);
sprintf(result,"proto=%s %d byte packet seq=%u ack=%u id=%u frag=%u from %s:%d to %s:%d",
ip_proto_name(iph->protocol), len, tcph->seq, tcph->ack_seq,
iph->id, iph->frag_off,
ip_ntoa(iph->saddr,nbuf1),tcph->source,
ip_ntoa(iph->daddr,nbuf2),tcph->dest);
} else {
sprintf(result,"proto=%s %d byte packet from %s to %s",
ip_proto_name(iph->protocol), len,
ip_ntoa(iph->saddr,nbuf1),
ip_ntoa(iph->daddr,nbuf2));
}
return result;
}
unsigned char *miniggsn_rcv_npdu(int *plen, uint32_t *dstaddr)
{
static unsigned char *recvbuf = NULL;
if (recvbuf == NULL) {
recvbuf = (unsigned char*)malloc(ggConfig.mgMaxPduSize+2);
if (!recvbuf) { /**error = -ENOMEM;*/ return NULL; }
}
// The O_NONBLOCK was set by default! Is not happening any more.
{
int flags = fcntl(tun_fd,F_GETFL,0);
if (flags & O_NONBLOCK) {
//MGDEBUG(4,"O_NONBLOCK = %d",O_NONBLOCK & flags);
flags &= ~O_NONBLOCK; // Want Blocking!
int fcntlstat = fcntl(tun_fd,F_SETFL,flags);
MGWARN("ggsn: WARNING: Turning off tun_fd blocking flag, fcntl=%d",fcntlstat);
}
}
// We can just read from the tunnel.
int ret = read(tun_fd,recvbuf,ggConfig.mgMaxPduSize);
if (ret < 0) {
MGERROR("ggsn: error: reading from tunnel: %s", strerror(errno));
//*error = ret;
return NULL;
} else if (ret == 0) {
MGERROR("ggsn: error: zero bytes reading from tunnel: %s", strerror(errno));
//*error = ret; // huh?
return NULL;
} else {
struct iphdr *iph = (struct iphdr*)recvbuf;
{
char infobuf[200];
MGINFO("ggsn: received %s at %s",packettoa(infobuf,recvbuf,ret), timestr().c_str());
//MGLOGF("ggsn: received proto=%s %d byte npdu from %s for %s at %s",
//ip_proto_name(iph->protocol), ret,
//ip_ntoa(iph->saddr,nbuf),
//ip_ntoa(iph->daddr,NULL), timestr());
}
*dstaddr = iph->daddr;
// TODO: Do we have to allocate a new buffer?
*plen = ret;
// Zero terminate for the convenience of the pinger.
recvbuf[ret] = 0;
return recvbuf;
}
}
// If this is a duplicate TCP packet, throw it away.
// The MS is so slow to respond that the servers often send dups
// which are unnecessary because we have reliable communication between here
// and the MS, so just toss them.
// Update 3-2012: Always do the check to print messages for dup packets even if not discarded.
static int mg_toss_dup_packet(mg_con_t*mgp,unsigned char *packet, int packetlen)
{
struct iphdr *iph = (struct iphdr*)packet;
if (iph->protocol != IPPROTO_TCP) { return 0; }
struct tcphdr *tcph = (struct tcphdr*) (packet + 4 * iph->ihl);
if (tcph->rst | tcph->urg) { return 0; }
int i;
for (i = 0; i < MG_PACKET_HISTORY; i++) {
// 3-2012: Jpegs are not going through the system properly.
// I am adding some more checks here to see if we are tossing packets inappropriately.
// The tot_len includes headers, but if they are not the same in the duplicate packet, oh well.
// TODO: If the connection is reset we should zero out our history.
if (mgp->mg_packets[i].saddr == iph->saddr &&
mgp->mg_packets[i].daddr == iph->daddr &&
mgp->mg_packets[i].totlen == iph->tot_len &&
//mgp->mg_packets[i].ipfragoff == iph->frag_off &&
//mgp->mg_packets[i].ipid == iph->id &&
mgp->mg_packets[i].seq == tcph->seq &&
mgp->mg_packets[i].source == tcph->source &&
mgp->mg_packets[i].dest == tcph->dest
) {
const char *what = ggConfig.mgIpTossDup ? "discarding " : "";
char buf1[40],buf2[40];
MGINFO("ggsn: %sduplicate %d byte packet seq=%d frag=%d id=%d src=%s:%d dst=%s:%d",what,
packetlen,tcph->seq,iph->frag_off,iph->id,
ip_ntoa(iph->saddr,buf1),tcph->source,
ip_ntoa(iph->daddr,buf2),tcph->dest);
return ggConfig.mgIpTossDup; // Toss duplicate tcp packet if option set.
}
}
i = mgp->mg_oldest_packet;
if (++mgp->mg_oldest_packet >= MG_PACKET_HISTORY) { mgp->mg_oldest_packet = 0; }
mgp->mg_packets[i].saddr = iph->saddr;
mgp->mg_packets[i].daddr = iph->daddr;
mgp->mg_packets[i].totlen = iph->tot_len;
//mgp->mg_packets[i].ipfragoff = iph->frag_off;
//mgp->mg_packets[i].ipid = iph->id;
mgp->mg_packets[i].seq = tcph->seq;
mgp->mg_packets[i].source = tcph->source;
mgp->mg_packets[i].dest = tcph->dest;
return 0; // Do not toss.
}
// There is data available on the socket. Go get it.
// see handle_nsip_read()
void miniggsn_handle_read()
{
int packetlen;
uint32_t dstaddr;
unsigned char *packet = miniggsn_rcv_npdu(&packetlen, &dstaddr);
if (!packet) { return; }
// We need to reassociate the packet with the PdpContext to which it belongs.
mg_con_t *mgp = mg_con_find_by_ip(dstaddr);
if (mgp == NULL || mgp->mg_pdp == NULL) {
MGERROR("ggsn: error: cannot find PDP context for incoming packet for IP dstaddr=%s",
ip_ntoa(dstaddr,NULL));
return; // -1;
}
if (mg_toss_dup_packet(mgp,packet,packetlen)) { return; }
PdpContext *pdp = mgp->mg_pdp;
//MGDEBUG(2,"miniggsn_handle_read pdp=%p",pdp);
pdp->pdpWriteHighSide(packet,packetlen);
}
// The npdu is a raw packet including the ip header.
int miniggsn_snd_npdu_by_mgc(mg_con_t *mgp,unsigned char *npdu, unsigned len)
{
// Verify the IP header.
struct iphdr *ipheader = (struct iphdr*)npdu;
// The return address must be the MS itself.
uint32_t ms_ip_address = mgp->mg_ip;
uint32_t packet_source_ip_addr = ipheader->saddr;
uint32_t packet_dest_ip_addr = ipheader->daddr;
char infobuf[200];
MGINFO("ggsn: writing %s at %s",packettoa(infobuf,npdu,len),timestr().c_str());
//MGLOGF("ggsn: writing proto=%s %d byte npdu to %s from %s at %s",
//ip_proto_name(ipheader->protocol),
//len,ip_ntoa(packet_dest_ip_addr,NULL),
//ip_ntoa(packet_source_ip_addr,nbuf), timestr().c_str());
#define MUST_HAVE(assertion) \
if (! (assertion)) { MGERROR("ggsn: Packet failed test, discarded: %s",#assertion); return -1; }
if (mg_debug_level > 2) ip_hdr_dump(npdu,"npdu");
MUST_HAVE(ipheader->version == 4); // 4 as in IPv4
MUST_HAVE(ipheader->ihl >= 5); // Minimum header length is 5 words.
int checksum = ip_checksum(ipheader,sizeof(*ipheader),NULL);
MUST_HAVE(checksum == 0); // If fails, packet is bad.
MUST_HAVE(ipheader->ttl > 0); // Time to live - how many hops allowed.
// The blackberry sends ICMP packets, so we better support.
// I'm just going to allow any protocol through.
//MUST_HAVE(ipheader->protocol == IPPROTO_TCP ||
// ipheader->protocol == IPPROTO_UDP ||
// ipheader->protocol == IPPROTO_ICMP);
MUST_HAVE(packet_source_ip_addr == ms_ip_address);
#if OLD_FIREWALL
// The destination address may not be a local address on this machine
// as configured by the operator.
// Note these are all in network order, so be careful.
//uint32_t local_ip_addr = inet_addr(mg_base_ip_str); // probably "192.168.99.1"
uint32_t net_mask = inet_addr(mg_net_mask); // probably "255.255.255.0"
MUST_HAVE((packet_dest_ip_addr & net_mask) != (local_ip_addr & net_mask));
#endif
for (GgsnFirewallRule *rp = gFirewallRules; rp; rp = rp->next) {
// 12-17: Change the message to indicate that this was a firewall rule violation.
//MUST_HAVE((packet_dest_ip_addr & rp->ipMasknl) != (rp->ipBasenl & rp->ipMasknl));
if (! ((packet_dest_ip_addr & rp->ipMasknl) != (rp->ipBasenl & rp->ipMasknl)) ) {
char ipaddrbuf[50]; ip_ntoa(packet_dest_ip_addr,ipaddrbuf);
MGERROR("ggsn: Packet wth dest ip = %s discarded by firewall",ipaddrbuf);
return -1;
}
}
// Decrement ttl and recompute checksum. We are doing this in place.
ipheader->ttl--;
ipheader->check = 0;
//ipheader->check = htons(ip_checksum(ipheader,sizeof(*ipheader),NULL));
ipheader->check = ip_checksum(ipheader,sizeof(*ipheader),NULL);
// Just write to the MS-side tunnel device.
int result = write(tun_fd,npdu,len);
if (result != (int) len) {
MGERROR("ggsn: error: write(tun_fd,%d) result=%d %s",len,result,strerror(errno));
}
return 0;
}
#if 0 // not needed
// Route an n-pdu from the MS out to the internet.
// Called by SNDCP when it has received/re-assembled a N-PDU
int miniggsn_snd_npdu(PdpContext *pctx,unsigned char *npdu, unsigned len)
{
// Find the fd from the pctx; We should put this in the pdp_ctx.
mg_con_t *mgp = mg_con_find_by_ctx(pctx);
if (mgp == NULL) { return -1; } // Whoops
return miniggsn_snd_npdu_by_mgc(mgp, npdu, len);
}
#endif
time_t gGgsnInitTime;
bool miniggsn_init()
{
static int initstatus = -1; // -1: uninited; 0:init failed; 1: init succeeded.
if (initstatus >= 0) {return initstatus;}
initstatus = 0; // assume failure.
// We init config options at GGSN startup.
// They cannot be changed while running.
// To change an option, you would have to stop and restart the GGSN.
ggConfig.mgIpTimeout = gConfig.getNum("GGSN.IP.ReuseTimeout");
ggConfig.mgMaxPduSize = gConfig.getNum("GGSN.IP.MaxPacketSize");
ggConfig.mgMaxConnections = gConfig.getNum("GGSN.MS.IP.MaxCount");
ggConfig.mgIpTossDup = gConfig.getBool("GGSN.IP.TossDuplicatePackets");
string logfile = gConfig.getStr("GGSN.Logfile.Name");
if (logfile.length()) {
mg_log_fp = fopen(logfile.c_str(),"w");
if (mg_log_fp == 0) {
MGERROR("could not open %s log file:%s","GGSN.Logfile.Name",logfile.c_str());
}
}
// This is the first message in the newly opened file.
time(&gGgsnInitTime);
MGINFO("Initializing Mini GGSN %s",ctime(&gGgsnInitTime)); // ctime includes a newline.
if (mg_log_fp) {
mg_debug_level = 1;
MGINFO("GGSN logging to file %s",logfile.c_str());
}
if (ggConfig.mgMaxConnections > 254) {
MGERROR("GGSN.MS.IP.MaxCount specifies too many connections (%d) specifed, using 254",
ggConfig.mgMaxConnections);
ggConfig.mgMaxConnections = 254;
}
// We need three IP things:
// 1. the route expressed using "/maskbits" notation,
// 2. the base ip address,
// 3. the mask for the MS (which has very little to do with the netmask of the host we are running on.)
// All three can be derived from the ipRoute, if specfied.
// But conceivably the user might want to start their base ip address elsewhere.
const char *ip_base_str = gConfig.getStr("GGSN.MS.IP.Base").c_str();
uint32_t mgIpBasenl = inet_addr(ip_base_str);
if (mgIpBasenl == INADDR_NONE) {
MGERROR("miniggn: GGSN.MS.IP.Base address invalid:%s",ip_base_str);
return false;
}
if ((ntohl(mgIpBasenl) & 0xff) == 0) {
MGERROR("miniggn: GGSN.MS.IP.Base address should not end in .0 but proceeding anyway: %s",ip_base_str);
}
const char *route_str = 0;
char route_buf[40];
string route_save;
if (gConfig.defines("GGSN.MS.IP.Route")) {
route_save = gConfig.getStr("GGSN.MS.IP.Route");
route_str = route_save.c_str();
}
uint32_t route_basenl, route_masknl;
if (route_str && *route_str && *route_str != ' ') {
if (strlen(route_str) > strlen("aaa.bbb.ccc.ddd/yy") + 2) { // add some slop.
MGWARN("miniggn: GGSN.MS.IP.Route address is too long:%s",route_str);
// but use it anyway.
}
if (! ip_addr_crack(route_str,&route_basenl,&route_masknl) || route_basenl == INADDR_NONE) {
MGWARN("miniggsn: GGSN.MS.IP.Route is not a valid ip address: %s",route_str);
// but use it anyway.
}
if (route_masknl == 0) {
MGWARN("miniggsn: GGSN.MS.IP.Route is not a valid route, /mask part missing or invalid: %sn",
route_str);
// but use it anyway.
}
// We would like to check that the base ip is within the ip route range,
// which is tricky, but check the most common case:
if ((route_basenl&route_masknl) != (mgIpBasenl&route_masknl)) {
MGWARN("miniggsn: GGSN.MS.IP.Base = %s ip address does not appear to be in range of GGSN.MS.IP.Route = %s",
ip_base_str, route_str);
// but use it anyway.
}
} else {
// Manufacture a route string. Assume route is 24 bits.
route_masknl = inet_addr("255.255.255.0");
route_basenl = mgIpBasenl & route_masknl; // Set low byte to 0.
ip_ntoa(route_basenl,route_buf);
strcat(route_buf,"/24");
route_str = route_buf;
}
// Firewall rules:
bool firewall_enable;
if ((firewall_enable = gConfig.getNum("GGSN.Firewall.Enable"))) {
// Block anything in the routed range:
addFirewallRule(route_basenl,route_masknl);
// Block local loopback:
uint32_t tmp_basenl,tmp_masknl;
if (ip_addr_crack("127.0.0.1/24",&tmp_basenl,&tmp_masknl)) {
addFirewallRule(tmp_basenl,tmp_masknl);
}
// Block the OpenBTS station itself:
uint32_t *myaddrs = ip_findmyaddr();
for ( ; *myaddrs != (unsigned)-1; myaddrs++) {
addFirewallRule(*myaddrs,0xffffffff);
}
if (firewall_enable >= 2) {
// Block all private addresses:
// 16-bit block (/16 prefix, 256 × C) 192.168.0.0 192.168.255.255 65536
uint32_t private_addrnl = inet_addr("192.168.0.0");
uint32_t private_masknl = inet_addr("255.255.0.0");
addFirewallRule(private_addrnl,private_masknl);
// 20-bit block (/12 prefix, 16 × B) 172.16.0.0 172.31.255.255 1048576
private_addrnl = inet_addr("172.16.0.0");
private_masknl = inet_addr("255.240.0.0");
addFirewallRule(private_addrnl,private_masknl);
// 24-bit block (/8 prefix, 1 × A) 10.0.0.0 10.255.255.255 16777216
private_addrnl = inet_addr("10.0.0.0");
private_masknl = inet_addr("255.0.0.0");
addFirewallRule(private_addrnl,private_masknl);
}
}
MGINFO("GGSN Configuration:");
MGINFO(" GGSN.MS.IP.Base=%s", ip_ntoa(mgIpBasenl,NULL));
MGINFO(" GGSN.MS.IP.MaxCount=%d", ggConfig.mgMaxConnections);
MGINFO(" GGSN.MS.IP.Route=%s", route_str);
MGINFO(" GGSN.IP.MaxPacketSize=%d", ggConfig.mgMaxPduSize);
MGINFO(" GGSN.IP.ReuseTimeout=%d", ggConfig.mgIpTimeout);
MGINFO(" GGSN.Firewall.Enable=%d", firewall_enable);
MGINFO(" GGSN.IP.TossDuplicatePackets=%d", ggConfig.mgIpTossDup);
if (firewall_enable) {
MGINFO("GGSN Firewall Rules:");
for (GgsnFirewallRule *rp = gFirewallRules; rp; rp = rp->next) {
char buf1[40], buf2[40];
MGINFO(" block ip=%s mask=%s",ip_ntoa(rp->ipBasenl,buf1),ip_ntoa(rp->ipMasknl,buf2));
}
}
uint32_t dns[2]; // We dont use the result, we just want to print out the DNS servers now.
ip_finddns(dns); // The dns servers are polled again later.
const char *tun_if_name = gConfig.getStr("GGSN.TunName").c_str();
if (tun_fd == -1) {
ip_init();
tun_fd = ip_tun_open(tun_if_name,route_str);
if (tun_fd < 0) {
MGERROR("ggsn: ERROR: Could not open tun device %s",tun_if_name);
LOG(ALERT) << "Cound not open tun device:"<<tun_if_name; // TEMPORARY MESSAGE
return false;
}
}
// DEBUG: Try it again.
//printf("DEBUG: Opening tunnel again: %d\n",ip_tun_open(tun_if_name,route_str));
if (mg_cons) free(mg_cons);
mg_cons = (mg_con_t*)calloc(ggConfig.mgMaxConnections,sizeof(mg_con_t));
if (mg_cons == 0) {
MGERROR("ggsn: ERROR: out of memory");
return false;
}
//memset(mg_cons,0,sizeof(mg_cons));
uint32_t base_iphl = ntohl(mgIpBasenl);
// 8-15: no dont do this. It subverts the purpose of the BASE ip address.
//base_iphl &= ~255; // In case they specify 192.168.2.1, make it 192.168.2.0
int i;
// If the last digit is 0 (192.168.99.0), change it to 1 for the first IP addr served.
if ((base_iphl & 255) == 0) { base_iphl++; }
for (i=0; i < ggConfig.mgMaxConnections; i++) {
mg_cons[i].mg_ip = htonl(base_iphl + i);
//mg_cons[i].mg_ip = htonl(base_iphl + 1 + i);
// DEBUG!!!!! Use my own ip address.
//mg_cons[i].mg_ip = inet_addr("192.168.1.99");
//printf("adding IP=%s\n",ip_ntoa(mg_cons[i].mg_ip,NULL));
}
initstatus = 1;
return initstatus;
}
}; // namespace