1 介绍
1.1 适用人员
适用于使用shell脚本进行openwrt功能开发的开发人员
1.2 开发环境
siflower SDK,siflower硬件平台
1.3 相关背景
VLAN(Virtual LAN),即“虚拟局域网”,可以使用户自由地根据实际需要分割广播域,在openwrt上可通过配置network文件实现vlan划分。但并不是每一个用户都了解vlan划分流程,因此路由器在面向普通用户的时候,应当具备方便、简洁的特点:用户甚至不需要了解什么是vlan,怎么区分wan/lan口;随便插上网线就能实现上网功能。
1.4 功能概述
wan/lan 自适应具有三种配置wan口的功能:开机自动配置、立即自动配置、立即手动配置。自动配置时会根据当前的网线连接情况划分wan/lan口。需要更换wan口时,只需将网线插入路由器需要配置的端口,运行脚本自动配置,就会自动将wan重新划分到该端口上。配置完成后,路由器依旧可以正常上网。
2 项目引用
3 开发详情
3.1 基础指令及原理
3.1.1 uci指令
以太网相关配置是用network文件存储的,路径为/etc/config/network。我们可以直接手动修改该文件,也可以通过在串口下直接键入uci指令修改network配置,还可以在shell中编写程序执行uci指令实现一定的功能。uci常用指令有:
| 指令 | 作用 |
|---|---|
| uci get network.lan.ipaddr | 获取lan节点下的ip选项的值 |
| uci set network.test=interface | network里加一个interface类型的节点 |
| uci set network.test.a=”abc” | 向test节点下的a选项赋值(不存在a则创建此选项) |
| uci set network.@switch_vlan[0].vlan=1 | 将第一个switch_vlan节点下的vlan值改为1 |
| uci delete network.test.a | 删除a选项 |
| uci delete network.test | 删除test节点 |
| uci commit | 保存修改 |
更多地了解如何使用uci指令配置config文件,请参考config文件配置手册或openwrt uci官方文档
3.1.2 vlan划分
VLAN(Virtual LAN),即“虚拟局域网”,可以使用户自由地根据实际需要分割广播域。AC22镜像network配置中vlan相关部分如下所示:
config interface 'lan' option ifname 'eth0.1' option force_link '1' option macaddr '10:16:88:3a:8d:f4' option type 'bridge' option proto 'static' option ipaddr '192.168.4.1' option netmask '255.255.255.0' option ip6assign '60' option group '0' option rps_cpus '2' option xps_cpus '2' config interface 'wan' option ifname 'eth0.2' option force_link '1' option macaddr '10:16:88:3a:8d:f5' option rps_cpus '1' option xps_cpus '0' option proto 'dhcp' ... config switch option name 'switch0' option reset '1' option enable_vlan '1' config switch_vlan option device 'switch0' option vlan '1' option ports '0 1 2 3 16t' config switch_vlan option device 'switch0' option vlan '2' option ports '4 16t'
lan所在的eth0.1被划到了vlan 1,对应port 0 1 2 3,也就是第1,2,3,4号口。wan所在的eth0.2被划到了vlan 2,对应第5号口。如果想要将5号口划为lan,3号口划为wan,则只需将wan对应的ports改为’2 16t’,lan对应的ports改为’0 1 3 4 16t’。uci对应的指令为:
uci set network.@switch_vlan[0].ports='0 1 3 4 16t' uci set network.@switch_vlan[1].ports='2 16t' uci commit
更多vlan划分的内容,请参考以太网wan-lan划分指南。如果能划分出5个vlan分别对应5个路由器端口,那么就能通过向外发送dhcp discover数据包的方法寻找dhcp服务器,从而知道哪一个口应该成为wan口。
3.1.3 dhcp协议
详细了解dhcp协议过程,请参考维基百科-DHCP协议。wan/lan自适应流程会利用到其中的前两步:
1、从某端口向dhcp server发送discover数据包。
2、若dhcp server收到discover数据包,则会回复一个offer包。
至此,若收到offer包,即可判断该端口与dhcp server相连,可配置为路由器的wan口。
3.2 功能设计流程
3.2.1 自启动实现
openwrt在开机启动时,会自动执行路径为/etc/rc.local的文件。因此在rc.local的末尾部分加上运行某脚本的指令,那么这个脚本就会实现开机自启动。例如:现有一个可执行脚本auto_adapt.sh放在/bin目录下。如果在rc.local最后(exit 0之前)添加./bin/auto_adapt.sh,这个脚本就会开机自启动。若在脚本中加入对uci参数的判断(例如network.lan.auto_adapt参数为1时才执行脚本),则可以实现由用户随时设置是否开机自启动。
3.2.2 wan/lan自适应流程
1 下发自动配置指令或路由器开机启动时,将network备份并重新划分5个vlan分别对应5个端口,替代原有配置。
2 利用发包工具send从这5个端口向外发送dhcp discover包,若收到dhcp server的回复,则说明检测到wan口。
3 恢复network原有配置,并将检测到的端口配置为wan口。下发手动配置指令时直接进行这一步。
4 重启网络,自适应配置完成,路由器恢复上网功能。
graph TD A(自动配置指令) -->B(划分vlan) --> C(send检测wan口) --> D(配置WAN口) --> E(重启网络) F(开机启动) -->G(rc.local判断是否自动配置) --> |yes| B I(手动配置指令) -->D
3.3 代码实现(以AC22为例)
实现wan/lan自适应的demo放在package/network/services/auto_adapt路径下。需要使用时,在sdk根目录下运行make menuconfig,选中Network下的auto_adapt,保存退出后再进行编译。files里为脚本和网页,src下为send发包工具代码。
3.3.1 send.c
send是配合auto_adapt.sh脚本使用的发包工具,源码路径为package/network/services/auto_adapt/src/send.c。此工具可以指定端口发送dhcp disvover数据包。根据是否能收到dhcp server回复的dhcp offer数据包判断该网口是否应该配置为wan口。可直接在串口键入指令”send + 端口名称”实现往该端口发包,例如send eth0.2。若收到回复,则会在串口返回log并将端口名称写入/tmp/dhcp_iface文件,供脚本读取并配置。
收到回复时会返回:
ifindex : 8 receive dhcp offer in ifindex 8, start setting
无法收到回复则会阻塞等待,直到auto_adapt.sh将其关闭。
利用以上指令,可以在auto_adapt.sh脚本里通过读取/tmp/dhcp_iface文件判断某个端口是否应该配置成wan口。
// send.c 代码
#include<stdio.h>
#include <unistd.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/socket.h>
#include <netinet/if_ether.h>
#include <netinet/ip.h>
#include <linux/udp.h>
#include <net/if.h>
#include <linux/sockios.h>
#include <linux/filter.h>
#include <linux/if_packet.h>
#include <sys/ioctl.h>
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS 80
#define ENABLE_UDHCPC_SLACK_FOR_BUGGY_SERVERS 1
#define MAC_BCAST_ADDR (uint8_t *) "\xff\xff\xff\xff\xff\xff"
#define BB_LITTLE_ENDIAN 0
#define DHCP_OPTIONS_BUFSIZE 308
#define BOOTREQUEST 1
#define DHCPOFFER 2
#define DHCP_MAGIC 0x63825363
#define DHCP_END 0xff
#define IF_FEATURE_UDHCP_PORT(...)
#define OPT_CODE 0
#define OPT_LEN 1
#define OPT_DATA 2
#define DHCP_PADDING 0x00
#define DHCP_CLIENT_ID 0x3d
#define DHCP_OPTION_OVERLOAD 0x34
#define DHCP_MESSAGE_TYPE 0x35
#define DHCP_END 0xff
#define LISTEN_RAW 2
#define PACKET_AUXDATA 8
#define FILE_FIELD 1
#define SNAME_FIELD 2
struct dhcp_packet {
uint8_t op; /* BOOTREQUEST or BOOTREPLY */
uint8_t htype; /* hardware address type. 1 = 10mb ethernet */
uint8_t hlen; /* hardware address length */
uint8_t hops; /* used by relay agents only */
uint32_t xid; /* unique id */
uint16_t secs; /* elapsed since client began acquisition/renewal */
uint16_t flags; /* only one flag so far: */
#define BROADCAST_FLAG 0x8000 /* "I need broadcast replies" */
uint32_t ciaddr; /* client IP (if client is in BOUND, RENEW or REBINDING state) */
uint32_t yiaddr; /* 'your' (client) IP address */
/* IP address of next server to use in bootstrap, returned in DHCPOFFER, DHCPACK by server */
uint32_t siaddr_nip;
uint32_t gateway_nip; /* relay agent IP address */
uint8_t chaddr[16]; /* link-layer client hardware address (MAC) */
uint8_t sname[64]; /* server host name (ASCIZ) */
uint8_t file[128]; /* boot file name (ASCIZ) */
uint32_t cookie; /* fixed first four option bytes (99,130,83,99 dec) */
uint8_t options[388];
} ;
struct ip_udp_dhcp_packet {
struct iphdr ip;
struct udphdr udp;
struct dhcp_packet data;
} ;
struct udp_dhcp_packet {
struct udphdr udp;
struct dhcp_packet data;
} ;
enum {
IP_UDP_DHCP_SIZE = sizeof(struct ip_udp_dhcp_packet) - CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS,
UDP_DHCP_SIZE = sizeof(struct udp_dhcp_packet) - CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS,
DHCP_SIZE = sizeof(struct dhcp_packet) - CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS,
};
uint16_t inet_cksum(uint16_t *addr, int nleft)
{
unsigned sum = 0;
while (nleft > 1) {
sum += *addr++;
nleft -= 2;
}
if (nleft == 1) {
if (BB_LITTLE_ENDIAN)
sum += *(uint8_t*)addr;
else
sum += *(uint8_t*)addr << 8;
}
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
sum += (sum >> 16); /* add carry */
return (uint16_t)~sum;
}
int udhcp_end_option(uint8_t *optionptr)
{
int i = 0;
while (optionptr[i] != DHCP_END) {
if (optionptr[i] != DHCP_PADDING)
i += optionptr[i + OPT_LEN] + OPT_DATA-1;
i++;
}
return i;
}
int udhcp_read_interface(const char *interface, int *ifindex, uint8_t *mac)
{
/* char buffer instead of bona-fide struct avoids aliasing warning */
char ifr_buf[sizeof(struct ifreq)];
struct ifreq *const ifr = (void *)ifr_buf;
int fd;
memset(ifr, 0, sizeof(*ifr));
fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
ifr->ifr_addr.sa_family = AF_INET;
strncpy(ifr->ifr_name, interface,16);
if (ifindex) {
if (ioctl(fd, SIOCGIFINDEX, ifr) != 0) {
close(fd);
return -1;
}
*ifindex = ifr->ifr_ifindex;
}
if (mac) {
if (ioctl(fd, SIOCGIFHWADDR, ifr) != 0) {
close(fd);
return -1;
}
memcpy(mac, ifr->ifr_hwaddr.sa_data, 6);
}
close(fd);
return 0;
}
int send_packet(struct dhcp_packet *dhcp_pkt,int ifindex)
{
int fd;
struct sockaddr_ll dest_sll;
struct ip_udp_dhcp_packet packet;
int result = -1;
unsigned padding;
fd=socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
if(fd==-1){
printf("socket failed\n");
}
memset(&dest_sll, 0, sizeof(dest_sll));
memset(&packet, 0, offsetof(struct ip_udp_dhcp_packet, data));
packet.data = *dhcp_pkt;
dest_sll.sll_family = AF_PACKET;
dest_sll.sll_protocol = htons(ETH_P_IP);
dest_sll.sll_ifindex = ifindex;
dest_sll.sll_halen = 6;
const uint8_t *dest_arp = MAC_BCAST_ADDR;
memcpy(dest_sll.sll_addr, dest_arp, 6);
bind(fd, (struct sockaddr *)&dest_sll, sizeof(dest_sll));
padding = DHCP_OPTIONS_BUFSIZE - 1 - udhcp_end_option(packet.data.options);
if (padding > DHCP_SIZE - 300)
padding = DHCP_SIZE - 300;
packet.ip.protocol = IPPROTO_UDP;
packet.ip.saddr = INADDR_ANY;
packet.ip.daddr = INADDR_BROADCAST;
packet.udp.source = htons(68);
packet.udp.dest = htons(67);
packet.udp.len = htons(UDP_DHCP_SIZE - padding);
packet.ip.tot_len = packet.udp.len;
packet.udp.check = inet_cksum((uint16_t *)&packet,IP_UDP_DHCP_SIZE - padding);
packet.ip.tot_len = htons(IP_UDP_DHCP_SIZE - padding);
packet.ip.ihl = sizeof(packet.ip) >> 2;
packet.ip.version = IPVERSION;
packet.ip.ttl = IPDEFTTL;
packet.ip.check = inet_cksum((uint16_t *)&packet.ip, sizeof(packet.ip));
result = sendto(fd, &packet, IP_UDP_DHCP_SIZE - padding, /*flags:*/ 0,(struct sockaddr *) &dest_sll, sizeof(dest_sll));
// close(fd);
return result;
}
void udhcp_init_header(struct dhcp_packet *packet)
{
memset(packet, 0, sizeof(*packet));
packet->op = BOOTREQUEST;
packet->htype = 1; /* ethernet */
packet->hlen = 6;
packet->cookie = htonl(DHCP_MAGIC);
uint8_t const_options[388] = {0x35,0x01,0x01,0x39,0x02,0x02,0x40,0x37,0x08,0x01,0x03,0x06,0x0c,0x0f,0x1c,0x2a,
0x79,0x3c,0x0c,0x75,0x64,0x68,0x63,0x70,0x20,0x31,0x2e,0x32,0x39,0x2e,0x33,0x0c,0x07,
0x4f,0x70,0x65,0x6e,0x57,0x72,0x74,0xff};
memcpy(packet->options, const_options, 41);
}
static void init_packet(struct dhcp_packet *packet, uint8_t *mac)
{
udhcp_init_header(packet);
packet->xid = rand();
packet->secs = htons(0);
memcpy(packet->chaddr, mac, 6);
}
static int sockfd = -1;
int setsockopt_int(int fd, int level, int optname, int optval)
{
return setsockopt(fd, level, optname, &optval, sizeof(int));
}
static int udhcp_raw_socket(int ifindex)
{
int fd;
struct sockaddr_ll sock;
fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
memset(&sock, 0, sizeof(sock)); /* let's be deterministic */
sock.sll_family = AF_PACKET;
sock.sll_protocol = htons(ETH_P_IP);
sock.sll_ifindex = ifindex;
bind(fd, (struct sockaddr *) &sock, sizeof(sock));
setsockopt_int(fd, SOL_PACKET, PACKET_AUXDATA,1);
return fd;
}
static int udhcp_recv_raw_packet(struct dhcp_packet *dhcp_pkt, int fd)
{
int bytes;
struct ip_udp_dhcp_packet packet;
uint16_t check;
unsigned char cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))];
struct iovec iov;
struct msghdr msg;
struct cmsghdr *cmsg;
iov.iov_base = &packet;
iov.iov_len = sizeof(packet);
memset(&msg, 0, sizeof(msg));
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = cmsgbuf;
msg.msg_controllen = sizeof(cmsgbuf);
for (;;) {
bytes = recvmsg(fd, &msg, 0);
if (bytes < 0) {
if (errno == EINTR)
continue;
printf("packet read error, ignoring\n");
return bytes; /* returns -1 */
}
break;
}
if (bytes < (int) (sizeof(packet.ip) + sizeof(packet.udp))) {
printf("packet is too short, ignoring\n");
return -2;
}
if (bytes < ntohs(packet.ip.tot_len)) {
printf("oversized packet, ignoring\n");
return -2;
}
bytes = ntohs(packet.ip.tot_len);
if (packet.ip.protocol != IPPROTO_UDP
|| packet.ip.version != IPVERSION
|| packet.ip.ihl != (sizeof(packet.ip) >> 2)
|| packet.udp.dest != htons(68)
|| ntohs(packet.udp.len) != (uint16_t)(bytes - sizeof(packet.ip))
) {
printf("unrelated/bogus packet, ignoring\n");
return -2;
}
/* verify IP checksum */
check = packet.ip.check;
packet.ip.check = 0;
if (check != inet_cksum((uint16_t *)&packet.ip, sizeof(packet.ip))) {
printf("bad IP header checksum, ignoring\n");
return -2;
}
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level == SOL_PACKET
&& cmsg->cmsg_type == PACKET_AUXDATA
) {
struct tpacket_auxdata *aux = (void *)CMSG_DATA(cmsg);
if (aux->tp_status & TP_STATUS_CSUMNOTREADY)
goto skip_udp_sum_check;
}
}
memset(&packet.ip, 0, offsetof(struct iphdr, protocol));
packet.ip.tot_len = packet.udp.len; /* yes, this is needed */
check = packet.udp.check;
packet.udp.check = 0;
if (check && check != inet_cksum((uint16_t *)&packet, bytes)) {
printf("packet with bad UDP checksum received, ignoring\n");
return -2;
}
skip_udp_sum_check:
if (packet.data.cookie != htonl(DHCP_MAGIC)) {
printf("packet with bad magic, ignoring\n");
return -2;
}
bytes -= sizeof(packet.ip) + sizeof(packet.udp);
memcpy(dhcp_pkt, &packet.data, bytes);
return bytes;
}
uint8_t* udhcp_get_option(struct dhcp_packet *packet, int code)
{
uint8_t *optionptr;
int len;
int rem;
int overload = 0;
enum {
FILE_FIELD101 = FILE_FIELD * 0x101,
SNAME_FIELD101 = SNAME_FIELD * 0x101,
};
optionptr = packet->options;
rem = sizeof(packet->options);
while (1) {
if (rem <= 0) {
complain:
printf("bad packet, malformed option field\n");
return NULL;
}
if (optionptr[OPT_CODE] == DHCP_PADDING) {
rem--;
optionptr++;
continue;
}
if (optionptr[OPT_CODE] == DHCP_END) {
if ((overload & FILE_FIELD101) == FILE_FIELD) {
overload |= FILE_FIELD101; /* "we looked at it" */
optionptr = packet->file;
rem = sizeof(packet->file);
continue;
}
if ((overload & SNAME_FIELD101) == SNAME_FIELD) {
overload |= SNAME_FIELD101; /* "we looked at it" */
optionptr = packet->sname;
rem = sizeof(packet->sname);
continue;
}
break;
}
if (rem <= OPT_LEN)
goto complain; /* complain and return NULL */
len = 2 + optionptr[OPT_LEN];
rem -= len;
if (rem < 0)
goto complain; /* complain and return NULL */
if (optionptr[OPT_CODE] == code) {
return optionptr + OPT_DATA;
}
if (optionptr[OPT_CODE] == DHCP_OPTION_OVERLOAD) {
if (len >= 3)
overload |= optionptr[OPT_DATA];
}
optionptr += len;
}
return NULL;
}
int main(int argc, char* argv[]){
int result;
char *iface;
uint8_t client_mac[6];
iface = (char *)malloc(sizeof(char));
iface = argv[1];
int ifindex;
udhcp_read_interface(iface,&ifindex,client_mac);
if(ifindex)
printf("ifindex : %d\n",ifindex);
else {
return 0;
}
sockfd = udhcp_raw_socket(ifindex);
struct dhcp_packet packet;
init_packet(&packet, client_mac);
result = send_packet(&packet,ifindex);
udhcp_recv_raw_packet(&packet, sockfd);
uint8_t *message;
message = udhcp_get_option(&packet, DHCP_MESSAGE_TYPE);
if (*message == DHCPOFFER) {
char cmd[128];
sprintf(cmd,"echo '%s' > /tmp/dhcp_iface",iface);
system(cmd);
printf("receive dhcp offer in ifindex %d, start setting\n",ifindex);
} else
printf("no dhcp offer received\n");
return result;
}3.3.2 auto_adapt.sh
实现wan/lan自适应的核心脚本,被编译到/bin/auto_adapt.sh路径下,用法为:
auto_adapt.sh start 立即进行自动配置
auto_adapt.sh set 2 指定将2号端口配置号为wan口
主要函数解析:
start() {
init() --初始化,备份network配置并划分vlan
checkport() --调用send,发送数据包寻找wan口
kill_all() --1s后停止send,kill所有send进程。
dinit() --寻找到wan口后恢复network配置
set_wan() --根据结果配置wan口。直接调用该函数可完成手动配置。
}#!/bin/sh
default_ports_lan=`uci get network.@switch_vlan[0].ports`
default_ports_wan=`uci get network.@switch_vlan[1].ports`
default_ports=`uci get network.@switch_vlan[1].ports | awk -F ' ' '{print $2}'`
port_str=${default_ports_lan/$default_ports/$default_ports_wan}
set_wan() {
if [ $1 -ne 0 ]; then
port_n=`expr $1 - 1`' '
port_lan=${port_str/$port_n/''}
port_wan=${port_n}${default_ports}
fi
uci set network.@switch_vlan[0].ports="$port_lan"
uci set network.@switch_vlan[1].ports="$port_wan"
uci commit
}
dinit() {
cp /tmp/adapt_network /etc/config/network
rm /tmp/adapt_network
}
kill_all() {
pid_fd=`ps | grep "send" |grep "eth0." | grep -v "grep" | awk -F ' ' '{print $1}'`
kill $pid_fd
}
checkport() {
a=0
rv=0
for i in `seq 1 5`
do
{
send eth0."$i" &
}&
done
wait
sleep 1
kill_all
}
init() {
cp /etc/config/network /tmp/adapt_network
echo " " > /etc/config/network
for i in `seq 1 5`
do
uci set network.wan"$i"='interface'
uci set network.wan"$i".ifname=eth0."$i"
uci set network.wan"$i".proto=dhcp
done
a=`uci add network switch`
uci set network.$a.name="switch0"
uci set network.$a.reset="1"
uci set network.$a.enable_vlan="1"
for i in `seq 1 5`
do
a=`uci add network switch_vlan`
uci set network.$a.device="switch0"
uci set network.$a.vlan="$i"
uci set network.$a.ports="`expr $i - 1` $default_ports"
done
uci commit
/etc/init.d/network reload
}
start() {
a=0
init
rm /tmp/dhcp_iface
for i in `seq 1 3`
do
checkport
if [ -f "/tmp/dhcp_iface" ]; then
a=`cat /tmp/dhcp_iface | awk -F '.' '{print $2}'`
rm /tmp/dhcp_iface
break
fi
sleep 1
done
dinit
if [ "$a" -ne 0 ];then
set_wan $a
fi
/etc/init.d/network reload &
}
case $1 in
start)
start
;;
set)
set_wan $2
/etc/init.d/network reload
;;
*)
echo "commond error"
;;
esac3.3.3 auto_adapt.htm
用于显示其功能的htm文件,配合feeds/luci/modules/luci-mod-network/luasrc/controller/admin/network文件,将自适应功能以网页+接口调用的形式可视化。开发流程可参考SiWiFi接口开发手册。最终界面如下所示:

