分类 Raspberry Pi 下的文章

某日发现了提供免费SSL证书的网站——www.sslforfree.com,立马手动申请了个证书来玩。部署以后,非常感动,重点是Chrome和FireFox都支持!曾在某知名网站申请过免费证书,总是申请失败。心灰意冷,本来准备买个廉价的付费版,幸好发现这网站!

免费虽好,但有效期只有3个月,而且到期后只能手动更新证书。不过找到提供该免费证书的网站——Let’s Encrypt(https://letsencrypt.org),以及该网站提供的自动更新证书的开源软件certbot(https://certbot.eff.org)。certbot首页就有安装教程,对应不同HTTP服务和操作系统。安装完毕后,执行以下命令,就可以获取证书了

#验证域名并获取SSL证书
sudo certbot certonly --webroot -w /var/www/example -d example.com -d www.example.com

最后,设置个自动更新,就安枕无忧了。

PS.既然有了免费的证书,何不部署个HTTP 2.0?

说起物联网,最早接触的是Arduino。可是入手Arduino Mega后,一直没做出什么好玩的东西,以致Mega丢在角落里好几年。近来在部门经理的怂恿下,入手了NodeMCU,做了个检测盆栽的土壤湿度。本来想在Github开个项目,但是这第一版太简单了,就随便记录一下。

NodeMCU的特点,在于自带WiFi(还可以使用AP模式),而且价格便宜。当然,缺点也在WiFi,比较耗电。为了省电,可以设置网卡的硬件参数(wifi.setphymode()),关闭WiFi(wifi.sleeptype()),甚至是整块板进入深度睡眠模式(node.dsleep(), rtctime.dsleep())。其开源、基于Lua的开发简单、尺寸迷你等优点,也是其好玩之处。

这个项目很简单,就是NodeMCU连个土壤湿度检测模块,定时(10分钟)获取一次数据,并上传到服务器。服务器是采用树梅派,用Python3,利用Flask框架,写了个HTTP服务,用于保存提交的数据,并做数据展示。数据都保存在MySQL数据库。

NodeMCU上的init.lua

print("\n")
print("ESP8266 Started")

dsleepTime = 630000000
ssid = "my-wifi"
password = "123456"
serverIp = "192.168.1.1"
serverPort = 8080

wifi.setphymode(wifi.PHYMODE_N)
wifi.setmode(wifi.STATION)
wifi.sta.config(ssid, password)
tmr.alarm(0,1000,1,function() 
    curIp = wifi.sta.getip()
    if curIp ~= nil then
        print("Current IP:"..curIp)
        tmr.stop(0)
        sendData(readValue(0, 5))
    end
end)

--发送数据到服务器    
function sendData(value)
    print("sendData start")
    conn = net.createConnection(net.TCP, false)
    conn:on("receive", function(conn, pl)
        print("Send data with value: "..value)
        print("Result:\n"..pl)
        conn:close()
        conn = nil
        wifi.sta.disconnect()
        
        node.dsleep(dsleepTime)
    end)
    conn:connect(serverPort, serverIp)
    conn:send("GET /gather/?key=123456&value="..value
        .." HTTP/1.1\r\nHost: " + serverIp + "\r\n"
        .."Connection: keep-alive\r\nAccept: */*\r\n\r\n")

    --connect time out
    tmr.alarm(1,5000,1,function()
        print("[error]connet to server time out")
        --conn:close()
        --conn = nil
        --wifi.sta.disconnect()
        tmr.stop(1)
        
        node.dsleep(dsleepTime)
    end)
end

--读取土壤湿度数据
--一次有效的采样,最少取3次数据,去掉最大值和最小值,再计算平均值
function readValue(adcPin, readTimes)
    if readTimes <= 2 then
        readTimes = 3
    end
    
    local curValue = 0
    local maxValue = 0
    local minValue = 0
    local sumValue = 0
    
    for i = 0, readTimes - 1 do
        curValue = adc.read(adcPin)
        sumValue = sumValue + curValue
        if maxValue < curValue then
            maxValue = curValue
        end
        if (minValue == 0) or (minValue > curValue) then
            minValue = curValue
        end
    end
    sumValue = sumValue - maxValue - minValue
    curValue = math.floor(sumValue / (readTimes - 2))

    return curValue
end

服务器上的HTTP服务,文件site.ini

[uwsgi]
socket = 127.0.0.1:8080
processes = 2
threads = 1
plugins = python3
master = true
pythonpath = /opt/work/web_gather
chdir = /opt/work/web_gather
module = site
callable = app
chmod-socket = 777
memory-report = true

服务器上的HTTP服务,文件site.py

#!/bin/python3

from datetime import datetime
from flask import Flask, request, abort, render_template
from flask.ext.sqlalchemy import SQLAlchemy
from sqlalchemy import desc

app = Flask(__name__)
app.config['SECRET_KEY'] = '123456'

app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:123456@127.0.0.1/web_gather'
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True

app.config['HUMIDITY_MIN'] = 740

db = SQLAlchemy(app)

class Humidity(db.Model):
    __tablename__ = 'humidity'
    id = db.Column('id', db.Integer, primary_key=True)
    createDate = db.Column('create_date', db.DateTime)
    sourceValue = db.Column('source_value', db.Integer)
    value = db.Column('value', db.Integer)

    def __repr__(self):
        return '<Humidity % r>' % self.id

@app.route('/', methods=['GET', 'POST'])
def index():
    key = request.args.get('key', '')
    
    if key == app.config['SECRET_KEY']:
        sourceValue = request.args.get('value', '0')
        value = (1024 - int(sourceValue)) / 1024 * 100
        curTime = datetime.today()
        humidity = Humidity(createDate=curTime, sourceValue=sourceValue, value=value)
        db.session.add(humidity)
        db.session.commit()
        return 'true'
    
    humidities = Humidity.query.order_by(desc(Humidity.createDate)).limit(500)
    return render_template('humidity.html', humidities=humidities)

if __name__ == '__main__':
    app.run(debug=False, host='127.0.0.1', port=8080)
    #app.debug = False
    #app.run()

MySQL的表定义

CREATE TABLE `humidity` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `create_date` datetime DEFAULT NULL,
  `source_value` int(11) DEFAULT NULL,
  `value` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2554 DEFAULT CHARSET=utf8

本来树莓派(Raspberry Pi 2 Model B)在家中安静地连上Internet,并定时下载网盘上的电脑,就已经很实用了。但是发现花生壳推出针对树莓派的内网端口及socket5映射软件,就试玩了一下:

树莓派花生壳(内网版)攻略
http://service.oray.com/question/2680.html

该软件的作用就是把处于内网环境的树莓派的端口或socket5映射到外网,实现外网直接访问树莓派,可玩性一下子提高几个等级。但是由于是免费版,拥有N多限制,包括流量、响应时间、可映射端口数量等等。查了相关资料,有网友说其实是利用了ssh隧道来实现的,既然这样,为什么不自己部署一个?安全性及相关限制都可以自己设置的。

所需条件
1)外网可访问的服务器或VPS(下文简称VPS),并且可运行SSH服务,最好是Linux系统
2)可访问VPS的树莓派(下文简称RPi)
3)域名,非必须

