OpenBTS-UMTS/SGSNGGSN/miniggsn.cpp

667 lines
24 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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