注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

心情挺好的博客

正在等你光临呢 呵呵

 
 
 

日志

 
 
关于我

喜欢摄影的朋友看过来:) 有时间就跟我一起去拍照去吧. QQ272751 上海圣玛丽摄影化妆培训学校 16年专业摄影培训化妆培训学校 电话:15900513500。 http://www.smlsh.com

网易考拉推荐

openwrt flash layout  

2009-01-14 23:22:15|  分类: 技术 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

Introduction

With the release of the Linux sources for the Linksys WRT54G/GS series of routers came a number of modified firmwares to extend functionality in various ways. Each firmware was 99% stock sources and 1% added functionality, and each firmware attempted to cater to a certain market segment with what functionality they provided. The downsides were two fold, one - it was often difficult to find a firmware with the combination of functionality desired (leading to forks and yet more custom firmwares) and two - all the firmwares were based on the original Linksys sources which were far behind mainstream linux development.

OpenWrt takes a different route, instead of starting out with the Linksys sources, the development started with a clean slate. Piece by piece software was added to bring the functionality back to that of the stock firmware, using the most recent versions available. What makes OpenWrt really unique though is the fact it employs a writable filesystem so the firmware is nolonger a static compilation of software but can instead be dynamically adjusted to fit the particular needs of the situation. In short, the device is turned into a mini linux PC with OpenWrt acting as the distribution, complete with almost all traditional linux commands and a package management system for easily loading on extra software and features.

The flash layout

The basics

Everything is stored on a single flash chip. The basic flash layout is as follows -

[bootloader][firmware][nvram]
There's no actual partition table as such, the names and locations are simply hardcoded.

When the device boots, it runs a bootloader, either PMON or CFE; both are identical as far as the user is concerned. It's the responsibility of this bootloader to perform basic system initialization along with validating and loading the actual firmware; think of it as the BIOS of the device before control is passed over to the opperating system. If the firmware fails to pass the CRC check, then the bootloader will presume that the firmware is corrupted and will wait for a new firmware to be uploaded via the network.

The nvram partition is used as a storage mechanism, in here you'll find a number of configuration values stored as 'name=value' pairs. There are acutally two nvram locations, the partition identified above, and a smaller copy embeded into the bootloader. The nvram embeded into the bootloader is used as a set of default values if the actual nvram partition can't be found.

The firmware itself starts immediately after the bootloader and is allowed to take all the space up until the start of nvram, although in practice it rarely does. As a result, there's often a few megabytes of unused space:

[bootloader][firmware][unused/jffs2][nvram]
OpenWrt makes use of this otherwise unused space by converting it into a writable jffs2 formatted filesystem. The reason for not simply extending the filesystem within the firmware is an artifact of the filesystem being writable; if the filesystem were contained within the firmware then any changes to the filesystem would require the the firmware CRC to be updated or else the firmware would fail to pass the booloader's CRC check.

Definition of a firmware