原理
RPi通过ssh客户端主动连接VPS的ssh服务,并开启VPS上的端口,映射到PRi的端口。为了方便创建连接,VPS上的ssh服务需要无密码访问,这里采用了证书登录。由于网络断掉后,ssh客户端的连接也会断掉,所以采用autossh代替ssh客户端。

关于ssh服务
参考以下文章:

1)此文章说明了很多ssh服务的安全相关配置,可用作VPS上的ssh服务配置参考。
SSH 安全性和配置入门
https://www.ibm.com/developerworks/cn/aix/library/au-sshsecurity/

2)详细说明了如果配置证书登录ssh服务
ssh证书登录(实例详解)
http://www.cnblogs.com/ggjucheng/archive/2012/08/19/2646346.html

3)介绍ssh隧道的使用
SSH Tunnel解决无公网IP 80被封等问题
http://blog.bbzhh.com/index.php/archives/60.html

4)介绍用autossh的使用
autossh在Ubuntu上的配置 ssh 隧道
http://yuanxiao.sinaapp.com/pages/122/132/377/article_index.html

配置
1)RPi上,创建证书。需要输密码时,直接按回车跳过。

ssh-keygen -t rsa

这里采用了rsa方式,默认生成两个文件:公钥(id_rsa.pub)和私钥(id_rsa)。把公钥(id_rsa.pub)文件,上传到VPS。

