Ejemplo n.º 1
0
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;
	}
}
Ejemplo n.º 2
0
// 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);
}
Ejemplo n.º 3
0
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(SQL_IP_TIMEOUT);
	ggConfig.mgMaxPduSize = gConfig.getNum(SQL_PDU_MAX_SIZE);
	ggConfig.mgMaxConnections = gConfig.getNum(SQL_PDP_MAX_COUNT);
	ggConfig.mgIpTossDup = gConfig.getNum(SQL_IP_TOSS_DUP);


	string logfile = gConfig.getStr(SQL_LOG_FILE);
	if (logfile.length()) {
		mg_log_fp = fopen(logfile.c_str(),"w");
		if (mg_log_fp == 0) {
			MGERROR("could not open %s log file:%s",SQL_LOG_FILE,logfile.c_str());
			// (pat) Add an alert to the console so people will notice this problem.
			LOG(ALERT) << "Cound not open tun device, GPRS non-functional:";
		}
	}

	// 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("%s specifies too many connections (%d) specifed, using 254",
			SQL_PDP_MAX_COUNT,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(SQL_IP_BASE).c_str();
	uint32_t mgIpBasenl = inet_addr(ip_base_str);
	if (mgIpBasenl == INADDR_NONE) {
		MGERROR("miniggsn: %s address invalid:%s",SQL_IP_BASE,ip_base_str);
		return false;
	}

	if ((ntohl(mgIpBasenl) & 0xff) == 0) {
		MGERROR("miniggsn: %s address should not end in .0 but proceeding anyway: %s",SQL_IP_BASE,ip_base_str);
	}

	//const char *route_str = DEFAULT_IP_ROUTE;
	const char *route_str = 0;
	char route_buf[40];
	string route_save;
	if (gConfig.defines(SQL_IP_ROUTE)) {
		route_save = gConfig.getStr(SQL_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("miniggsn: %s address is too long:%s",SQL_IP_ROUTE,route_str);
			// but use it anyway.
		}

		if (! ip_addr_crack(route_str,&route_basenl,&route_masknl) || route_basenl == INADDR_NONE) {
			MGWARN("miniggsn: %s is not a valid ip address: %s",SQL_IP_ROUTE,route_str);
			// but use it anyway.
		}
		if (route_masknl == 0) {
			MGWARN("miniggsn: %s is not a valid route, /mask part missing or invalid: %sn",
				SQL_IP_ROUTE,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: %s = %s ip address does not appear to be in range of %s = %s",
				SQL_IP_BASE, ip_base_str, SQL_IP_ROUTE,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(SQL_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("  %s=%s", SQL_IP_BASE, ip_ntoa(mgIpBasenl,NULL));
		MGINFO("  %s=%d", SQL_PDP_MAX_COUNT, ggConfig.mgMaxConnections);
		MGINFO("  %s=%s", SQL_IP_ROUTE, route_str);
		MGINFO("  %s=%d", SQL_PDU_MAX_SIZE, ggConfig.mgMaxPduSize);
		MGINFO("  %s=%d", SQL_IP_TIMEOUT, ggConfig.mgIpTimeout);
		MGINFO("  %s=%d", SQL_FIREWALL_ENABLE, firewall_enable);
		MGINFO("  %s=%d", SQL_IP_TOSS_DUP, 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(SQL_TUN_IF_NAME).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);
			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;
}
Ejemplo n.º 4
0
// 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) {
		MUST_HAVE((packet_dest_ip_addr & rp->ipMasknl) != (rp->ipBasenl & rp->ipMasknl));
	}

    // 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;
}
Ejemplo n.º 5
0
// Call this to update the PCO for retransmission to the MS.
// We cannot just make up a PCO ahead of time and then send it out to
// all the MS for two reasons:
// o The incoming options have uniquifying transaction identifiers that are
// different each time, so we must modify the incoming pcoReq each time while
// carefully preserving those modifiers.
// o There are two major formats for the PCO options - see below.
static void setPco(ByteVector &resultpco, ByteVector &pcoReq, mg_con_t *mgp)
{
	if (mg_dns[0] == 0) { ip_finddns(mg_dns); }

	// GSM 24.008 10.5.6.3: Procotol Configuration Options.
	// These are the "negotiated" address params wrapped in PPP,
	// except they are not very negotiated; we are going to cram them
	// into the MS and it can accept them or die.
	// The first byte is the header, followed by any number of:
	//	protocol id (2 octets)
	//	length of contents (1 octet)
	// I have seen this supplied by the MS in two different ways:
	// - as two separate 0x8021 requests, each with a single option request
	//	(Blackberry)
	// - as a single 0x8021 request with two option requests for the two DNS servers.
	//	(Samsung phone)
	resultpco.clone(pcoReq);
	unsigned char *pc = resultpco.begin();
	unsigned char *end = pc + resultpco.size();
	 __attribute__((unused)) const char *protname = "";
	if (pc == NULL) {
		MGERROR("SGSN: Empty PCO field")
	}
	else if (*pc++ != 0x80) {
		MGERROR("SGSN: Unrecognized PCO Config Protocol: %d\n",pc[-1]);
	} else while (pc < end) {
		unsigned proto = (pc[0] << 8) + pc[1];
		pc += 2;
		unsigned ipcplen = *pc++;
		if (proto == 0x8021) {	// IP Control Protocol.
			// IPCP looks like this:
			// 1 byte: command: 1 => configure-request
			// 1 byte: transaction uniquifying identifier.
			// 2 bytes: total length of ipcp (even though length above was a byte)
			// Followed by IPCP options, where each option is:
			//		1 byte: option code
			//		1 byte: total option length N (should be 6)
			//		N bytes: data
			if (pc[0] == 1) {	// command = configure-request
				// pc[1] is the uniquifying identifier, leave it alone.
				// pc[2]&pc[3] are another length.
				// Followed by one or more 6 byte option consisting of:
					// pc[4]: IPCP option code
					// pc[5]: length (6)
					// pc[6-9]: data
				// Note: the 4 byte header length is included in the option_len
				unsigned ipcp_len = pc[3];  // pc[2] better be 0.
				unsigned char *op;
				for (op = &pc[4]; op < pc + ipcp_len; op += op[1]) {
					const char *what = "";
					switch (op[0]) {
					case 0x03:
						pc[0] = 2;
                                                if (op[1] < 6) { goto bad_ipcp_opt_len; }
						memcpy(&op[2], &mgp->mg_ip, 4);
						break;
					case 0x81:	// primary dns.
						pc[0] = 2;	// IPCP command = ACK
						if (op[1] < 6) {
							bad_ipcp_opt_len:
							MGERROR("SGSN: Invalid PCO IPCP Config Option Length: opt=0x%x len=%d\n",
							op[0], op[1]);
							goto next_protocol;
						}
						memcpy(&op[2], &mg_dns[0], 4);	// addr in network order.
						break;
					case 0x83:	// secondary dns
						pc[0] = 2;	// IPCP command = ACK
						if (op[1] < 6) { goto bad_ipcp_opt_len; }
						memcpy(&op[2], &mg_dns[mg_dns[1] ? 1 : 0], 4);	// addr in network order.
						break;
					case 2:
						what = "IP Compression Protocol"; goto bad_ipcp;
					case 0x82:
						what = "primary NBNS [NetBios Name Service]"; goto bad_ipcp;
					case 0x84:
						what = "secondary NBNS [NetBios Name Service]"; goto bad_ipcp;
					default: bad_ipcp:
						// It would be nice to send an SMS message that the phone is set up improperly.
						MGWARN("SGSN: warning: ignoring PDP Context activation IPCP option %d %s\n",pc[4],what);
						break;
					}
				}
			}
		} else if (proto == 0xc021) {	// LCP: 
			protname = "(LCP [Link Control Protocol] for PPP)";
			goto unsupported_protocol;
		} else if (proto == 0xc223) {	// CHAP:
			protname = "(CHAP [Challenge-Handshake Authentication Protocol] for PPP)";
			goto unsupported_protocol;
		} else if (proto == 0xc023) {	// PAP: Password authentication protocol.
			protname = "(PAP [Password Authentication Protocol] for PPP)";
			// 6-12: Remove this message for the 3.0 release because we get
			// lots of bogus complaints about PAP.
			//goto unsupported_protocol;
			goto next_protocol;
		} else {
			// If we see any of these options are non-empty, the MS may be configured to use PPP.
			// It is hopeless; user must reconfigure the MS.
			unsupported_protocol:
			if (ipcplen) {
				MGWARN("SGSN: warning: ignoring PDP Context activation sub-protocol 0x%x %s; MS may require reconfiguration.\n",proto,protname);
			}
		}
		next_protocol:
		pc += ipcplen;
	}
}