The firmware itself follows the pattern:
[trx header][kernel][filesystem]
The trx header provides information on the basic structure of the firmware:
struct trx_header {
    unsigned magic;             /* "HDR0" */
    unsigned len;               /* Length of file including header */
    unsigned crc32;             /* 32-bit CRC from flag_version to end of file */
    unsigned flag_version;      /* 0:15 flags, 16:31 version */
    unsigned offsets[3];        /* Offsets of partitions from start of header */
};
At the start of the trx is the signature "HDR0" to identify it as a trx, this is followed by information such as the length of the trx file itself, the CRC, flags for what features are supported and offsets for up to 3 partitions contained within the trx. (It's this CRC which is used to verify the firmware before reflashing and again by the bootloader to check for corruption.)

Although the firmware is stored in flash as a trx file, the firmware itself is generally downloaded/distributed as a bin file, which adds the following header before the trx data:

struct code_header {
        char magic[4];          /* wrt54g: "W54G", wrt54gs: "W54S" */
        char res1[4];
        char fwdate[3];
        char fwvern[3];
        char id[4];             /* "UND0" */
        char hw_ver;            /* 4702 chips: 0, 4712 chips: 1 */
        unsigned short flags;
        unsigned char res2[10];
}; /* 32 bytes */
The purpose of the bin header is to provide the firmware version and identify the model the firmware was intended for. If the file begins with the string "W54G" then the firmware is intended for a on a wrt54g, if it starts with "W54S" then the firmware is intented for a wrt54gs. The bin header is automatically stripped off by the bootloader and the upgrade webpage in the linksys firmware; it is not stripped off automatically by OpenWrt's mtd commands.

Hardware revisions

There are two series, the WRT54G and the WRT54GS. The WRT54GS is sold as a "Speedbooster" model, capable of speeds beyond 54Mbps when used with compatible hardware, this is mostly just a marketing ploy since the WRT54G models released at the same time also support speedbooster with a few minor software modifications. The real reason the WRT54GS models are interesting is because of the extra flash storage and RAM they provide -- hardly anyone cares about speedbooster.

4M FLASH/16M RAM 8M FLASH/32M RAM Revision notes
WRT54G v1.0 - Initial release, 125Mhz, AMD flash, removable minipci wireless card, PMON bootloader
WRT54G v1.1 - The flash chips where switched to intel, integrated wireless
WRT54G v2.0 WRT54GS v1.0 200Mhz CPU, CFE bootloader, introduction of GS models
WRT54G v2.2 WRT54GS v1.1 Ethernet switch changed from ADM6996 to Broadcom Roboswitch
WRT54G v3.0 WRT54GS v2.0 Two additional leds and a new button

The packaging doesn't always indicate the revision number; the best way to determine the revision is by looking at the bottom of the router. All linksys routers have a silver and black sticker with the linksys logo and the words "Model no." followed by the model name and optionally a version number. Models that don't have a version number are v1.0; the version number was ommited because at that time there was only one version.

Loading OpenWrt

The filesystem layout: squashfs

OpenWrt comes in two variations, a squashfs version and more recently a jffs2 version. The difference between the two variations is how the filesystem is structured. Initially, the OpenWrt firmware only included a small readonly squashfs filesystem and when booted, the system would create a jffs2 partition after the firmware, populate the filesystem with symlinks and use a pivot_root() call to swap the root filesystems. This is refered to as the squashfs version, since it depends on a squashfs filesystem embeded into the firmware.

Specifically, on bootup, the kernel boots using the squashfs filesystem, instead of following typical linux rules, the "init" application is not the first program loaded but rather the second; the first thing the system does is executes /etc/preinit from the squashfs partition. Preinit gets executed via the kernel's "init=/etc/preinit" commandline option, it's job is to switch the root from squashfs to jffs2, using pivot_root before finally passing control to init. The squashfs partition is moved from root to "/rom" and jffs2 takes over as the root filesystem in what can best be described as a "poor man's overlay filesystem"; each file on the jffs2 partition starts out as a symlink to the squashfs filesystem on /rom.

The reason for this preinit setup is that the preinit script becomes one of the only things that's executed directly from the squashfs filesystem; everything else, including init is executed from the jffs2 partition and can easily be modified to suit the needs of the system, providing the illusion of a fully writable system. The problem and at the same time one of the major advantages to the squashfs version is that the squahsfs filesystem itself isn't writable. To make a change to the filesystem you first need to delete the symlink to the /rom (squashfs) file from the now root jffs2 partition, and recreate the file. Nothing short of a reflash will change the file on the squashfs partition, so if you replace a 13k file, the squashfs copy of the file is still using up 13k of space and another 13k is used by the newly created file on the jffs2 root partition.

The advantage comes from the fact that because the squashfs partition can't be modified, there's always a pristine copy of the base filesystem. To explain this concept better, what would happen if the preinit script didn't perform the call to pivot_root and instead left the squashfs filesystem as the root? This is a condition we refer to as "failsafe mode", because it allows you to boot into the system and recover a corrupt jffs2 partition. Failsafe mode can be triggered by rebooting, waiting for the DMZ led on the front of the router to light and then pressing and holding the reset button for just over a second.

Filesystem layouts continued: the jffs2 layout

Recent versions of OpenWrt have offered the ability to have an entirely jffs2 root partition and bypass some of the side effects of the squashfs+jffs2 layout. The idea being that with a completely writable filesystem that the base set of files can then become a software package that's updated, saving the user from having to reflash to update to the next version of OpenWrt.

As mentioned earlier in the flash section, the jffs2 partition can't be stored as part of the firmware or any changes to the filesystem would alter the firmware's CRC and it would be seen as a corrupt firmware. So how do we create and populate the jffs2 filesystem in the first place?

The answer to how the jffs2 layout works can only be described as the very definition of a hack -- the jffs2 filesystem is part of the firmware, but only up util the first bootup. Once booted, the command "jffs2root --move" is executed, causing the program to alter the flash, modifying the firmware's trx headers so the firmware is now only the kernel, making the jffs2 partition external. Since this trick depends on modifying the firmware, an additional reboot is required before the system is in a usable state; the first time it boots the firmware is modified, a reboot is then required and the system is usable from the second boot.

The more recent revelation was that it didn't matter what type of filesystem was embeded into the original firmware, that the same trick could be used to convert a squashfs install into a jffs2 only system. Once the jffs2 partition nolonger Had any more remaining symlinks to the squashfs filesystem on /rom, you could run the jffs2root utility again and it would repartition, allowing the jffs2 partition to take over the space previously held by the squashfs partition -- as long as the new partition contained some vaild jffs2 data, the invalid squashfs data was ignored.

Deciding on a filesystem layout

Both the squahsfs and jffs2 filesystems use heavy compression to save disk (flash) space. The choice of layouts really depends on usage patterns. At this point, the recommended install is the squashfs version wich will provide you with failsafe mode and a slightly better compression ratio than the jffs2 partitions. Should you feel the need to switch over to jffs2 later, the jffs2root script can be run -- the reverse can't be said.

Enabling boot_wait

Although it's not a strict requirement, the first step in loading OpenWrt is to enable an nvram setting known as boot_wait. Once enabled this setting will cause the bootloader to always pause for roughly 3 seconds each time the device is powered on to allow the uploading of a new firmware. This is a safety mechanism that will allow you to recover if the firmware should fail to boot.

The trick to enabling the boot_wait setting is the Ping.asp page found under the administration/diagnostics page of the web administration. Early versions of the firmware would simply plug the value entered on the webpage into a "ping %s" command and execute it. As the firmwares progressed they attempted to fix the bug by slowly adding more and more restrictions to the characters which could be entered into the fields; eventually the bug was fixed by replacing the system() calls with an execve() call which prevented the arguments from being evaluated by the shell. The last firmware revisions which featured a usable Ping.asp bug were 3.01.3 for the WRT54G models (all revisions) and 3.37.2 for the WRT54GS models (all revisions). If you call this a security hole or not depends on your point of view; it's certainly a flaw in their code, but it requires the administrator password to be able to access the page.

Here's the code behind the Ping.asp page on the 3.37.2 firmware:

char *ip = nvram_safe_get("ping_ip");
if(!check_wan_link(0))
        buf_to_file(PING_TMP, "Network is unreachable\n");
else if(strchr(ip, ' ') || strchr(ip, '`') || strstr(ip, PING_TMP))
   // Fix Ping.asp bug, user can execute command ping in Ping.asp
        buf_to_file(PING_TMP, "Invalid IP Address or Domain Name\n");

else if(nvram_invmatch("ping_times","") && nvram_invmatch("ping_ip","")){
        char cmd[80];
        snprintf(cmd, sizeof(cmd), "ping -c %s -f %s %s &", nvram_safe_get("ping_times"), PING_TMP, ip);
        printf("cmd=[%s]\n",cmd);
        eval("killall", "ping");
        unlink(PING_TMP);
        system(cmd);
}

The ping command is only executed if the WAN interface has an ip address and the ip address to ping doesn't contain any of the following: (space) (tilde) "/tmp/ping.log". There's also a length restriction on the HTML that only allows 31 characters to be entered into the IP field.

With that in mind, make sure the WAN interface has an ip address and ping the following "ip addresses" in sequential order to enable the boot_wait variable:

;cp${IFS}*/*/nvram${IFS}/tmp/n
;*/n${IFS}set${IFS}boot_wait=on
;*/n${IFS}commit
;*/n${IFS}show>tmp/ping.log
Note that these commands make use of a number of tricks to keep the total length under 31 characters and avoid any of the above pitfalls. The actual commands executed are:
cp /usr/sbin/nvram /tmp/n
/tmp/n set boot_wait=on
/tmp/n commit
/tmp/n show > tmp/ping.log
The Ping.asp page is coded to display "/tmp/ping.log" as the ping results so only the last command will actually produce any output. The output from the "nvram show" command will include all variables set in nvram, the only one of particular interest here is "boot_wait=on"; if you don't see that try the commands again.

Uploading the firmware

(If you're already running OpenWrt, skip to the Upgrading section below)

Uploading the OpenWrt firmware can be done in a number of ways, either via the webpage or the previously enabled boot_wait. It's recommended that you use boot_wait and upload the firmware via tftp so you aren't at a loss of what to do should the firmware fail to boot.

Using boot_wait

As previously mentioned, when boot_wait is set to on, the bootloader will allow a 3 second window for a new firmware. Uploads are done using a tftp client in binary mode, the router is always at the default ip address of 192.168.1.1.

A sample session might look like this:

tftp 192.168.1.1
tftp> binary
tftp> rexmt 1
tftp> timeout 60
tftp> trace
Packet tracing on.
tftp> put openwrt-wrt54g-squashfs.bin

Upgrading a previous OpenWrt install

The ipkg system can be used to upgrade the various applications included with the firmware, however upgrading the kernel itself requires a reflashing to a newer firmware. If you're already running the OpenWrt firmware, then you can reflash the firmware by writing it directly to the flash:
mtd -r write firmware.trx linux
This will write the file "firmware.trx" directly to the "linux" partition, and if successful it will automatically reboot. As for getting the firmware.trx onto the filesystem, popular choices include wget, scp and network mounted drives.

The linux partition is the entire area from the end of the bootloader to the start of nvram, the write command will take the file specified and write it to the start of the linux partition; it will not erase the partition and it won't write any more data than that contained in firmware.trx; no other partitions (including nvram) are touched. This means, that if you're using the squashfs version, that the jffs2 data generally remains intact (provided the new firmware isn't large enough to overwrite the jffs2 data). If you're using the jffs2 version of the firmware, the old jffs2 data will be lost, requiring packages to be reinstalled.

Backing up a previous OpenWrt install

As mentioned in the previous section, the linux partition is the entire space between the bootloader and nvram, and can be written with the mtd command. Backing up or duplicating an OpenWrt install is simply a matter of getting a copy of that linux partition:
dd if=/dev/mtd/1 of=/tmp/firmware.trx
This will create a firmware.trx containing the firmware and the jffs2 filesystem located after the firmware. This can then be written back to the router (or another router of a similar flash size) using the mtd write command above.

Using OpenWrt

Once booted, the OpenWrt console is available via telnet, using whatever ip address the router was previously configured for.
Trying 192.168.1.41...
Connected to 192.168.1.41.
Escape character is '^]'.
 === IMPORTANT ============================
  Use 'passwd' to set your login password
  this will disable telnet and enable SSH
 ------------------------------------------


BusyBox v1.00 (2005.05.30-01:57+0000) Built-in shell (ash)
Enter 'help' for a list of built-in commands.

  _______                     ________        __
 |       |.-----.-----.-----.|  |  |  |.----.|  |_
 |   -   ||  _  |  -__|     ||  |  |  ||   _||   _|
 |_______||   __|_____|__|__||________||__|  |____|
          |__| W I R E L E S S   F R E E D O M
          
root@OpenWrt:/# 
By default there's no password. It's strongly suggested that you set a root password, which as noted in the login banner will disable telnet and enable SSH.

Almost all standard linux commands will work, however most of the commands are actually compact versions provided by busybox and may not support all commandline switches.

NVRAM

Previously (with the stock firmware), all configuration was done by setting values in nvram; a small partition at the end of the flash used to hold config values. Unlike the stock firmware, OpenWrt features a fully writable filesystem capable of storing application configurations, thus OpenWrt only uses nvram for a handful of values from nvram. OpenWrt's policy is to never require new nvram variables and to never alter them automatically.

Access to the nvram partition is done via the nvram utility:

Command Description
nvram show Shows all variables stored in nvram
nvram get [variable] Returns the contents of [variable]
nvram set [variable]=[value] Assigns [value] to [variable]
nvram unset [variable] Removes [variable] from nvram
nvram commit Saves the nvram cache back to the flash

It's important to note, that with the exception of the commit command, all nvram commands work with a memory cached copy of the nvram data and do not alter the data stored on the flash chip. Any changes not commited to the flash will be lost on a reboot as the cache is repopulated with the values stored on flash.

Networking: IEEE 802.3 (ethernet)

The primary use of nvram in OpenWrt is to store the network configuration.
Variable Description
[type]_ifname The name of the interface used for [type]
[type]_ifnames If [type]_ifname is a bridge (br0-br99) then this variable contains the interfaces to be added to the bridge
[type]_proto This variable determines how the interface is to be configured:
dhcp - Attempt a dhcp lease to determine ip, netmask, dns, gateway
static - manual configuration
[type]_ipaddr The ip address to assign to the interface or to request via dhcp
[type]_netmask The netmask associated with the interface
[type]_dns The dns server to be used
[type]_gateway The default gateway
[type]_stp Enable spanning tree on bridges
[type]_hwaddr MAC address to be used for the interface

Here [type] refers to "lan" for the lan/wifi bridge, and "wan" for the connection to the internet. Configuration using the above values is done by the /etc/init.d/S40network script, using the "ifup [type]" command; the lan interface is handled by "ifup lan" and the wan interface is handled by "ifup wan". Note that [type] itself has no value; the networking script defaults to configuring the lan and wan, but there's no reason why you couldn't follow the above pattern with [type] as "foo" and perform "ifup foo" to configure foo_ifname.

The default /etc/init.d/S40network script:

#!/bin/sh
. /etc/functions.sh
case "$1" in
  start|restart)
    ifup lan
    ifup wan
    ifup wifi
    wifi up

    for route in $(nvram get static_route); do {
      eval "set $(echo $route | sed 's/:/ /g')"
      $DEBUG route add -net $1 netmask $2 gw $3 metric $4 dev $5
    } done
    ;;
esac

To see what the ifup script actually does, there's a DEBUG mode:

DEBUG=echo ifup lan
This will cause ifup to only print the commands that would normally be executed to configure the interface.

Networking continued: IEEE 802.11 (wireless)

The wireless layer has it's own set of variables prefixed by wl0_
Variable Value
wl0_mode One of the following:
ap - wireless access point
sta - wireless client
wet - wireless bridge (note: buggy)
wl0_infra Mode to be used for sta/wet
0 - adhoc (point to point)
1 - infrastructure (connect to an ap)
wl0_ssid Network name (default "linksys")
wl0_closed Broadcast SSID in beacons
0 - broadcast ssid
1 - hide SSID (broadcast a beacon with null SSID field)
wl0_radio Enable/Disable radio
0 - wireless is disabled
1 - wireless is enabled
wl0_bcn Beacon period (transmission interval, default 100)
wl0_frameburst Allow framebursts
on - allow frameburst
off - don't allow frameburst
wl0_afterburner Enable afterburner/speedbooster extensions (note: buggy)
on - afterburner is enabled
off - afterburner is disabled

Configuration is done by the /sbin/wifi wrapper. The command "wifi up" is used in /etc/init.d/S40network to enable the wireless layer.

Failsafe

If you can't get OpeWrt to boot, modified and broken the bootup scripts or firewalled yourself out, you can use OpenWrt's failsafe mode to get back in and fix the settings. While in failsafe mode, the ip address is forced to 192.168.1.1, and if possible the squashfs filesystem is used instead of the jffs2 filesystem.

To get into failsafe mode on a WRT54G or WRT54GS, you need to press and hold the reset button when the DMZ led at the front of the router lights at bootup.

The actual code responsible for the failsafe check looks like this: (taken from preinit)

echo 0x01 > /proc/sys/diag
sleep 1
if [ $(cat /proc/sys/reset) = 1 ] || [ "$(/usr/sbin/nvram get failsafe)" = 1 ]; then
  export FAILSAFE=true
  while :; do { echo $(((X=(X+1)%8)%2)) > /proc/sys/diag; sleep $((X==0)); } done & 
fi

The first thing preinit does is turn on the DMZ led through /proc/sys/diag, the system then pauses for one second to allow the user to notice the led and press the button, at the end of the one second delay the system checks if the reset button is currently held, which triggers failsafe. Additionally, failsafe can also be triggered by setting the nvram variable "failsafe" to "1", allowing failsafe to be used without physical access to the device.

Once in failsafe mode, the DMZ led will flash continuously and the FAILSAFE variable is set, triggering failsafe mode in various scripts.

Failsafe in detail: nvram overrides

In /etc/functions.sh, you'll find the following lines:
nvram () {
  case $1 in
    get) eval "echo \${NVRAM_$2:-\$(command nvram get $2)}";;
    *) command nvram $*;;
  esac
}
The purpose of these lines is to allow /etc/nvram.overrides to set variables which take priority over the actual nvram variables. The above code creates a shell wrapper around the nvram application, allowing it to intercept get commands; if a shell environment variable exists it will be returned, otherwise the actual nvram command will be run.

This allows the /etc/nvram.overrides script to set NVRAM_lan_ipaddr=192.168.1.1, providing a known ip address for failsafe mode without erasing the user's existing configuration. The user is then able to fix or experiment with the configuration without having to redo the configuration each time a mistake is made. The user is then able to fix or experiment with the configuration without having to redo the configuration each time a mistake is made.

Package management: ipkg

The ipkg utility is a lightweight package manager used to download and install OpenWrt packages from the internet. (Linux users familiar with apt-get will recognise the similarities)
Command Description
ipkg update Download a list of packages available
ipkg list View the list of packages
ipkg install dropbear Install the dropbear package
ipkg remove dropbear Remove the dropbear package
More options can be found via ipkg --help.

The list of repositories that ipkg searches for packages is in /etc/ipkg.conf. Each repository is entered in the following format:

src [name] [url]
Links to various repositories and the packages they contain can be found at http://tracker.openwrt.org/

More information

Links to more information and support can be found on the project website at http://openwrt.org/
  评论这张
 
阅读(3115)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017