2)VPS上,生成授权文件,把自制的公钥都放入去。

cat id_rsa.pub >> ~/.ssh/authorized_keys

3)VPS上,安装并配置sshd。配置文件路径/etc/ssh/sshd_config,比较重要的配置项如下:

# 禁用root账户登录,增加安全性,非必要
PermitRootLogin no

# 是否让 sshd 去检查用户家目录或相关档案的权限数据,
# 避免使用者将某些重要文件的权限设错,而可能导致一些问题发生。
# 例如使用者的 ~/.ssh/ 权限设错时,某些特殊情况下会不许用户登入
StrictModes no

# 是否允许用户自行使用成对的密钥系统进行登入行为,仅针对 version 2。
RSAAuthentication yes
PubkeyAuthentication yes
AuthorizedKeysFile      %h/.ssh/authorized_keys

# 禁用密码登录,增加安全性
PasswordAuthentication no

配置完成后,记得重启sshd服务,使配置生效。

4)VPS上,安装TCP Echo服务,供autossh判断网络隧道连通情况。默认运行端口为7。

apt-get install openbsd-inetd

安装后,需要修改配置文件/etc/inetd.conf,在文件末尾增加以下内容:

echo        stream    tcp    nowait    root    internal

然后就可以启动服务了。

5)RPi上,用autossh连接VPS并进行端口映射

# 安装autossh
sudo apt-get install autossh

# 创建port_forward.sh文件
cat >> port_forward.sh << EOF
#!/bin/bash
export AUTOSSH_PIDFILE=/var/run/autossh.pid
export AUTOSSH_POLL=60
export AUTOSSH_FIRST_POLL=30

# -f是后台运行
# -M 后台管理端口:VPS的echo服务端口
# -NR VPS端口:本机地址:本机端口,本例把RPi端口80映射到VPS端口8080
# 后面的就是登录相关信息
autossh -f -M 4567:7 -NR 8080:localhost:80 user@vps_name.net -p 22 -i /path/to/id_rsa 

# 设置可执行权限
chmod 755 port_forward.sh

# 运行
./port_forward.sh

6)最后就可以直接访问http://vps_name.net:8080就可以访问RPi端口80上的服务了。

经过一段时间的研究和实践,终于把树莓派(Raspberry Pi 2 Model B)作为家庭服务器用起来了。简单来说,就是树莓派作为无线路由、网盘下载机和提供DLNA服务,Chromecast作为播放器在电视机播放多媒体内容,用Android手机作为遥控器控制一切。

树莓派
1)无线路由
参考前面的Bolg
Setup Raspberry Pi 2 as Wireless Router
http://blog.foxail.org/index.php/archives/661/

2)网盘下载机
这里选择了百度盘,其空间足够大(免费2TB空间),有Linux的客户端(第三方开发的)。树莓派上安装客户端:
https://github.com/GangZhuo/BaiduPCS

然后设置个定时同步下载,就可以自动下载电影了。下载前,还会上传当前的下载状态及硬盘空间使用情况到网盘,方便查看。

3)安装USB存储设备
参考以下文章,增加自动挂载USB存储设备:
树莓派自动挂载usb移动存储设备
http://rpi.linux48.com/usbstorage.html

该脚本在Debian 8上有问题,修改了一下,如下:

