对于 1024以内的端口,默认只提供给系统使用,外部应用是没有权限使用的,如果想要外部访问不带8080 ,则需要在服务端,设置一条如下iptable 指令;把端口号设置为80就可以省略不写。

iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080    // 8080 是服务器绑定的端口号,也可以改换成其他

这样会把所有的8080端口都转发到80端口,可以用下面的命令指定ip和port进行转发,

iptables -t nat -A PREROUTING -d 192.168.0.1 -p tcp --dport 80 -j DNAT --to-destination 192.168.0.1:8080

可以adb shell后直接执行命令,当然得root才行。执行后可以用 iptables -L -n -t nat 查看一下执行的结果,

可以看到命令已经成功执行,如果要删除可以用下面的命令,

iptables -t nat -D PREROUTING -d 192.168.0.1 -p tcp --dport 80 -j DNAT --to-destination 192.168.0.1:8080

执行完后,同样可以执行 iptables -L -n -t nat 看一下结果,发现设置的规则已经被删除。

经验证,开启root权限后或者eng版本可行,由于开启root权限将引起无法预料的安全性问题,是否有不开启root权限能达到同样效果的解决方案?

那就要把这条命令放到系统里面去执行,并且开放一个接口。

客制化一套API出来,可参考如下。在启动http服务时,设置true, 在关闭服务器或者服务器挂掉时,设置false。

--- a/frameworks/base/core/java/android/os/INetworkManagementService.aidl
+++ b/frameworks/base/core/java/android/os/INetworkManagementService.aidl
@@ -618,4 +618,9 @@ interface INetworkManagementService

    /**
     * 
     * @hide
     */
    void setPortNat(boolean enable,String gateway);
--- a/frameworks/base/services/core/java/com/android/server/NetworkManagementService.java
+++ b/frameworks/base/services/core/java/com/android/server/NetworkManagementService.java
@@ -3143,4 +3143,18 @@ public class NetworkManagementService extends INetworkManagementService.Stub
             throw e.rethrowAsParcelableException();
         }
     }
   /**
     * 
     * 
     * */
     public void setPortNat(boolean enable,String gateway) {
        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
        try {
            mConnector.execute("firewall", "port_nat", enable ? 1 : 0,gateway);
        } catch (NativeDaemonConnectorException e) {
            throw e.rethrowAsParcelableException();
       }
    }


--- a/system/netd/server/FirewallController.h
+++ b/system/netd/server/FirewallController.h
@@ -83,6 +83,9 @@ public:
     static const char* LOCAL_STANDBY;
 
     static const char* ICMPV6_TYPES[];

     int setPortNat(bool enable,const char* gateway);

 

--- a/system/netd/server/CommandListener.cpp
+++ b/system/netd/server/CommandListener.cpp
@@ -1696,6 +1696,23 @@ int CommandListener::FirewallCmd::runCommand(SocketClient *cli, int argc,
     }
     ///@}
  
     if (!strcmp(argv[1], "port_nat")) {
       
     	if (argc != 4) {
            cli->sendMsg(ResponseCode::CommandSyntaxError,
                         "Usage: firewall set_port_nat <bool>",
                         false);
            return 0;
        }
		bool enable = atoi(argv[2]) == 1;
        int res = sFirewallCtrl->setPortNat(enable,argv[3]);
        return sendGenericOkFail(cli, res);
     }

--- a/system/netd/server/FirewallController.cpp
+++ b/system/netd/server/FirewallController.cpp
@@ -774,3 +774,25 @@ int FirewallController::setEgressProtoRule(const char* proto, FirewallRule rule)
 
     return res;
 }

int FirewallController::setPortNat(bool enable,const char* gateway) {
     int res = 0;
     IptablesTarget target = V4;



   //   bool startFlag  = false;
		enable = true;
     if(enable)
     {
          ALOGE("httpd server start, all dest port 80 stream nat to 8080");
		  	
		  char const* port =":8080"; 
		  const char *gatewayPort;
		  char buf[30];
	      strcpy(buf,gateway);
	      strcat(buf,port);
	      gatewayPort=(const char*)buf;
		  
		  ALOGE("httpd gateway= %s,gatewayPort= %s",gateway,gatewayPort);
		  
		  //every time remove old rules
		  res |=  execIptables(target, "-t", "nat","-D","PREROUTING","-d",gateway,"-p", "tcp","--dport", "80","-j","DNAT","--to-destination",gatewayPort,NULL);  
		  res |=  execIptables(target, "-t", "nat","-D","PREROUTING","-d","192.168.42.129","-p", "tcp","--dport", "80","-j","DNAT","--to-destination","192.168.42.129:8080",NULL);
		  
		 
		  res |=  execIptables(target, "-t", "nat","-A","PREROUTING","-d",gateway,"-p", "tcp","--dport", "80","-j","DNAT","--to-destination",gatewayPort,NULL);
        
		  res |=  execIptables(target, "-t", "nat","-A","PREROUTING","-d","192.168.42.129","-p", "tcp","--dport", "80","-j","DNAT","--to-destination","192.168.42.129:8080",NULL);
     }else{        
          ALOGD("httpd server stop, clear the 80 to 8080 port nat chain");  
     }
	return res;
 }


