OpenWrt 如何实现WAN、LAN自适应

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=interfacenetwork里加一个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"
		;;
esac

3.3.3 auto_adapt.htm

用于显示其功能的htm文件,配合feeds/luci/modules/luci-mod-network/luasrc/controller/admin/network文件,将自适应功能以网页+接口调用的形式可视化。开发流程可参考SiWiFi接口开发手册。最终界面如下所示:

auto_adapt_htm

  • 手动配置一栏显示当前端口的连接状态,点击”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


本文章由作者:佐须之男 整理编辑,原文地址: OpenWrt 如何实现WAN、LAN自适应
本站的文章和资源来自互联网或者站长的原创,按照 CC BY -NC -SA 3.0 CN协议发布和共享,转载或引用本站文章应遵循相同协议。如果有侵犯版权的资 源请尽快联系站长,我们会在24h内删除有争议的资源。欢迎大家多多交流,期待共同学习进步。

相关推荐