KERNEL!="sd*", GOTO="media_by_label_auto_mount_end"
SUBSYSTEM!="block",GOTO="media_by_label_auto_mount_end"
IMPORT{program}="/sbin/blkid -o udev -p %N"
ENV{ID_FS_TYPE}=="", GOTO="media_by_label_auto_mount_end"
ENV{ID_FS_LABEL}!="", ENV{dir_name}="%E{ID_FS_LABEL}"
ENV{ID_FS_LABEL}=="", ENV{dir_name}="Untitled-%k"
ACTION=="add", ENV{mount_options}="relatime,sync"
ACTION=="add", ENV{ID_FS_TYPE}=="vfat", ENV{mount_options}="iocharset=utf8,uid=1000,gid=1000,umask=000"
ACTION=="add", ENV{ID_FS_TYPE}=="ntfs", ENV{mount_options}="iocharset=utf8,uid=1000,gid=1000,umask=000"
ACTION=="add", RUN+="/bin/mkdir -p /media/%E{dir_name}", RUN+="/bin/mount -o $env{mount_options} /dev/%k /media/%E{dir_name}"

ACTION=="remove", ENV{dir_name}!="", RUN+="/bin/umount -l /media/%E{dir_name}", RUN+="/bin/rmdir /media/%E{dir_name}"
LABEL="media_by_label_auto_mount_end"

如果是U盘,直接插上就可以了。但如果是移动硬盘,要考虑树莓派的电流不足的问题,会导致硬盘mount不上,或者不能写入数据。不得不换了个2A的USB电源和有源USB Hub。现在算是能顺利运行了,但还是想简化电路和连接方式,后面再考虑。

4)DLNA服务
装个minidlna就可以了。如果需要识别rmvb文件,需要改源码并重新编译。参考这个:
Raspiberry Pi安装minidlna1.1.4并支持rmvb
http://raspi.vanabel.info/wordpress/?p=92

Chromecast
国内真的不推荐用这个,因为要连上google才能使用。为了连上google,花了很多时间去研究,最后采用简单的方案:设置某个固定ip采用代理。本文前面的“无线路由”配置中,包含了相关配置。

关于ROOT。感觉不获取ROOT权限,也不会影响使用。不过个人习惯,ROOT后会增加更多的可能性。并且,升级到最新版系统后,目前不能ROOT了。

检查是否可ROOT:[INFO] Rootable Serial Numbers
http://forum.xda-developers.com/showthread.php?t=2537022

ROOT教程,需要借助硬件Leonardo Pro Micro(基于芯片ATmega32U4):Chromecast ROOT
http://raspberrypihelp.net/motornavigatie-dutch/62-chromecast-root

Android手机
目前安装了connectbot,作为SSH客户端连接树莓派。项目源码:
https://github.com/connectbot/connectbot

DLNA方面,采用BubbleUPnP,可以用本机播放树莓派的多媒体内容,可以把树莓派的多媒体内容推送到Chromecast。Google Play下载:
https://play.google.com/store/apps/details?id=com.bubblesoft.android.bubbleupnp

垂涎于Raspberry Pi 2 Model B的四核性能及低廉的价格,待其跌到200RMB左右时,终于还是入手了一个。

装完Raspbian,立马试了XMBC/Kodi媒体中心。效果还是跟以前一样,不尽人意。无它的,就是视频和音频的编码支持不完善,导致很多视频格式都不支持。(为了解决电视机播放手机视频的问题,入手了Chromecast。但Chromecast也不是个省油的灯,后面再说吧。)于是Pi只能放在后台,作为路由和服务器来使用了。

需求:增强无线网络覆盖范围,即作为桥接的无线路由,手机和Chromecast可以通过其上网,特别Chromecast要可以访问Google。

硬件准备:
1)Raspberry Pi 2 Model B,1台。
2)16GB的TF卡,1个,刷上Raspbian。
3)无线网卡,2个(磊科NW338和水星MW150UH),并且有一个(水星MW150UH)支持Ad-hoc功能,即能开启无线热点功能。
4)USB风扇,1个,用于扇热。温度过高的话,会导致硬件性能下降,就是网络传输速度会变慢。

