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