这里发现了一个问题,就是有时候wifi和usbthering切换的时候,会导致所有的rules被清除掉,这样就会出现问题。解决这个问题的思路就是在清除的时候把规则重新加进去,对FirewallController所有有可能清除rules的函数里面打log,发现这个是在clearUdpForwarding函数里面执行的

 //Delete the old IPTABLE rule
 res |= execIptables(target, "-F", "oem_fwd", NULL);
 res |= execIptables(target, "-t", "nat", "-F", "PREROUTING", NULL);

于是在这个之后重新把我们的规则加上,

int FirewallController::clearUdpForwarding(const char* inInterface, const char* extInterface) {
    IptablesTarget target = V4;
    int res = 0;
	
	char const* port =":8080"; 
	char buf[GATEWAY_BUF_LENGTH];
	
    if(inInterface==NULL || extInterface==NULL){
        ALOGW("clearUdpForwarding: invalid args");
    } else {
        ALOGD("clearUdpForwarding: %s-%s", inInterface, extInterface);
    }
    //Delete the old IPTABLE rule
    res |= execIptables(target, "-F", "oem_fwd", NULL);
    res |= execIptables(target, "-t", "nat", "-F", "PREROUTING", NULL);
    property_set("net.rndis.client", "");
     
	//重新把规则加上
	if (strlen(mGateway)){	
		  strcpy(buf,mGateway);
	      strcat(buf,port);
		  res |=  execIptables(target, "-t", "nat","-A","PREROUTING","-d",mGateway,"-p", "tcp","--dport", "80","-j","DNAT","--to-destination",(const char*)buf,NULL);
		  res |=  execIptables(target, "-t", "nat","-A","PREROUTING","-d","192.168.42.129","-p", "tcp","--dport", "80","-j","DNAT","--to-destination","192.168.42.129:8080",NULL);

		  ALOGE("...clearUdpForwarding end....");
	} 		
    return res;

}

这里需要把传进来的gateway保存一下,

int FirewallController::setPortNat(bool enable,const char* gateway) {
     ...
     strcpy(mGateway,gateway);
     ...
}

构造函数里

--- a/system/netd/server/FirewallController.cpp
+++ b/system/netd/server/FirewallController.cpp
@@ -77,6 +77,7 @@ const char* FirewallController::ICMPV6_TYPES[] = {
 FirewallController::FirewallController(void) {
     // If no rules are set, it's in BLACKLIST mode
     mFirewallType = BLACKLIST;
+       memset(mGateway,0,GATEWAY_BUF_LENGTH);
 }

同时在头文件里面添加

--- a/system/netd/server/FirewallController.h
+++ b/system/netd/server/FirewallController.h
@@ -41,7 +41,7 @@ enum FirewallChinaRule { MOBILE, WIFI };
 
 #define PROTOCOL_GRE 47
 #define PROTOCOL_ICMP   1
-
+#define GATEWAY_BUF_LENGTH 64
 static const char* NSIOT_WHITE_LIST[] = {
         "1.1.1.1",
         "1.2.3.4",
@@ -116,7 +116,7 @@ private:
     int createChain(const char*, const char*, FirewallType);
     FirewallType getFirewallType(ChildChain);
     int refreshPkgUidList(const char *file_path, int uid, bool add);
-
+       char mGateway[GATEWAY_BUF_LENGTH];
 };

另外上面的清除代码存在问题,当规则不存在的时候,去执行的话会报错的。如果不清除的话,这个规则会越来越多,可能会带来一些问题。

//every time remove old rules
res |=  execIptables(target, "-t", "nat","-D","PREROUTING","-d",gateway,"-p", "tcp","--dport", "80","-j","DNAT","--to-destination",gatewayPort,NULL);  
res |=  execIptables(target, "-t", "nat","-D","PREROUTING","-d","192.168.42.129","-p", "tcp","--dport", "80","-j","DNAT","--to-destination","192.168.42.129:8080",NULL);

改为下面的,就不会报错,不是针对某条规则,而是把所有规则都清除,这样每次设置新规则前就会把所有的旧规则清除。

 //Delete the old IPTABLE rule
 res |= execIptables(target, "-F", "oem_fwd", NULL);
 res |= execIptables(target, "-t", "nat", "-F", "PREROUTING", NULL);

调用的方法很简单

INetworkManagementService mNetworkService = INetworkManagementService.Stub.asInterface(
				ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE));

try {
    mNetworkService.setPortNat(true); //打开
} catch (RemoteException e) {
			
}

try {
    mNetworkService.setPortNat(false); //关闭
} catch (RemoteException e) {
			
}


 

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