软件安装及设置:
1)初始化准备
参考以下文章,设置好无线网卡的接口名称。否则每次开机,每个无线网卡的接口名称都可能会改变。
Setting Network Interface Name on Raspbian
http://www.foxail.org/blog/index.php/archives/532/

我设置了“磊科 NW338”为wlan0,“水星 MW150UH”为wlan1,。wlan0用于连接无线路由,wlan1用于开启无线热点。

2)配置无线网卡
MW150UH 可能还需要第三方驱动,这里是已编译好的驱动文件:
(UPDATE) Drivers for TL-WN725N V2 - 3.6.11+ -> 4.1.xx+
https://www.raspberrypi.org/forums/viewtopic.php?f=28&t=62371

确定网卡可以运行后,修改 /etc/network/interfaces 为以下内容:

auto lo
iface lo inet loopback
 
auto eth0
allow-hotplug eth0
iface eth0 inet manual
 
auto wlan0
allow-hotplug wlan0
iface wlan0 inet manual
wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf
 
auto wlan1
#allow-hotplug wlan1
iface wlan1 inet static
  address 192.168.11.1
  netmask 255.255.255.0

3)hostapd
安装命令:

sudo apt-get install hostapd

由于采用了 水星 MW150UH ,芯片为RTL8188EU,所以参考了以下文章,使用相关已编译的 hostapd 文件来替换原来的 /usr/sbin/hostapd 。
Access Point (AP) for TP-Link TL-WN725N V2
https://www.raspberrypi.org/forums/viewtopic.php?f=91&t=54946

然后备份配置文件/etc/hostapd.conf,并改为如下内容:

interface=wlan1
driver=rtl871xdrv
ssid=rpi-ap
channel=1
hw_mode=g
auth_algs=1
wpa=2
wpa_passphrase=123456
wpa_key_mgmt=WPA-PSK
rsn_pairwise=CCMP
#macaddr_acl=1
#accept_mac_file=/etc/hostapd/hostapd.accept

4)dnsmasq
安装命令:

sudo apt-get install dnsmasq

然后备份配置文件/etc/dnsmasq.conf,并改为如下内容:

interface=wlan1
bind-interfaces
#except-interface=lo
dhcp-range=192.168.11.100,192.168.11.150,255.255.255.0,6h
 
# chromecast bind ip
dhcp-host=aa:bb:cc:dd:ee:ff,my-chromecast,192.168.11.10

5)iptables
设置iptables,实现转发:

sudo iptables -F
sudo iptables -t nat -A POSTROUTING -s 192.168.11.0/24 -o wlan0 -j MASQUERADE
sudo iptables -A FORWARD -s 192.168.11.0/24 -o wlan0 -j ACCEPT
sudo iptables -A FORWARD -d 192.168.11.0/24 -m conntrack --ctstate ESTABLISHED,RELATED -i wlan0 -j ACCEPT

保存iptables配置:

sudo iptables-save > /etc/iptables.rules

设置系统启动时自动加载iptables的配置。修改文件/etc/network/if-pre-up.d/iptables为以下内容:

#!/bin/sh
/sbin/iptables-restore < /etc/iptables.rules

保存该文件后,设置其权限:

sudo chmod +x /etc/network/if-pre-up.d/iptables

6)设置转发
修改文件 /etc/sysctl.conf ,找到net.ipv4.ip_forward,改为:

net.ipv4.ip_forward=1

7)解决Chromecast连上google的问题
为解决这个问题,花了不少时间去探索和尝试。Chromecast就算ROOT了,也不能安装shadowsocks客户端。解决方法是,固定 Chromecast 的IP地址,该IP的所有连接走 Raspberry Pi 上 shadowsocks-libev 的客户端。由于国内支持 Chromecast 的应用几乎没有,所以没必要再考虑访问国内网站的问题。

