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; } }
// 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); }
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; }
// 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; }
// 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; } }