手动配置一栏显示当前端口的连接状态,点击”lan”按钮即可将对应端口配置为wan口。
自动配置一栏点击立即配置后则立即进行一次wan口自动配置。若未找到wan口则配置不变。
开机自启动一栏可选择是否启用。启用后,开机时会进行一次wan口自动配置。
3.3.4 rc.local
开机自动运行的脚本,读取/etc/config/network文件lan下的auto_adapt选项。若该选项被配置为1,则立即进行一次自动配置。
4 测试用例
4.1 测试环境配置
一台待已编译自启动脚本的待测路由、串口、能获取到ip并上网的网线。
4.2 测试流程
4.2.1 手动配置测试
1、将网线插入第1号口。
2、串口执行auto_adapt.sh set 1,或者浏览器访问路由器配置界面,打开“网络”选项下的“wan/lan自适应”,点击手动配置下的第一个按钮。
3、等待约10s,同时在串口观察是否打印log。
4、在串口输入ping www.baidu.com检测是否能上网。更换端口,重复测试。
4.2.2 自动配置测试
1、将网线插入路由器任意端口。
2、串口执行auto_adapt.sh start,或者浏览器访问路由器配置界面,打开“网络”选项下的“wan/lan自适应”,点击自动配置下的“立即配置”按钮。
3、等待约10s,同时在串口观察是否打印log。
4、在串口输入ping www.baidu.com检测是否能上网。更换端口,重复测试。
4.2.3 开机自启动测试
1、将外部网线插入路由器任意端口。
2、在串口输入指令:uci set network.lan.auto_adapt=1;uci commit network。或者浏览器访问路由器配置界面,打开“网络”选项下的“wan/lan自适应”,启用“开机自启动”选项下的按钮。
3、重启路由器。待路由器重启完成。
4、在串口输入ping www.baidu.com检测是否能上网。更换端口,重复测试。
4.3 测试结果
以上所有操作执行后,均会有自适应脚本的相应log打印;等待一段时间后,路由器能正常上网,说明功能正常。
5 FAQ
Q: 脚本在开机后没有自启动,如何检查?
A:在/etc/rc.local中,运行脚本指令之前添加log,确认自启动脚本是否被调用。若此log未出现,说明rc.local有问题而非自启动脚本本身的问题。之后在auto_adapt.sh脚本中添加log,并用ps指令查看脚本是否已在后台运行。若log已打但脚本未运行,则可判断是脚本逻辑问题导致运行结束。
Q:配置后路由器无法上网,如何检查?
A:首先查看/etc/config/network文件是否按照规范配置,wan口有没有划分正确;串口输入/etc/init.d/network restart重启网络,差看是否是因为配置未生效;查看是否是外部网络不通畅的问题。
Q:配置之后网页无法点击?
A:这是因为更换了wan/lan配置并重启了网络,刷新网页或重新登录即可。
项目的代码地址:https://github.com/Siflower/1806_SDK/tree/release/openwrt-18.06/package/network/services/auto_adapt