具体部署步骤,shadowsocks-libev 的官方说明已经详细说明了:
https://github.com/shadowsocks/shadowsocks-libev#advanced-usage

简单记录一下我的操作:
a)安装shadowsocks-libev,主要用到 ss-redir 。直接去GitHub下载源码编译安装吧:
https://github.com/shadowsocks/shadowsocks-libev

b)设置iptables:

# 建立新的链路
sudo iptables -t nat -N SHADOWSOCKS

# 跳过Shadowsocks服务器IP,这里假设为123.123.123.123
sudo iptables -t nat -A SHADOWSOCKS -d 123.123.123.123 -j RETURN

# 跳过内网IP地址
sudo iptables -t nat -A SHADOWSOCKS -d 0.0.0.0/8 -j RETURN
sudo iptables -t nat -A SHADOWSOCKS -d 10.0.0.0/8 -j RETURN
sudo iptables -t nat -A SHADOWSOCKS -d 127.0.0.0/8 -j RETURN
sudo iptables -t nat -A SHADOWSOCKS -d 169.254.0.0/16 -j RETURN
sudo iptables -t nat -A SHADOWSOCKS -d 172.16.0.0/12 -j RETURN
sudo iptables -t nat -A SHADOWSOCKS -d 192.168.0.0/16 -j RETURN
sudo iptables -t nat -A SHADOWSOCKS -d 224.0.0.0/4 -j RETURN
sudo iptables -t nat -A SHADOWSOCKS -d 240.0.0.0/4 -j RETURN

# 其他IP跳转到 ss-redir 的代理端口
sudo iptables -t nat -A SHADOWSOCKS -p tcp -j REDIRECT --to-ports 12345

# 设置 Chromecast 利用代理访问外网
sudo iptables -t nat -A PREROUTING -s 192.168.11.10/32 -p tcp -j SHADOWSOCKS

设置完,记得保存iptables配置:

sudo iptables-save > /etc/iptables.rules

c)配置并启动 ss-redir,记得本地端口要设置为与上面的一致。

ss-redir -u -c /etc/config/shadowsocks.json -f /var/run/shadowsocks.pid

最后,重启一下Raspberry Pi ,应该所有配置都会生效了。

折腾RaspberryPi已经一段日子了,期间还因接错GPIO的线而报废了一个。重新入手了英国版后,却没再怎么玩了。

