自从换了 DELL 无线路由后,俺的 E61i 也出现无法正常连接 WLAN 上网的情况,而且和当初 Vista 的故障一样——可以访问 google,但大部分网站的连接不上,据以往经验,估计还是因为手机的 MTU 大于这个路由器最大支持的能力。
在网上搜了半天,发现在 S60 2nd 机型上,有一种改 genericnif.ini 的方法可以改变 MTU,但是到了 S60 3rd 机型,貌似就只能自己动手写程序了。
但 Symbian 下的 C++ 开发俺不熟啊,PyS60 没有接口啊,怎么办。正好在查相关开发信息的时候,发现 Symbian 出了一个 POSIX 兼容层(S60 平台上的称呼是 Open C),PyS60 社区有人据此移植了 libffi 和 _ctypes 过去;又有人包装了兼容 Python2.5 的 ctypes 提供一些外围函数;wiki 上还有一个 open c 下执行 setsockopt 的例子
于是安装了 Open C 的 SDK, 然后在它的文档里找到这么一段:
The following flags will work only on hardware because it is supported by the underlying symbian API.
- SIOCGIFMETRIC
- SIOCGIFMTU
- SIOCGIFNETMASK
- SIOCGIFBRDADDR
- SIOCGIFFLAGS
- SIOCGIFDSTADDR
- SIOCGIFPHYS
- SIOCSIFMETRIC
- SIOCSIFMTU
- SIOCSIFFLAGS
只要确认 SIOCSIFMTU 是可以用的就好办了,对一个熟练的 C 程序员且有一定 ctypes 使用经验的人来说,除了平台移植问题外,开发是没有什么困难的。
开发过程如下
- 在手机上安装 libffi、Ctypes (_ctypes.pyd)、ctypes
- 在 Windows 下安装 S60 C++ SDK,以及 Open C plugin。可以找到 pips_nokia_1_3_SS.sis 等一共 4 个 .sis 文件,都安装到手机上
- 找到 winscw\udeb\libc.lib,然后用 dumpbin.exe /exports libc.lib 寻找函数名对应的序号,PyS60 的 ctypes 必须得用序号去定位函数,比如这样
-
from _ctypes import *
-
from ctypes import *
-
libc = dlopen("libc.dll")
-
-
socket = lambda x,y,z: call_function(dlsym(libc, "339"), (x,y,z))
-
ioctl = lambda x,y,z: call_function(dlsym(libc, "180"), (x,y,z))
-
close = lambda x: call_function(dlsym(libc, "57"), (x,))
-
perror = lambda x: call_function(dlsym(libc, "255"), (x,))
-
dup = lambda x: call_function(dlsym(libc, "67"), (x,))
-
close(2)
-
dup(1) #把标准错误重定向到标准输出,这样 perror 就输出到标准输出了
-
-
ntohl = lambda x: call_function(dlsym(libc, "168"), (x,))
-
ntohs = lambda x: call_function(dlsym(libc, "169"), (x,))
-
print ntohs(1), ntohl(256) #简单的测试脚本
- 没有 dumpbin.exe 这个工具(VC++)的,也可以用 MinGW 的 nm.exe 来处理 libc.lib,只不过输出结果还需要再处理一下
-
f = open("c:\libc.txt")
-
idx = 1
-
for i in f.readlines():
-
if i.startswith("00000000 T "):
-
if i[11] == '.':
-
continue
-
print idx, i[11:].strip()
-
idx += 1
- 根据 SDK 的头文件,找出 SIOCSIFMTU 这样的常量定义写成 python 语句
-
IOCPARM_MASK = 0x1fff
-
_IOC = lambda inout,g,n,l: (inout | (l & IOCPARM_MASK) << 16 | ord(g) << 8 | n)
-
_IOR = lambda group, num, len: _IOC(0x40000000L, group, num, len)
-
_IOW = lambda group, num, len: _IOC(0x80000000L, group, num, len)
-
_IOWR = lambda group, num, len: _IOC(0x40000000L|0x80000000L, group, num, len)
-
-
SIOCGIFCONF = _IOWR('s', 3, SIZEOF_IFCONF)
-
SIOCGIFFLAGS = _IOWR('i', 17, SIZEOF_IFREQ)
-
SIOCGIFMTU = _IOWR('i', 51, SIZEOF_IFREQ)
-
SIOCSIFMTU = _IOW('i', 52, SIZEOF_IFREQ)
-
-
IFF_UP = 0x1
-
-
AF_INET = 0x0800
-
SOCK_DGRAM = 2
-
IPPROTO_UDP = 17
-
- 还要去找 struct ifconf, struct ifreq 是怎样定义结构体的,结构体大小是多少
-
SIZEOF_IFREQ = 84
-
SIZEOF_IFCONF = 8
-
IFREQ_IDXFMT = '52sH'
-
IFREQ_MTUFMT = '52sI'
-
IFREQ_FLAGSFMT = '52sBB'
-
autopadding = lambda start, size: start + str(size - calcsize(start)) + 's'
-
ifreq_idxfmt = autopadding(IFREQ_IDXFMT, SIZEOF_IFREQ)
-
ifreq_mtufmt = autopadding(IFREQ_MTUFMT, SIZEOF_IFREQ)
-
ifreq_flagsfmt = autopadding(IFREQ_FLAGSFMT, SIZEOF_IFREQ)
-
- 真正的操作很简单:
-
sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
-
-
result = create_string_buffer(SIZEOF_IFREQ * 20)
-
ifc = create_string_buffer(pack("II", SIZEOF_IFREQ * 20, addressof(result)), SIZEOF_IFCONF)
-
-
ret = ioctl(sockfd, SIOCGIFCONF, ifc)
-
ifc_len, ifc_buf = unpack("II", ifc.raw)
-
count = ifc_len / SIZEOF_IFREQ
-
for i in range(0, ifc_len, SIZEOF_IFREQ):
-
ifreq = result.raw[i:i+SIZEOF_IFREQ]
-
ifr_name, ifr_idx, padding = unpack(ifreq_idxfmt, ifreq)
-
ifr = create_string_buffer(ifr_name, SIZEOF_IFREQ)
-
ret = ioctl(sockfd, SIOCGIFFLAGS, ifr)
-
ifr_name, ifr_flags, ifr_flagshigh, padding = unpack(ifreq_flagsfmt, ifr)
-
if not (ifr_flags & IFF_UP):
-
print "%s: DOWN" % ifr_name
-
continue
-
ret = ioctl(sockfd, SIOCGIFMTU, ifr)
-
ifr_name, ifr_mtu, padding = unpack(ifreq_mtufmt, ifr)
-
print "%s: UP, MTU: %d" % (ifr_name, ifr_mtu)
-
if ifr_mtu > 1400: #凡是 MTU > 1400 的,都改成 1400
-
ifr = create_string_buffer(pack(ifreq_mtufmt, ifr_name, 1400, padding), SIZEOF_IFREQ)
-
if -1 == ioctl(sockfd, SIOCSIFMTU, ifr): foo = perror("why")
-
- 但...为什么 perror 会报 Permission denied 呢? 后来查了半天,发现是 SIOCSIFMTU 需要有 NetworkControl 权限。于是去 这里 申请一个自己手机 IMEI 对应的证书(免费的),证书对应的 key 在这里
- 用 ensymble 这个工具来给 libffi/Ctypes/PythonScriptShell(unsigned_testrange) 重新签名,加上 NetworkControl 权限
ensymble.py signsis --cert=20081008001.cer --privkey=dospy.key --passphrase="" --execaps="ALL-TCB-ALLFILES-DRM" PythonScriptShell_1_4_4_3rdEd_unsigned_testrange.SIS imei_PythonScriptShell_1_4_4_3rdEd.sisensymble.py signsis --cert=20081008001.cer --privkey=dospy.key --passphrase="" --dllcaps="ALL-TCB-ALLFILES-DRM" libffi.sisx imei_libffi.sis
ensymble.py signsis --cert=20081008001.cer --privkey=dospy.key --passphrase="" --dllcaps="ALL-TCB-ALLFILES-DRM" ctypes.sisx imei_ctypes.sis
- 这下大功告成,我....我终于又可以在家里坐在马桶上用手机上无线了!!
评论
很不错,但是我没看
很不错,但是我没看明白。
为何改客户端呢?
为何不使用能改MTU的路由器呢?比如DD-WRT Compatible的
http://www.dd-wrt.com/wiki/index.php/Supported_Devices
我的这个 dd-wrt
我的这个 dd-wrt 不知道为什么,MTU 无法调高
太专业了!我看不懂,但我也遇到这个问题,能不能帮我一下!Q
太专业了!我看不懂,但我也遇到这个问题,能不能帮我一下!QQ:34944239