昨天搜索相关资料时,找到一个很简单的关机按钮方法(http://spaceblogs.org/flipdot/2013/06/shut-down-your-raspberry-pi-on-button-press-and-add-reset-function/)就是利用2.0版新增的P5接口,直接连上个开关,实现关机功能。根据该文章,还可以直接在P6上连个按钮,实现重启。

RaspberryPi针脚相关的资料,可以参考:http://elinux.org/RPi_Low-level_peripherals

今晚测试了一下,该方法可行。然后就是折腾了整晚的钻孔……还是入手个小电钻吧。期待明天可以接线。

PS。关机按钮的作用是保护SD卡,以免强制断电关机后,发生数据损坏或丢失的情况~

由于想把Raspberry Pi改造成无线中继,所以插了两个USB网卡上去。后来发现系统启动后,无线网卡的名称每次都不一定相同,以致启动桥接服务的shell脚本出错。但笔记本上的Lubuntu 12.10却是无线网卡每次的名称都不变。同样是基于Debian,Raspbian也应该可以实现无线网卡名称固定(其实就是跟MAC绑定网络接口名称)。翻查了资料,还是Debian的Wiki比较靠谱。见:http://wiki.debian.org/udev

最后还是比较懒,从Lubuntu 12.10上复制文件 /etc/udev/rules.d/70-persistent-net.rules 到Raspbian上,再改改来用就可以了。该文件修改如下:

## begin the file /etc/udev/rules.d/70-persistent-net.rules #################
# USB Ethernet (usb)
#SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="aa:bb:cc:dd:ee:ff", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="eth*", NAME="eth1"

# USB device 0x0cf3:0x1006 (Mercury MW54U)
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="aa:bb:cc:dd:ee:ff", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="wlan*", NAME="wlan0"

# USB device 0x0bda:0x8171 (Netcore NW338)
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="aa:bb:cc:dd:ee:ff", ATTR{dev_id}=="0x0", ATTR{type}=="1", KERNEL=="wlan*", NAME="wlan1"
## end the file /etc/udev/rules.d/70-persistent-net.rules #################

其中aa:bb:cc:dd:ee:ff要替换成网卡对应的MAC码。

入手Raspberry Pi Model B (512MB) 已经一段时间了。装上官方的Raspbian,感觉跟手头上的AO522没区别。同样是基于Debian,同样是LXDE桌面。也试了下Xbian (XMBC + Raspbian),感觉非常棒,只是菜单操作有点卡。可惜手上的SD卡空间不够大,否则,还可以玩玩Android。

玩过后,正是按计划进行。首先是作为无线路由来使用。其实就跟Lubuntu共享WiFi一样,测试过程中,还改良了一下以前写的sharewifi脚本(以前的版本:http://www.foxail.org/blog/?p=490)。但是还有一个问题,执行该脚本的stop操作后,系统不会把建立的mon.wlan0接口关闭,需要重启系统,wifi才能从共享模式变成正常的接收模式。

1)sharewifi脚本,设置共享wifi,我放在/opt下,内容如下:(首先要安装hostapd和dnsmasq,执行sudo apt-get install hostapd dnsmasq即可)

### begin the file /opt/sharewifi #########################################
#!/bin/sh

# Setup wireless AP, share the Internet from interface0 to interface1
# USAGE: sharewifi [ start | stop ] interface0 interface1
# EXAMPLE: sharewifi start wlan1 wlan0

help( )
{
    cat << HELP
    Setup wireless AP, share the Internet from interface0 to interface1
    USAGE: sharewifi [ help | start | stop ] interface0 interface1
    EXAMPLE: sharewifi start wlan1 wlan0

    List clients:
    cat /var/lib/misc/dnsmasq.leases
    HELP
    exit 0
}

start( )
{
    echo Starting share wifi ......
    echo Share Internet $port_in to $port_out

    # Configure iptable rules
    iptables -F
    iptables -t nat -A POSTROUTING -s $ip_prefix.0/24 -o $port_in -j MASQUERADE
    iptables -A FORWARD -s $ip_prefix.0/24 -o $port_in -j ACCEPT
    iptables -A FORWARD -d $ip_prefix.0/24 -m conntrack --ctstate ESTABLISHED,RELATED -i $port_in -j ACCEPT

    # Log the message of route
    #iptables -A INPUT -m conntrack --ctstate NEW -p tcp --dport 80 -j LOG --log-prefix "NEW_HTTP_CONN: "

    # Save iptable rules
    sh -c "iptables-save > /etc/iptables.rules"

    # Configure hostapd
    hostapd_conf=/etc/hostapd/hostapd.conf
    [ -f $hostapd_conf ] && rm $hostapd_conf
    echo >> $hostapd_conf interface=$port_out
    echo >> $hostapd_conf driver=nl80211
    echo >> $hostapd_conf ssid=foxrpi-ap
    echo >> $hostapd_conf channel=1
    echo >> $hostapd_conf hw_mode=g
    echo >> $hostapd_conf auth_algs=1
    echo >> $hostapd_conf wpa=3
    echo >> $hostapd_conf wpa_passphrase=1234567890
    echo >> $hostapd_conf wpa_key_mgmt=WPA-PSK
    echo >> $hostapd_conf wpa_pairwise=TKIP CCMP
    echo >> $hostapd_conf rsn_pairwise=CCMP
    chmod 755 $hostapd_conf

    # Configure /etc/dnsmasq.conf
    dnsmasq_conf=/etc/dnsmasq.conf
    [ -f $dnsmasq_conf ] && rm $dnsmasq_conf
    echo >> $dnsmasq_conf interface=$port_out
    echo >> $dnsmasq_conf bind-interfaces #这个是只监听wlan0,没有之会检测所有卡
    echo >> $dnsmasq_conf except-interface=lo
    echo >> $dnsmasq_conf dhcp-range=$ip_prefix.10,$ip_prefix.110,6h #设置dhcp地址范
    chmod 755 $dnsmasq_conf

    # Enable routing
    sysctl net.ipv4.ip_forward=1

    #killall named
    /etc/init.d/hostapd stop
    ifconfig $port_out $ip_prefix.1
    hostapd -B $hostapd_conf
    /etc/init.d/dnsmasq restart

    echo Sucess share wifi

    exit 0
}

stop( )
{
    echo Stopping share wifi ......
    echo Stop share Internet $port_in to $port_out

    # Configure iptable rules
    iptables -F

    # Log the message of route
    #iptables -A INPUT -m conntrack --ctstate NEW -p tcp --dport 80 -j LOG --log-prefix "NEW_HTTP_CONN: "

    # Save iptable rules
    sh -c "iptables-save > /etc/iptables.rules"

    # Configure hostapd
    hostapd_conf=/etc/hostapd/hostapd.conf
    [ -f $hostapd_conf ] && rm $hostapd_conf

    # Configure /etc/dnsmasq.conf
    dnsmasq_conf=/etc/dnsmasq.conf
    [ -f $dnsmasq_conf ] && rm $dnsmasq_conf

    # Enable routing
    sysctl net.ipv4.ip_forward=0

    #killall named
    /etc/init.d/hostapd stop
    /etc/init.d/dnsmasq stop
    ifconfig $port_out down
    ifconfig $port_out del $ip_prefix.1
    ifconfig $port_out up

    echo Sucess stop share wifi

    exit 0
}

ip_prefix=192.168.23 #wifi路由所用网段

#port_in is the network interface which connected to Internet, and default wlan1.
port_in=wlan1

#port_out is the network interface which will be setup AP, and default wlan0.
port_out=wlan0

if [ -n "$2" ]; then
    port_in=$2

    if [ -n "$3" ]; then
        port_out=$3
    fi
fi

case "$1" in
"help" )
    help ;;
"start" )
    start ;;
"stop" )
    stop ;;
*)
    help ;;
esac
### end the file /opt/sharewifi #########################################

2)设置开机自动启动,先编写开机启动的脚本。在/etc/init.d下建立文件sharewifi,文件内容如下:

### begin the file /etc/init.d/sharewifi ######################################### 
#!/bin/sh 
### BEGIN INIT INFO # Provides: sharewifi 
# Required-Start: $syslog 
# Required-Stop: $syslog 
# Default-Start: 2 3 4 5 
# Default-Stop: 0 1 6 
# Short-Description: starts the Wireless AP server 
# Description: starts the Wireless AP using start-stop-daemon 
### END INIT INFO
sharewifi=/opt/sharewifi
NAME=sharewifi
DESC=sharewifi

# Include nginx defaults if available
if [ ! -f $sharewifi ]; then
    echo "Can't find the file $sharewifi"
    exit 1
fi

case "$1" in
start)
    echo -n "Starting $DESC: "
    sh $sharewifi start eth0 wlan0
    echo "$NAME."
    ;;

stop)
    echo -n "Stopping $DESC: "
    sh $sharewifi stop eth0 wlan0
    echo "$NAME."
    ;;

restart)
    echo -n "Restarting $DESC: "
    sh $sharewifi stop eth0 wlan0
    sleep 1
    sh $sharewifi start eth0 wlan0
    echo "$NAME."
    ;;

*)
    echo "Usage: $NAME {start|stop|restart}" >&2
    exit 1
    ;;
esac

exit 0
### end the file /etc/init.d/sharewifi #########################################

3)执行以下命令,即可设置开机启动:

sudo update-rc.d /etc/init.d/sharewifi defaults